Esempio n. 1
0
    public PatchProvider(MonoBehaviour behaviour, PatchCache cache)
    {
        this.behaviour = behaviour;
#if SAFETY_CHECK
        this.cache = cache;
#endif
    }
Esempio n. 2
0
        public MenuRenderer(Wad wad, DrawScreen screen)
        {
            this.wad    = wad;
            this.screen = screen;

            cache = new PatchCache(wad);
        }
        public OpeningSequenceRenderer(Wad wad, DrawScreen screen, SfmlRenderer parent)
        {
            this.screen = screen;
            this.parent = parent;

            cache = new PatchCache(wad);
        }
Esempio n. 4
0
        public OpeningSequenceRenderer(DrawScreen screen, IRenderer parent)
        {
            this.screen = screen;
            this.parent = parent;

            this.cache = new PatchCache();
        }
Esempio n. 5
0
        // Static Query Methods
        public static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir)
        {
            if (cache.Initialized || !patchDir.Exists)
            {
                return;
            }

            var    patches = cache.KipPatches;
            string type    = null;

            if (StrEquals(AmsNsoPatchDir, patchDir.Name))
            {
                patches = cache.NsoPatches; type = "NSO";
            }
            else if (StrEquals(AmsNroPatchDir, patchDir.Name))
            {
                patches = cache.NroPatches; type = "NRO";
            }
            else if (StrEquals(AmsKipPatchDir, patchDir.Name))
            {
                patches = cache.KipPatches; type = "KIP";
            }
            else
            {
                return;
            }

            foreach (var modDir in patchDir.EnumerateDirectories())
            {
                patches.Add(new Mod <DirectoryInfo>(modDir.Name, modDir));
                Logger.Info?.Print(LogClass.ModLoader, $"Found {type} patch '{modDir.Name}'");
            }
        }
Esempio n. 6
0
        public FinaleRenderer(CommonResource resource, DrawScreen screen)
        {
            this.flats   = resource.Flats;
            this.sprites = resource.Sprites;

            this.screen = screen;
            this.scale  = screen.Width / 320;

            this.cache = new PatchCache();
        }
Esempio n. 7
0
 public async Task RunAsync(PatchCache patchCache, PluginInfo pluginInfo, CancellationToken ct = default)
 {
     if (_Enabled)
     {
         await _InstallAsync(patchCache, pluginInfo, ct);
     }
     else
     {
         await _RemoveAsync(ct);
     }
 }
Esempio n. 8
0
        public FinaleRenderer(CommonResource resource, DrawScreen screen)
        {
            wad     = resource.Wad;
            flats   = resource.Flats;
            sprites = resource.Sprites;

            this.screen = screen;
            scale       = screen.Width / 320;

            cache = new PatchCache(wad);
        }
Esempio n. 9
0
        public FinaleRenderer(GameContent content, DrawScreen screen)
        {
            wad     = content.Wad;
            flats   = content.Flats;
            sprites = content.Sprites;

            this.screen = screen;
            scale       = screen.Width / 320;

            cache = new PatchCache(wad);
        }
Esempio n. 10
0
        private async Task _InstallAsync(PatchCache patchCache, PluginInfo pluginInfo, CancellationToken ct = default)
        {
            App.Logger.Info(nameof(EnglishPatchPhase), "Verifying translation plugins and data");

            await pluginInfo.BlockTranslation.ValidateAsync(_InstallConfiguration, ct);

            await pluginInfo.ItemTranslation.ValidateAsync(_InstallConfiguration, ct);

            await pluginInfo.TitleTranslation.ValidateAsync(_InstallConfiguration, ct);

            await pluginInfo.TextTranslation.ValidateAsync(_InstallConfiguration, ct);
        }
Esempio n. 11
0
        public IntermissionRenderer(Wad wad, DrawScreen screen)
        {
            this.wad    = wad;
            this.screen = screen;

            cache = new PatchCache(wad);

            minus   = Patch.FromWad(wad, "WIMINUS");
            numbers = new Patch[10];
            for (var i = 0; i < 10; i++)
            {
                numbers[i] = Patch.FromWad(wad, "WINUM" + i);
            }
            percent = Patch.FromWad(wad, "WIPCNT");
            colon   = Patch.FromWad(wad, "WICOLON");

            scale = screen.Width / 320;
        }
Esempio n. 12
0
        public IntermissionRenderer(DrawScreen screen)
        {
            this.screen = screen;

            this.cache = new PatchCache();

            this.minus   = Patch.FromWad("WIMINUS");
            this.numbers = new Patch[10];

            for (var i = 0; i < 10; i++)
            {
                this.numbers[i] = Patch.FromWad("WINUM" + i);
            }

            this.percent = Patch.FromWad("WIPCNT");
            this.colon   = Patch.FromWad("WICOLON");

            this.scale = screen.Width / 320;
        }
Esempio n. 13
0
            static bool TryQuery(ModCache mods, PatchCache patches, ulong?titleId, DirectoryInfo dir, DirectoryInfo searchDir)
            {
                if (StrEquals(AmsContentsDir, dir.Name))
                {
                    if (titleId.HasValue)
                    {
                        QueryContentsDir(mods, dir, (ulong)titleId);

                        return(true);
                    }
                }
                else if (IsPatchesDir(dir.Name))
                {
                    QueryPatchDirs(patches, dir, searchDir);

                    return(true);
                }

                return(false);
            }
Esempio n. 14
0
            static bool TryQuery(DirectoryInfo searchDir, PatchCache patches, Dictionary <ulong, ModCache> modCaches)
            {
                if (IsContentsDir(searchDir.Name))
                {
                    foreach (var(titleId, cache) in modCaches)
                    {
                        QueryContentsDir(cache, searchDir, titleId);
                    }

                    return(true);
                }
                else if (IsPatchesDir(searchDir.Name))
                {
                    QueryPatchDirs(patches, searchDir);

                    return(true);
                }

                return(false);
            }
Esempio n. 15
0
        public MenuRenderer(DrawScreen screen)
        {
            this.screen = screen;

            this.cache = new PatchCache();
        }
Esempio n. 16
0
        public async Task RunAsync(PatchInfo[] toUpdate, PatchCache patchCache, CancellationToken ct = default)
        {
            Progress.Progress        = 0;
            Progress.IsIndeterminate = true;
            Progress.CompletedCount  = 0;
            Progress.TotalCount      = 0;

            if (toUpdate.Length == 0)
            {
                return;
            }

            var state = new ProcessState
            {
                AtomicIndex          = 0,
                AtomicCompletedCount = 0,
                AtomicProcessCount   = 0,
                UpdateBuckets        = new ConcurrentQueue <List <PatchCacheEntry> >()
            };

            var processCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ct);

            Progress.TotalCount = toUpdate.Length;

            App.Logger.Info(nameof(VerifyFilesPhase), "Starting processing threads");
            Progress.IsIndeterminate = false;
            // TODO: processor affinity?
            var threadTasks = Enumerable.Range(0, Environment.ProcessorCount)
                              .Select(i => ConcurrencyUtils.RunOnDedicatedThreadAsync(() =>
            {
                Interlocked.Increment(ref state.AtomicProcessCount);
                try
                {
                    App.Logger.Info(nameof(VerifyFilesPhase), $"Processing thread {i} started");

                    _Process(state, toUpdate, processCancellationTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    App.Logger.Error(nameof(VerifyFilesPhase), $"Processing thread {i} canceled");
                    processCancellationTokenSource.Cancel();
                    throw;
                }
                catch (Exception ex)
                {
                    App.Logger.Error(nameof(VerifyFilesPhase), $"Exception in processing thread {i}", ex);
                    processCancellationTokenSource.Cancel();
                    throw;
                }
                finally
                {
                    Interlocked.Decrement(ref state.AtomicProcessCount);
                    App.Logger.Info(nameof(VerifyFilesPhase), $"Processing thread {i} ended");
                }
            }, $"{nameof(VerifyFilesPhase)}({i})")).ToArray();

            await Task.Run(async() =>
            {
                while (state.AtomicProcessCount > 0 || state.UpdateBuckets.Count != 0)
                {
                    await Task.Delay(250, ct);

                    Progress.Progress       = state.AtomicCompletedCount / (double)toUpdate.Length;
                    Progress.CompletedCount = state.AtomicCompletedCount;

                    if (state.UpdateBuckets.Count == 0)
                    {
                        continue;
                    }

                    while (state.UpdateBuckets.TryDequeue(out var list))
                    {
                        if (list.Count > 0)
                        {
                            await patchCache.InsertUnderTransactionAsync(list);
                        }
                    }
                }
            });

            Progress.IsIndeterminate = true;

            App.Logger.Info(nameof(VerifyFilesPhase), "Joining processing threads");
            try
            {
                await Task.WhenAll(threadTasks);
            }
            catch (Exception ex)
            {
                App.Logger.Error(nameof(VerifyFilesPhase), "Error verifying files", ex);
                throw;
            }
        }
Esempio n. 17
0
 // Assumes searchDirPaths don't overlap
 public static void CollectMods(Dictionary <ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths)
 {
Esempio n. 18
0
        public async Task RunAsync(PatchInfo[] toUpdate, PatchCache patchCache, CancellationToken ct = default)
        {
            Progress.Progress        = 0;
            Progress.IsIndeterminate = true;
            Progress.CompletedCount  = 0;
            Progress.TotalCount      = 0;

            if (toUpdate.Length == 0)
            {
                return;
            }

            var state = new ProcessState
            {
                AtomicIndex          = 0,
                AtomicCompletedCount = 0,
                AtomicProcessCount   = 0,
                UpdateBuckets        = new ConcurrentQueue <List <PatchCacheEntry> >()
            };

            var exceptions = new ConcurrentBag <Exception>();
            var errorCancellationTokenSource   = new CancellationTokenSource();
            var processCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(errorCancellationTokenSource.Token, ct);

            Progress.TotalCount = toUpdate.Length;

            App.Current.Logger.Info(nameof(VerifyFilesPhase), "Starting processing threads");
            Progress.IsIndeterminate = false;
            // TODO: processor affinity?
            var threads = Enumerable.Range(0, Environment.ProcessorCount).Select(i =>
            {
                var thread = new Thread(() =>
                {
                    Interlocked.Increment(ref state.AtomicProcessCount);
                    try
                    {
                        App.Current.Logger.Info(nameof(VerifyFilesPhase), $"Processing thread {i} started");

                        _ProcessAsync(state, toUpdate, processCancellationTokenSource.Token).GetAwaiter().GetResult();
                    }
                    catch (OperationCanceledException ex)
                    {
                        App.Current.Logger.Error(nameof(VerifyFilesPhase), $"Processing thread {i} canceled", ex);
                    }
                    catch (Exception ex)
                    {
                        App.Current.Logger.Error(nameof(VerifyFilesPhase), $"Exception in processing thread {i}", ex);
                        exceptions.Add(ex);

                        errorCancellationTokenSource.Cancel();
                    }
                    finally
                    {
                        Interlocked.Decrement(ref state.AtomicProcessCount);

                        App.Current.Logger.Info(nameof(VerifyFilesPhase), $"Processing thread {i} ended");
                    }
                });
                thread.Name = $"{nameof(VerifyFilesPhase)}({i})";
                thread.Start();

                return(thread);
            }).ToArray();

            await Task.Run(async() =>
            {
                while (state.AtomicProcessCount > 0 || state.UpdateBuckets.Count != 0)
                {
                    await Task.Delay(250, ct);

                    Progress.Progress       = state.AtomicCompletedCount / (double)toUpdate.Length;
                    Progress.CompletedCount = state.AtomicCompletedCount;

                    if (state.UpdateBuckets.Count == 0)
                    {
                        continue;
                    }

                    while (state.UpdateBuckets.TryDequeue(out var list))
                    {
                        if (list.Count > 0)
                        {
                            await patchCache.InsertUnderTransactionAsync(list);
                        }
                    }
                }
            });

            Progress.IsIndeterminate = true;

            App.Current.Logger.Info(nameof(VerifyFilesPhase), "Joining processing threads");
            foreach (var t in threads)
            {
                await Task.Factory.StartNew(() => t.Join(), TaskCreationOptions.LongRunning);
            }

            if (exceptions.Count > 0)
            {
                var aggregate = new AggregateException("Error verifying files", exceptions);
                App.Current.Logger.Error(nameof(VerifyFilesPhase), "Error verifying files", aggregate);
                throw aggregate;
            }
        }
Esempio n. 19
0
 public void Clear()
 {
     AppMods.Clear();
     Patches = new PatchCache();
 }
Esempio n. 20
0
 public ModLoader()
 {
     AppMods = new Dictionary <ulong, ModCache>();
     Patches = new PatchCache();
 }
Esempio n. 21
0
        public async Task LaunchAsync(bool shouldLaunchPSO2 = true)
        {
            IsLaunchingPSO2 = shouldLaunchPSO2;

            if (State == ApplicationState.Patching)
            {
                App.Logger.Info(nameof(MainWindowViewModel), "Cancelling launch");
                _LaunchCancellationTokenSource?.Cancel();
                return;
            }

            UploadErrorButtonVisible = false;
            IsChangelogVisible       = false;

            _ActivityCount += 1;

            App.Logger.Info(nameof(MainWindowViewModel), "Starting launch procedure");
            try
            {
                _LaunchCancellationTokenSource = new CancellationTokenSource();

                App.Logger.Info(nameof(MainWindowViewModel), "Saving client settings");
                await Task.Run(() =>
                {
                    Properties.Settings.Default.EnglishPatchEnabled  = ArksLayerEnglishPatchEnabled;
                    Properties.Settings.Default.TelepipeProxyEnabled = ArksLayerTelepipeProxyEnabled;
                    Properties.Settings.Default.ModFilesEnabled      = ModFilesEnabled;
                    Properties.Settings.Default.Save();
                });

                bool shouldRetry = true;
                while (shouldRetry)
                {
                    try
                    {
                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        var newPhases = new ObservableCollection <PhaseState>();

                        var pso2DirectoriesPhase      = new PSO2DirectoriesPhase(InstallConfiguration);
                        var pso2DirectoriesPhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_PSO2Directories"
                        };
                        newPhases.Add(pso2DirectoriesPhaseState);

                        ModFilesPhase modFilesPhase      = null;
                        PhaseState    modFilesPhaseState = null;
                        if (Properties.Settings.Default.ModFilesEnabled)
                        {
                            modFilesPhase      = new ModFilesPhase(InstallConfiguration);
                            modFilesPhaseState = new PhaseState
                            {
                                TitleKey = "MainWindow_Phase_ModFiles"
                            };
                            newPhases.Add(modFilesPhaseState);
                        }

                        var deleteCensorFilePhase      = new DeleteCensorFilePhase(InstallConfiguration);
                        var deleteCensorFilePhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_DeleteCensorFile"
                        };
                        newPhases.Add(deleteCensorFilePhaseState);

                        var downloadConfigurationState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_DownloadConfiguration"
                        };
                        newPhases.Add(downloadConfigurationState);

                        var patchCacheState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_PatchCache"
                        };
                        newPhases.Add(patchCacheState);

                        var comparePhase      = new ComparePhase(InstallConfiguration);
                        var comparePhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_Compare"
                        };
                        newPhases.Add(comparePhaseState);
                        {
                            var compareProgressControl = new ProgressControl();
                            compareProgressControl.SetBinding(ProgressControl.ProgressProperty, new Binding
                            {
                                Source = comparePhase.Progress,
                                Path   = new PropertyPath(nameof(comparePhase.Progress.Progress)),
                                Mode   = BindingMode.OneWay
                            });
                            compareProgressControl.SetBinding(ProgressControl.IsIndeterminateProperty, new Binding
                            {
                                Source = comparePhase.Progress,
                                Path   = new PropertyPath(nameof(comparePhase.Progress.IsIndeterminate)),
                                Mode   = BindingMode.OneWay
                            });
                            comparePhaseState.Child = compareProgressControl;
                        }

                        var verifyFilesPhase      = new VerifyFilesPhase(InstallConfiguration);
                        var verifyFilesPhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_VerifyFiles"
                        };
                        newPhases.Add(verifyFilesPhaseState);
                        {
                            var verifyFilesControl = new ProgressControl();
                            verifyFilesControl.SetBinding(ProgressControl.ProgressProperty, new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.Progress)),
                                Mode   = BindingMode.OneWay
                            });
                            verifyFilesControl.SetBinding(ProgressControl.IsIndeterminateProperty, new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.IsIndeterminate)),
                                Mode   = BindingMode.OneWay
                            });
                            var verifyFilesControlMessageBinding = new MultiBinding
                            {
                                Converter = StringFormatValueConverter.Instance
                            };
                            verifyFilesControlMessageBinding.Bindings.Add(new LocaleBindingExtension("MainWindow_Phase_VerifyFiles_Message"));
                            verifyFilesControlMessageBinding.Bindings.Add(new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.CompletedCount)),
                                Mode   = BindingMode.OneWay
                            });
                            verifyFilesControlMessageBinding.Bindings.Add(new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.TotalCount)),
                                Mode   = BindingMode.OneWay
                            });
                            verifyFilesControl.SetBinding(ProgressControl.MessageProperty, verifyFilesControlMessageBinding);
                            verifyFilesPhaseState.Child = verifyFilesControl;
                        }

                        var pluginInfoState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_PluginInfo"
                        };
                        newPhases.Add(pluginInfoState);

                        var pso2hPhase      = new PSO2hPhase(InstallConfiguration, ArksLayerEnglishPatchEnabled || ArksLayerTelepipeProxyEnabled);
                        var pso2hPhaseState = new PhaseState
                        {
                            TitleKey = ArksLayerEnglishPatchEnabled || ArksLayerTelepipeProxyEnabled
                                ? "MainWindow_Phase_PSO2h_TitleEnabled"
                                : "MainWindow_Phase_PSO2h_TitleDisabled"
                        };
                        newPhases.Add(pso2hPhaseState);

                        var telepipeProxyPhase      = new TelepipeProxyPhase(InstallConfiguration, ArksLayerTelepipeProxyEnabled);
                        var telepipeProxyPhaseState = new PhaseState
                        {
                            TitleKey = ArksLayerTelepipeProxyEnabled
                                ? "MainWindow_Phase_TelepipeProxy_TitleEnabled"
                                : "MainWindow_Phase_TelepipeProxy_TitleDisabled"
                        };
                        newPhases.Add(telepipeProxyPhaseState);

                        var englishPatchPhase      = new EnglishPatchPhase(InstallConfiguration, ArksLayerEnglishPatchEnabled);
                        var englishPatchPhaseState = new PhaseState
                        {
                            TitleKey = ArksLayerEnglishPatchEnabled
                                ? "MainWindow_Phase_EnglishPatch_TitleEnabled"
                                : "MainWindow_Phase_EnglishPatch_TitleDisabled"
                        };
                        newPhases.Add(englishPatchPhaseState);

                        LargeAddressAwarePhase largeAddressAwarePhase      = null;
                        PhaseState             largeAddressAwarePhaseState = null;
                        if (Properties.Settings.Default.LargeAddressAwareEnabled)
                        {
                            largeAddressAwarePhase      = new LargeAddressAwarePhase(InstallConfiguration);
                            largeAddressAwarePhaseState = new PhaseState
                            {
                                TitleKey = "MainWindow_Phase_LargeAddressAware"
                            };
                            newPhases.Add(largeAddressAwarePhaseState);
                        }

                        var launchPSO2State = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_LaunchPSO2"
                        };
                        if (IsLaunchingPSO2)
                        {
                            newPhases.Add(launchPSO2State);
                        }

                        Phases = newPhases;

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Logger.Info("Launch", $"Running {nameof(PSO2DirectoriesPhase)}");
                        await _AttemptPhase(pso2DirectoriesPhaseState,
                                            () => pso2DirectoriesPhase.RunAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        if (modFilesPhase != null && modFilesPhaseState != null)
                        {
                            App.Logger.Info("Launch", $"Running {nameof(ModFilesPhase)}");
                            await _AttemptPhase(modFilesPhaseState,
                                                () => modFilesPhase.RunAsync(_LaunchCancellationTokenSource.Token));
                        }

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Logger.Info("Launch", $"Running {nameof(DeleteCensorFilePhase)}");
                        await _AttemptPhase(deleteCensorFilePhaseState,
                                            () => deleteCensorFilePhase.RunAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Logger.Info("Launch", "Fetching download configuration");
                        var downloadConfiguration = await _AttemptPhase(downloadConfigurationState,
                                                                        () => DownloadConfiguration.CreateDefaultAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Logger.Info("Launch", "Connecting to patch cache database");
                        using (var patchCache = await _AttemptPhase(patchCacheState, () => PatchCache.CreateAsync(InstallConfiguration)))
                        {
                            _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                            // loop this block so files that were updated while a possibly long
                            // verify phase took place are not missed
                            while (true)
                            {
                                comparePhaseState.State     = PhaseControl.State.Queued;
                                verifyFilesPhaseState.State = PhaseControl.State.Queued;

                                _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                                App.Logger.Info("Launch", $"Running {nameof(ComparePhase)}");
                                var toUpdate = await _AttemptPhase(comparePhaseState,
                                                                   () => comparePhase.RunAsync(downloadConfiguration, patchCache, _LaunchCancellationTokenSource.Token));

                                if (toUpdate.Length == 0)
                                {
                                    verifyFilesPhaseState.State = PhaseControl.State.Success;
                                    break;
                                }

                                _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                                App.Logger.Info("Launch", $"Running {nameof(VerifyFilesPhase)}");
                                await _AttemptPhase(verifyFilesPhaseState,
                                                    () => verifyFilesPhase.RunAsync(toUpdate, patchCache, _LaunchCancellationTokenSource.Token));
                            }

                            _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                            try
                            {
                                App.Logger.Info("Launch", "Fetching plugin info");
                                var pluginInfo = await _AttemptPhase(pluginInfoState,
                                                                     () => PluginInfo.FetchAsync(_LaunchCancellationTokenSource.Token));

                                _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                                App.Logger.Info("Launch", $"Running {nameof(PSO2hPhase)}");
                                await _AttemptPhase(pso2hPhaseState,
                                                    () => pso2hPhase.RunAsync(pluginInfo, _LaunchCancellationTokenSource.Token));

                                _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                                App.Logger.Info("Launch", $"Running {nameof(TelepipeProxyPhase)}");
                                await _AttemptPhase(telepipeProxyPhaseState,
                                                    () => telepipeProxyPhase.RunAsync(pluginInfo, _LaunchCancellationTokenSource.Token));

                                _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                                App.Logger.Info("Launch", $"Running {nameof(EnglishPatchPhase)}");
                                await _AttemptPhase(englishPatchPhaseState,
                                                    () => englishPatchPhase.RunAsync(patchCache, pluginInfo, _LaunchCancellationTokenSource.Token));

                                _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();
                            }
                            catch (OperationCanceledException)
                            {
                                throw;
                            }
                            catch (Exception ex)
                            {
                                App.Logger.Error("Launch", "Error in Arks-Layer tasks", ex);

                                // only bother the user about this stuff if anything related to arks-layer is currently enabled
                                // (currently this wont attempt to clean up anything arks-layer related so if someone was to decide
                                // they no longer want to use the one of the plugins here, the disabling of them would not work)
                                if (ArksLayerEnglishPatchEnabled || ArksLayerTelepipeProxyEnabled)
                                {
                                    var skipArksLayerResult = MessageBox.Show(
                                        LocaleManager.Instance["MainWindow_ArksLayerErrorMessage"],
                                        LocaleManager.Instance["PSRTAstra"],
                                        MessageBoxButton.YesNo,
                                        MessageBoxImage.Exclamation);

                                    if (skipArksLayerResult == MessageBoxResult.No)
                                    {
                                        shouldRetry = false;
                                        throw;
                                    }
                                }
                            }

                            if (largeAddressAwarePhaseState != null && largeAddressAwarePhase != null)
                            {
                                App.Logger.Info("Launch", $"Running {nameof(LargeAddressAwarePhase)}");
                                await _AttemptPhase(largeAddressAwarePhaseState,
                                                    () => largeAddressAwarePhase.RunAsync(_LaunchCancellationTokenSource.Token));
                            }
                        }

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();
                        if (!IsLaunchingPSO2)
                        {
                            break;
                        }

                        // cancellation no longer works after this point

                        App.Logger.Info("Launch", "Starting PSO2");

                        await _AttemptPhase(launchPSO2State, async() =>
                        {
                            var startInfo = new ProcessStartInfo()
                            {
                                FileName        = InstallConfiguration.PSO2Executable,
                                Arguments       = "+0x33aca2b9",
                                UseShellExecute = false
                            };
                            startInfo.EnvironmentVariables["-pso2"] = "+0x01e3f1e9";

                            await Task.Run(() =>
                            {
                                var process = new Process()
                                {
                                    StartInfo = startInfo
                                };
                                process.Start();

                                process.WaitForExit();
                            });
                        });

                        App.Logger.Info("Launch", "PSO2 launch process ended");

                        if (Properties.Settings.Default.CloseOnLaunchEnabled)
                        {
                            App.Current.Shutdown();
                        }
                    }
                    // only properly cancel the launch if it was canceled by us
                    catch (OperationCanceledException ex) when(_LaunchCancellationTokenSource.IsCancellationRequested)
                    {
                        App.Logger.Info("Launch", "Launch cancelled", ex);
                    }
                    catch (Exception ex)
                    {
                        App.Logger.Error("Launch", "Error during launch phases", ex);
                        UploadErrorButtonVisible = true;

                        if (shouldRetry)
                        {
                            try
                            {
                                await Task.Delay(TimeSpan.FromSeconds(10), _LaunchCancellationTokenSource.Token);
                            }
                            catch (OperationCanceledException)
                            {
                                App.Logger.Info("Launch", "Launch was canceled during the auto-restart period");
                            }
                        }

                        continue;
                    }

                    break;
                }
            }
            finally
            {
                _ActivityCount -= 1;
                _LaunchCancellationTokenSource = null;
            }
        }
Esempio n. 22
0
        public async Task LaunchAsync()
        {
            if (State == ApplicationState.Patching)
            {
                App.Current.Logger.Info(nameof(MainWindowViewModel), "Cancelling launch");
                _LaunchCancellationTokenSource?.Cancel();
                return;
            }

            UploadErrorButtonVisible = false;
            IsChangelogVisible       = false;

            _ActivityCount += 1;

            App.Current.Logger.Info(nameof(MainWindowViewModel), "Starting launch procedure");
            try
            {
                _LaunchCancellationTokenSource = new CancellationTokenSource();

                App.Current.Logger.Info(nameof(MainWindowViewModel), "Saving client settings");
                await Task.Run(() =>
                {
                    Properties.Settings.Default.EnglishPatchEnabled  = ArksLayerEnglishPatchEnabled;
                    Properties.Settings.Default.TelepipeProxyEnabled = ArksLayerTelepipeProxyEnabled;
                    Properties.Settings.Default.ModFilesEnabled      = ModFilesEnabled;
                    Properties.Settings.Default.Save();
                });

                while (true)
                {
                    try
                    {
                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        var newPhases = new ObservableCollection <PhaseState>();

                        var pso2DirectoriesPhase      = new PSO2DirectoriesPhase(InstallConfiguration);
                        var pso2DirectoriesPhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_PSO2Directories"
                        };
                        newPhases.Add(pso2DirectoriesPhaseState);

                        ModFilesPhase modFilesPhase      = null;
                        PhaseState    modFilesPhaseState = null;
                        if (Properties.Settings.Default.ModFilesEnabled)
                        {
                            modFilesPhase      = new ModFilesPhase(InstallConfiguration);
                            modFilesPhaseState = new PhaseState
                            {
                                TitleKey = "MainWindow_Phase_ModFiles"
                            };
                            newPhases.Add(modFilesPhaseState);
                        }

                        var deleteCensorFilePhase      = new DeleteCensorFilePhase(InstallConfiguration);
                        var deleteCensorFilePhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_DeleteCensorFile"
                        };
                        newPhases.Add(deleteCensorFilePhaseState);

                        var downloadConfigurationState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_DownloadConfiguration"
                        };
                        newPhases.Add(downloadConfigurationState);

                        var patchCacheState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_PatchCache"
                        };
                        newPhases.Add(patchCacheState);

                        var comparePhase      = new ComparePhase(InstallConfiguration);
                        var comparePhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_Compare"
                        };
                        newPhases.Add(comparePhaseState);
                        {
                            var compareProgressControl = new ProgressControl();
                            compareProgressControl.SetBinding(ProgressControl.ProgressProperty, new Binding
                            {
                                Source = comparePhase.Progress,
                                Path   = new PropertyPath(nameof(comparePhase.Progress.Progress)),
                                Mode   = BindingMode.OneWay
                            });
                            compareProgressControl.SetBinding(ProgressControl.IsIndeterminateProperty, new Binding
                            {
                                Source = comparePhase.Progress,
                                Path   = new PropertyPath(nameof(comparePhase.Progress.IsIndeterminate)),
                                Mode   = BindingMode.OneWay
                            });
                            comparePhaseState.Child = compareProgressControl;
                        }

                        var verifyFilesPhase      = new VerifyFilesPhase(InstallConfiguration);
                        var verifyFilesPhaseState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_VerifyFiles"
                        };
                        newPhases.Add(verifyFilesPhaseState);
                        {
                            var verifyFilesControl = new ProgressControl();
                            verifyFilesControl.SetBinding(ProgressControl.ProgressProperty, new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.Progress)),
                                Mode   = BindingMode.OneWay
                            });
                            verifyFilesControl.SetBinding(ProgressControl.IsIndeterminateProperty, new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.IsIndeterminate)),
                                Mode   = BindingMode.OneWay
                            });
                            var verifyFilesControlMessageBinding = new MultiBinding
                            {
                                Converter = StringFormatValueConverter.Instance
                            };
                            verifyFilesControlMessageBinding.Bindings.Add(new LocaleBindingExtension("MainWindow_Phase_VerifyFiles_Message"));
                            verifyFilesControlMessageBinding.Bindings.Add(new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.CompletedCount)),
                                Mode   = BindingMode.OneWay
                            });
                            verifyFilesControlMessageBinding.Bindings.Add(new Binding
                            {
                                Source = verifyFilesPhase.Progress,
                                Path   = new PropertyPath(nameof(verifyFilesPhase.Progress.TotalCount)),
                                Mode   = BindingMode.OneWay
                            });
                            verifyFilesControl.SetBinding(ProgressControl.MessageProperty, verifyFilesControlMessageBinding);
                            verifyFilesPhaseState.Child = verifyFilesControl;
                        }

                        var pluginInfoState = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_PluginInfo"
                        };
                        newPhases.Add(pluginInfoState);

                        var pso2hPhase      = new PSO2hPhase(InstallConfiguration, ArksLayerEnglishPatchEnabled || ArksLayerTelepipeProxyEnabled);
                        var pso2hPhaseState = new PhaseState
                        {
                            TitleKey = ArksLayerEnglishPatchEnabled || ArksLayerTelepipeProxyEnabled
                                ? "MainWindow_Phase_PSO2h_TitleEnabled"
                                : "MainWindow_Phase_PSO2h_TitleDisabled"
                        };
                        newPhases.Add(pso2hPhaseState);

                        var telepipeProxyPhase      = new TelepipeProxyPhase(InstallConfiguration, ArksLayerTelepipeProxyEnabled);
                        var telepipeProxyPhaseState = new PhaseState
                        {
                            TitleKey = ArksLayerTelepipeProxyEnabled
                                ? "MainWindow_Phase_TelepipeProxy_TitleEnabled"
                                : "MainWindow_Phase_TelepipeProxy_TitleDisabled"
                        };
                        newPhases.Add(telepipeProxyPhaseState);

                        var englishPatchPhase      = new EnglishPatchPhase(InstallConfiguration, ArksLayerEnglishPatchEnabled);
                        var englishPatchPhaseState = new PhaseState
                        {
                            TitleKey = ArksLayerEnglishPatchEnabled
                                ? "MainWindow_Phase_EnglishPatch_TitleEnabled"
                                : "MainWindow_Phase_EnglishPatch_TitleDisabled"
                        };
                        newPhases.Add(englishPatchPhaseState);

                        LargeAddressAwarePhase largeAddressAwarePhase      = null;
                        PhaseState             largeAddressAwarePhaseState = null;
                        if (Properties.Settings.Default.LargeAddressAwareEnabled)
                        {
                            largeAddressAwarePhase      = new LargeAddressAwarePhase(InstallConfiguration);
                            largeAddressAwarePhaseState = new PhaseState
                            {
                                TitleKey = "MainWindow_Phase_LargeAddressAware"
                            };
                            newPhases.Add(largeAddressAwarePhaseState);
                        }

                        var launchPSO2State = new PhaseState
                        {
                            TitleKey = "MainWindow_Phase_LaunchPSO2"
                        };
                        newPhases.Add(launchPSO2State);

                        Phases = newPhases;


                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", $"Running {nameof(PSO2DirectoriesPhase)}");
                        await _AttemptPhase(pso2DirectoriesPhaseState,
                                            () => pso2DirectoriesPhase.RunAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        if (modFilesPhase != null && modFilesPhaseState != null)
                        {
                            App.Current.Logger.Info("Launch", $"Running {nameof(ModFilesPhase)}");
                            await _AttemptPhase(modFilesPhaseState,
                                                () => modFilesPhase.RunAsync(_LaunchCancellationTokenSource.Token));
                        }

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", $"Running {nameof(DeleteCensorFilePhase)}");
                        await _AttemptPhase(deleteCensorFilePhaseState,
                                            () => deleteCensorFilePhase.RunAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", "Fetching download configuration");
                        var downloadConfiguration = await _AttemptPhase(downloadConfigurationState,
                                                                        () => DownloadConfiguration.CreateDefaultAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", "Connecting to patch cache database");
                        var patchCache = await _AttemptPhase(patchCacheState,
                                                             () => PatchCache.CreateAsync(InstallConfiguration));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        // loop this block so files that were updated while a possibly long
                        // verify phase took place are not missed
                        while (true)
                        {
                            comparePhaseState.State     = PhaseControl.State.Queued;
                            verifyFilesPhaseState.State = PhaseControl.State.Queued;

                            _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                            App.Current.Logger.Info("Launch", $"Running {nameof(ComparePhase)}");
                            var toUpdate = await _AttemptPhase(comparePhaseState,
                                                               () => comparePhase.RunAsync(downloadConfiguration, patchCache, _LaunchCancellationTokenSource.Token));

                            if (toUpdate.Length == 0)
                            {
                                verifyFilesPhaseState.State = PhaseControl.State.Success;
                                break;
                            }

                            _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                            App.Current.Logger.Info("Launch", $"Running {nameof(VerifyFilesPhase)}");
                            await _AttemptPhase(verifyFilesPhaseState,
                                                () => verifyFilesPhase.RunAsync(toUpdate, patchCache, _LaunchCancellationTokenSource.Token));
                        }

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", "Fetching plugin info");
                        var pluginInfo = await _AttemptPhase(pluginInfoState,
                                                             () => PluginInfo.FetchAsync(_LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", $"Running {nameof(PSO2hPhase)}");
                        await _AttemptPhase(pso2hPhaseState,
                                            () => pso2hPhase.RunAsync(pluginInfo, _LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", $"Running {nameof(TelepipeProxyPhase)}");
                        await _AttemptPhase(telepipeProxyPhaseState,
                                            () => telepipeProxyPhase.RunAsync(pluginInfo, _LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        App.Current.Logger.Info("Launch", $"Running {nameof(EnglishPatchPhase)}");
                        await _AttemptPhase(englishPatchPhaseState,
                                            () => englishPatchPhase.RunAsync(patchCache, pluginInfo, _LaunchCancellationTokenSource.Token));

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        if (largeAddressAwarePhaseState != null && largeAddressAwarePhase != null)
                        {
                            App.Current.Logger.Info("Launch", $"Running {nameof(LargeAddressAwarePhase)}");
                            await _AttemptPhase(largeAddressAwarePhaseState,
                                                () => largeAddressAwarePhase.RunAsync(_LaunchCancellationTokenSource.Token));
                        }

                        _LaunchCancellationTokenSource.Token.ThrowIfCancellationRequested();

                        // cancellation no longer works after this point

                        App.Current.Logger.Info("Launch", "Starting PSO2");

                        await _AttemptPhase(launchPSO2State, async() =>
                        {
                            var startInfo = new ProcessStartInfo()
                            {
                                FileName        = InstallConfiguration.PSO2Executable,
                                Arguments       = "+0x33aca2b9",
                                UseShellExecute = false
                            };
                            startInfo.EnvironmentVariables["-pso2"] = "+0x01e3f1e9";

                            await Task.Run(() =>
                            {
                                var process = new Process()
                                {
                                    StartInfo = startInfo
                                };
                                process.Start();

                                process.WaitForExit();
                            });
                        });

                        App.Current.Logger.Info("Launch", "PSO2 launch process ended");
                    }
                    catch (OperationCanceledException)
                    {
                        App.Current.Logger.Info("Launch", "Launch cancelled");
                        return;
                    }
                    catch (Exception ex)
                    {
                        App.Current.Logger.Info("Launch", "Error during launch phases", ex);
                        UploadErrorButtonVisible = true;

                        try
                        {
                            await Task.Delay(TimeSpan.FromSeconds(10), _LaunchCancellationTokenSource.Token);
                        }
                        catch (OperationCanceledException) { }

                        continue;
                    }

                    break;
                }
            }
            finally
            {
                _ActivityCount -= 1;
                _LaunchCancellationTokenSource = null;
            }
        }
Esempio n. 23
0
        private async Task _InstallAsync(PatchCache patchCache, PluginInfo pluginInfo, CancellationToken ct = default)
        {
            App.Current.Logger.Info(nameof(EnglishPatchPhase), "Downloading english translation information");
            var translation = await TranslationInfo.FetchEnglishAsync(ct);

            App.Current.Logger.Info(nameof(EnglishPatchPhase), "Getting data from patch cache");
            var cacheData = await patchCache.SelectAllAsync();

            string CreateRelativePath(string path)
            {
                var root     = new Uri(_InstallConfiguration.PSO2BinDirectory);
                var relative = root.MakeRelativeUri(new Uri(path));

                return(relative.OriginalString);
            }

            bool Verify(string path, string hash)
            {
                var relative = CreateRelativePath(path);

                var info = new FileInfo(path);

                if (info.Exists == false)
                {
                    return(false);
                }

                if (cacheData.ContainsKey(relative) == false)
                {
                    return(false);
                }

                var data = cacheData[relative];

                if (data.Hash != hash)
                {
                    return(false);
                }

                if (data.LastWriteTime != info.LastWriteTimeUtc.ToFileTimeUtc())
                {
                    return(false);
                }

                return(true);
            }

            using (var client = new ArksLayerHttpClient())
            {
                async Task VerifyAndDownlodRar(string path, string downloadHash, Uri downloadPath)
                {
                    if (Verify(path, downloadHash) == false)
                    {
                        App.Current.Logger.Info(nameof(EnglishPatchPhase), $"Downloading \"{Path.GetFileName(downloadPath.LocalPath)}\"");
                        using (var response = await client.GetAsync(downloadPath))
                            using (var stream = await response.Content.ReadAsStreamAsync())
                                using (var archive = RarArchive.Open(stream))
                                {
                                    foreach (var file in archive.Entries.Where(e => !e.IsDirectory))
                                    {
                                        var filePath = Path.Combine(_InstallConfiguration.ArksLayer.PatchesDirectory, file.Key);

                                        using (var fs = File.Create(filePath, 4096, FileOptions.Asynchronous))
                                            await file.OpenEntryStream().CopyToAsync(fs);

                                        await patchCache.InsertUnderTransactionAsync(new[]
                                        {
                                            new PatchCacheEntry()
                                            {
                                                Name          = CreateRelativePath(filePath),
                                                Hash          = downloadHash,
                                                LastWriteTime = new FileInfo(filePath).LastWriteTimeUtc.ToFileTimeUtc()
                                            }
                                        });
                                    }
                                }
                    }
                }

                await VerifyAndDownlodRar(_InstallConfiguration.ArksLayer.EnglishBlockPatch, translation.BlockMD5, new Uri(translation.BlockPatch));
                await VerifyAndDownlodRar(_InstallConfiguration.ArksLayer.EnglishItemPatch, translation.ItemMD5, new Uri(translation.ItemPatch));
                await VerifyAndDownlodRar(_InstallConfiguration.ArksLayer.EnglishTextPatch, translation.TextMD5, new Uri(translation.TextPatch));
                await VerifyAndDownlodRar(_InstallConfiguration.ArksLayer.EnglishTitlePatch, translation.TitleMD5, new Uri(translation.TitlePatch));
            }

            App.Current.Logger.Info(nameof(EnglishPatchPhase), "Validating plugin dlls");

            await pluginInfo.PSO2BlockRenameDll.ValidateFileAsync(_InstallConfiguration.ArksLayer.PluginPSO2BlockRenameDll, ct);

            await pluginInfo.PSO2ItemTranslatorDll.ValidateFileAsync(_InstallConfiguration.ArksLayer.PluginPSO2ItemTranslatorDll, ct);

            await pluginInfo.PSO2TitleTranslatorDll.ValidateFileAsync(_InstallConfiguration.ArksLayer.PluginPSO2TitleTranslatorDll, ct);

            await pluginInfo.PSO2RAISERSystemDll.ValidateFileAsync(_InstallConfiguration.ArksLayer.PluginPSO2RAISERSystemDll, ct);
        }
Esempio n. 24
0
 public static void CollectMods(ModCache mods, PatchCache patches, ulong?titleId, params string[] searchDirPaths)
 {
Esempio n. 25
0
        public async Task <PatchInfo[]> RunAsync(DownloadConfiguration downloadConfiguration, PatchCache patchCache, CancellationToken ct = default)
        {
            Progress.Progress        = 0;
            Progress.IsIndeterminate = true;

            App.Logger.Info(nameof(ComparePhase), "Fetching patches");
            var patches = await PatchInfo.FetchPatchInfosAsync(_InstallConfiguration, downloadConfiguration, ct);

            App.Logger.Info(nameof(ComparePhase), "Fetching cache data");
            var cacheData = await patchCache.SelectAllAsync();

            App.Logger.Info(nameof(ComparePhase), "Pre-fetching file times");
            var preFetchedFileTimes = await Task.Run(() => _PreFetchFileTimes());

            var workingPatches = patches
                                 .Select(p => new UpdateInfo
            {
                PatchInfo         = p,
                ShouldUpdate      = true,
                LastWriteFileTime = preFetchedFileTimes.ContainsKey(p.Name) ? preFetchedFileTimes[p.Name] : 0
            })
                                 .ToArray();

            var nextIndexAtomic     = 0;
            var progressValueAtomic = 0;

            var errorTokenSource    = new CancellationTokenSource();
            var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ct, errorTokenSource.Token);

            App.Logger.Info(nameof(ComparePhase), "Comparing files");
            Progress.IsIndeterminate = false;

            void ProcessLoop()
            {
                try
                {
                    var pso2ExecutableFileName = Path.GetFileName(_InstallConfiguration.PSO2Executable);

                    while (true)
                    {
                        var index = Interlocked.Increment(ref nextIndexAtomic) - 1;
                        if (index >= workingPatches.Length)
                        {
                            return;
                        }

                        combinedTokenSource.Token.ThrowIfCancellationRequested();

                        try
                        {
                            ref var patch = ref workingPatches[index];

                            var relativeFilePath = patch.PatchInfo.Name
                                                   .Substring(0, patch.PatchInfo.Name.Length - 4)
                                                   .Replace('/', Path.DirectorySeparatorChar)
                                                   .ToLower();
                            var filePath = Path.Combine(_InstallConfiguration.PSO2BinDirectory, relativeFilePath);
                            var fileName = Path.GetFileName(relativeFilePath);

                            // skip this if mod files are not enabled so they are marked as invalid
                            if (Properties.Settings.Default.ModFilesEnabled)
                            {
                                // skip file if a file with the same name exists in the mods folder
                                if (Path.GetDirectoryName(filePath).ToLower() == _InstallConfiguration.DataWin32Directory.ToLower())
                                {
                                    var modFilePath = Path.Combine(_InstallConfiguration.ModsDirectory, fileName);
                                    if (File.Exists(modFilePath))
                                    {
                                        patch.ShouldUpdate = false;
                                        continue;
                                    }
                                }
                            }

                            if (!cacheData.ContainsKey(patch.PatchInfo.Name))
                            {
                                continue;
                            }

                            var cacheEntry = cacheData[patch.PatchInfo.Name];

                            if (patch.PatchInfo.Hash != cacheEntry.Hash)
                            {
                                continue;
                            }

                            // skip pso2.exe if the file is large address aware patched
                            if (fileName == pso2ExecutableFileName &&
                                File.Exists(filePath) &&
                                Properties.Settings.Default.LargeAddressAwareEnabled &&
                                LargeAddressAware.IsLargeAddressAwarePactchApplied(_InstallConfiguration, patch.PatchInfo.Hash))
                            {
                                patch.ShouldUpdate = false;
                                continue;
                            }

                            if (patch.LastWriteFileTime == 0)
                            {
                                patch.LastWriteFileTime = new FileInfo(filePath).LastWriteTimeUtc.ToFileTimeUtc();
                            }

                            patch.ShouldUpdate = patch.LastWriteFileTime != cacheEntry.LastWriteTime;
                        }
                        finally
                        {
                            Interlocked.Increment(ref progressValueAtomic);
                        }
                    }
                }