public PacketDocumentViewModel(PacketDocumentSolutionItem solutionItem,
                                       IMainThread mainThread,
                                       MostRecentlySearchedService mostRecentlySearchedService,
                                       IDatabaseProvider databaseProvider,
                                       Func <INativeTextDocument> nativeTextDocumentCreator,
                                       IMessageBoxService messageBoxService,
                                       IPacketFilteringService filteringService,
                                       IDocumentManager documentManager,
                                       ITextDocumentService textDocumentService,
                                       IWindowManager windowManager,
                                       IPacketViewerSettings packetSettings,
                                       IEnumerable <IPacketDumperProvider> dumperProviders,
                                       IInputBoxService inputBoxService,
                                       IHistoryManager history,
                                       IPacketFilterDialogService filterDialogService,
                                       IActionReactionProcessorCreator actionReactionProcessorCreator,
                                       IRelatedPacketsFinder relatedPacketsFinder,
                                       ITeachingTipService teachingTipService,
                                       PacketDocumentSolutionNameProvider solutionNameProvider,
                                       ISniffLoader sniffLoader,
                                       ISpellStore spellStore,
                                       PrettyFlagParameter prettyFlagParameter,
                                       Func <IUpdateFieldsHistory> historyCreator)
        {
            this.solutionItem      = solutionItem;
            this.mainThread        = mainThread;
            this.messageBoxService = messageBoxService;
            this.filteringService  = filteringService;
            this.actionReactionProcessorCreator = actionReactionProcessorCreator;
            this.relatedPacketsFinder           = relatedPacketsFinder;
            this.sniffLoader = sniffLoader;
            History          = history;
            history.LimitStack(20);
            packetViewModelCreator = new PacketViewModelFactory(databaseProvider, spellStore);
            MostRecentlySearched   = mostRecentlySearchedService.MostRecentlySearched;
            Title                 = solutionNameProvider.GetName(this.solutionItem);
            SolutionItem          = solutionItem;
            FilterText            = nativeTextDocumentCreator();
            SelectedPacketPreview = nativeTextDocumentCreator();
            SelectedPacketPreview.DisableUndo();
            Watch(this, t => t.FilteringProgress, nameof(ProgressUnknown));

            AutoDispose(history.AddHandler(new SelectedPacketHistory(this)));

            filteredPackets    = visiblePackets = AllPackets;
            ApplyFilterCommand = new AsyncCommand(async() =>
            {
                if (inApplyFilterCommand)
                {
                    return;
                }

                inApplyFilterCommand     = true;
                MostRecentlySearchedItem = FilterText.ToString();
                inApplyFilterCommand     = false;

                if (!string.IsNullOrEmpty(FilterText.ToString()))
                {
                    mostRecentlySearchedService.Add(FilterText.ToString());
                }

                if (currentActionToken != filteringToken)
                {
                    throw new Exception("Invalid concurrent access!");
                }
                filteringToken?.Cancel();
                filteringToken     = null;
                currentActionToken = null;
                await FilterPackets(FilterText.ToString());
            });

            SaveToFileCommand = new AsyncCommand(async() =>
            {
                var path = await windowManager.ShowSaveFileDialog("Text file|txt");
                if (path == null)
                {
                    return;
                }

                LoadingInProgress   = true;
                FilteringInProgress = true;
                try
                {
                    await using StreamWriter writer = File.CreateText(path);
                    int i     = 0;
                    int count = FilteredPackets.Count;
                    foreach (var packet in FilteredPackets)
                    {
                        await writer.WriteLineAsync(packet.Text);
                        if ((i % 100) == 0)
                        {
                            Report(i * 1.0f / count);
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    await this.messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                            .SetTitle("Fatal error")
                                                            .SetMainInstruction("Fatal error during saving file")
                                                            .SetContent(e.Message)
                                                            .WithOkButton(false)
                                                            .Build());
                }
                LoadingInProgress   = false;
                FilteringInProgress = false;
            });

            OpenHelpCommand = new DelegateCommand(() => windowManager.OpenUrl("https://github.com/BAndysc/WoWDatabaseEditor/wiki/Packet-Viewer"));

            On(() => SelectedPacket, doc =>
            {
                if (doc != null)
                {
                    SelectedPacketPreview.FromString(doc.Text);
                }
            });

            On(() => SplitUpdate, _ =>
            {
                SplitPacketsIfNeededAsync().Wait();
                ((ICommand)ApplyFilterCommand).Execute(null);
            });

            On(() => HidePlayerMove, _ =>
            {
                ((ICommand)ApplyFilterCommand).Execute(null);
            });

            On(() => DisableFilters, _ =>
            {
                ((ICommand)ApplyFilterCommand).Execute(null);
            });

            foreach (var dumper in dumperProviders)
            {
                Processors.Add(new(dumper));
            }

            QuickRunProcessor = new AsyncAutoCommand <ProcessorViewModel>(async(processor) =>
            {
                LoadingInProgress   = true;
                FilteringInProgress = true;
                try
                {
                    var tokenSource = new CancellationTokenSource();
                    AssertNoOnGoingTask();
                    currentActionToken = tokenSource;

                    var output = await RunProcessorsThreaded(new List <ProcessorViewModel>()
                    {
                        processor
                    }, tokenSource.Token).ConfigureAwait(true);

                    if (output != null && !tokenSource.IsCancellationRequested)
                    {
                        documentManager.OpenDocument(textDocumentService.CreateDocument($"{processor.Name} ({Title})", output, processor.Extension, true));
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    await messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                       .SetIcon(MessageBoxIcon.Error)
                                                       .SetTitle("Fatal error")
                                                       .SetMainInstruction("Fatal error during processing")
                                                       .SetContent(
                                                           "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e)
                                                       .WithOkButton(true)
                                                       .Build());
                }
                LoadingInProgress   = false;
                FilteringInProgress = false;
                currentActionToken  = null;
            });

            RunProcessors = new AsyncAutoCommand(async() =>
            {
                var processors = Processors.Where(s => s.IsChecked).ToList();
                if (processors.Count == 0)
                {
                    return;
                }

                LoadingInProgress   = true;
                FilteringInProgress = true;
                try
                {
                    var tokenSource = new CancellationTokenSource();
                    AssertNoOnGoingTask();
                    currentActionToken = tokenSource;

                    var output = await RunProcessorsThreaded(processors, tokenSource.Token).ConfigureAwait(true);

                    if (output != null && !tokenSource.IsCancellationRequested)
                    {
                        var extension = processors.Select(p => p.Extension).Distinct().Count() == 1
                            ? processors[0].Extension
                            : "txt";
                        documentManager.OpenDocument(textDocumentService.CreateDocument($"{processors[0].Name} ({Title})", output, extension, true));
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    await messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                       .SetIcon(MessageBoxIcon.Error)
                                                       .SetTitle("Fatal error")
                                                       .SetMainInstruction("Fatal error during processing")
                                                       .SetContent(
                                                           "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e)
                                                       .WithOkButton(true)
                                                       .Build());
                }
                LoadingInProgress   = false;
                FilteringInProgress = false;
                currentActionToken  = null;
            }, () => Processors.Any(c => c.IsChecked));

            foreach (var proc in Processors)
            {
                AutoDispose(proc.ToObservable(p => p.IsChecked)
                            .SubscribeAction(_ => RunProcessors.RaiseCanExecuteChanged()));
            }

            UndoCommand              = new DelegateCommand(history.Undo, () => history.CanUndo);
            RedoCommand              = new DelegateCommand(history.Redo, () => history.CanRedo);
            History.PropertyChanged += (_, _) =>
            {
                UndoCommand.RaiseCanExecuteChanged();
                RedoCommand.RaiseCanExecuteChanged();
            };

            IEnumerable <int> GetFindEnumerator(int start, int count, int direction, bool wrap)
            {
                for (int i = start + direction; i >= 0 && i < count; i += direction)
                {
                    yield return(i);
                }

                if (wrap)
                {
                    if (direction > 0)
                    {
                        for (int i = 0; i < start; ++i)
                        {
                            yield return(i);
                        }
                    }
                    else
                    {
                        for (int i = count - 1; i > start; --i)
                        {
                            yield return(i);
                        }
                    }
                }
            }

            async Task Find(string searchText, int start, int direction)
            {
                var count         = VisiblePackets.Count;
                var searchToLower = searchText.ToLower();

                foreach (var i in GetFindEnumerator(start, count, direction, true))
                {
                    if (VisiblePackets[i].Text.Contains(searchToLower, StringComparison.InvariantCultureIgnoreCase))
                    {
                        SelectedPacket = VisiblePackets[i];
                        return;
                    }
                }

                await messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                   .SetTitle("Find")
                                                   .SetMainInstruction("Not found")
                                                   .SetContent("Cannot find text: " + searchText)
                                                   .WithOkButton(true)
                                                   .Build());
            }

            ToggleFindCommand   = new DelegateCommand(() => FindPanelEnabled = !FindPanelEnabled);
            CloseFindCommand    = new DelegateCommand(() => FindPanelEnabled = false);
            FindPreviousCommand = new AsyncAutoCommand <string>(async searchText =>
            {
                var start = SelectedPacket != null ? VisiblePackets.IndexOf(SelectedPacket) : 0;
                await Find(searchText, start, -1);
            }, str => str is string s && !string.IsNullOrEmpty(s));
            FindNextCommand = new AsyncAutoCommand <string>(async searchText =>
            {
                var start = SelectedPacket != null ? VisiblePackets.IndexOf(SelectedPacket) : 0;
                await Find(searchText, start, 1);
            }, str => str is string s && !string.IsNullOrEmpty(s));

            FindRelatedPacketsCommands = new AsyncAutoCommand(async() =>
            {
                if (selectedPacket == null)
                {
                    return;
                }

                if (!await EnsureSplitOrDismiss())
                {
                    return;
                }

                FilteringInProgress = true;
                FilteringProgress   = -1;
                var tokenSource     = new CancellationTokenSource();
                AssertNoOnGoingTask();
                currentActionToken        = tokenSource;
                IFilterData?newFilterData = await GetRelatedFilters(selectedPacket.Packet.BaseData.Number, tokenSource.Token);
                FilteringInProgress       = false;
                currentActionToken        = null;

                if (newFilterData == null)
                {
                    return;
                }

                if (!FilterData.IsEmpty)
                {
                    if (await messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                           .SetTitle("Find related packets")
                                                           .SetMainInstruction("Your current filter data is not empty")
                                                           .SetContent("Do you want to override current filter data?")
                                                           .WithButton("Override", true)
                                                           .WithButton("Merge", false)
                                                           .Build()))
                    {
                        FilterData = newFilterData;
                    }
                    else
                    {
                        FilterData.SetMinMax(Min(FilterData.MinPacketNumber, newFilterData.MinPacketNumber),
                                             Max(FilterData.MaxPacketNumber, newFilterData.MaxPacketNumber));
                        if (newFilterData.IncludedGuids != null)
                        {
                            foreach (var guid in newFilterData.IncludedGuids)
                            {
                                FilterData.IncludeGuid(guid);
                            }
                        }
                        if (newFilterData.ForceIncludePacketNumbers != null)
                        {
                            foreach (var packet in newFilterData.ForceIncludePacketNumbers)
                            {
                                FilterData.IncludePacketNumber(packet);
                            }
                        }
                    }
                }
                else
                {
                    FilterData = newFilterData;
                }

                await ApplyFilterCommand.ExecuteAsync();
            });

            ExcludeEntryCommand = new AsyncAutoCommand <PacketViewModel?>(async vm =>
            {
                if (vm == null || vm.Entry == 0)
                {
                    return;
                }

                FilterData.ExcludeEntry(vm.Entry);
                await ApplyFilterCommand.ExecuteAsync();
            }, vm => vm is PacketViewModel pvm && pvm.Entry != 0);

            IncludeEntryCommand = new AsyncAutoCommand <PacketViewModel?>(async vm =>
            {
                if (vm == null || vm.Entry == 0)
                {
                    return;
                }

                FilterData.IncludeEntry(vm.Entry);
                await ApplyFilterCommand.ExecuteAsync();
            }, vm => vm is PacketViewModel pvm && pvm.Entry != 0);

            ExcludeGuidCommand = new AsyncAutoCommand <PacketViewModel?>(async vm =>
            {
                if (vm == null || vm.MainActor == null)
                {
                    return;
                }

                FilterData.ExcludeGuid(vm.MainActor);
                await ApplyFilterCommand.ExecuteAsync();
            }, vm => vm is PacketViewModel pvm && pvm.MainActor != null);

            IncludeGuidCommand = new AsyncAutoCommand <PacketViewModel?>(async vm =>
            {
                if (vm == null || vm.MainActor == null)
                {
                    return;
                }

                FilterData.IncludeGuid(vm.MainActor);
                await ApplyFilterCommand.ExecuteAsync();
            }, vm => vm is PacketViewModel pvm && pvm.MainActor != null);

            ExcludeOpcodeCommand = new AsyncAutoCommand <PacketViewModel?>(async vm =>
            {
                if (vm == null)
                {
                    return;
                }

                FilterData.ExcludeOpcode(vm.Opcode);
                await ApplyFilterCommand.ExecuteAsync();
            }, vm => vm != null);

            IncludeOpcodeCommand = new AsyncAutoCommand <PacketViewModel?>(async vm =>
            {
                if (vm == null)
                {
                    return;
                }

                FilterData.IncludeOpcode(vm.Opcode);
                await ApplyFilterCommand.ExecuteAsync();
            }, vm => vm != null);

            AutoDispose(this.ToObservable(o => o.SelectedPacket).SubscribeAction(_ =>
            {
                ExcludeEntryCommand.RaiseCanExecuteChanged();
                IncludeEntryCommand.RaiseCanExecuteChanged();
                ExcludeGuidCommand.RaiseCanExecuteChanged();
                IncludeGuidCommand.RaiseCanExecuteChanged();
                ExcludeOpcodeCommand.RaiseCanExecuteChanged();
                IncludeOpcodeCommand.RaiseCanExecuteChanged();
            }));

            ExplainSelectedPacketCommand = new AsyncAutoCommand(async() =>
            {
                if (!reasonPanelVisibility || actionReactionProcessor == null)
                {
                    return;
                }
                if (selectedPacket == null)
                {
                    return;
                }

                DetectedActions.Clear();
                var detected = actionReactionProcessor.GetAllActions(selectedPacket.Id);
                if (detected != null)
                {
                    DetectedActions.AddRange(detected.Select(s => new DetectedActionViewModel(s)));
                }

                DetectedEvents.Clear();
                var events = actionReactionProcessor.GetAllEvents(selectedPacket.Id);
                if (events != null)
                {
                    DetectedEvents.AddRange(events.Select(s => new DetectedEventViewModel(s)));
                }

                var reasons = actionReactionProcessor.GetPossibleEventsForAction(selectedPacket.Packet.BaseData);
                Predictions.Clear();
                foreach (var reason in reasons)
                {
                    Predictions.Add(new ActionReasonPredictionViewModel(selectedPacket.Packet.BaseData, reason.rate.Value, reason.rate.Explain, reason.@event));
                }
                PossibleActions.Clear();
                var actions = actionReactionProcessor.GetPossibleActionsForEvent(selectedPacket.Packet.BaseData.Number);
                foreach (var action in actions)
                {
                    var actionHappened = actionReactionProcessor.GetAction(action.packetId);
                    if (actionHappened == null)
                    {
                        continue;
                    }
                    PossibleActions.Add(new PossibleActionViewModel(selectedPacket.Packet.BaseData, action.chance, "", actionHappened.Value));
                }
            }, () => ReasonPanelVisibility);

            On(() => ReasonPanelVisibility, isVisible =>
            {
                if (!isVisible)
                {
                    return;
                }
                ApplyFilterCommand.ExecuteAsync().ContinueWith(_ =>
                {
                    ExplainSelectedPacketCommand.Execute(null);
                }, TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted).ListenErrors();
            });

            AutoDispose(this.ToObservable(o => o.SelectedPacket)
                        .CombineLatest(this.ToObservable(o => o.ReasonPanelVisibility))
                        .SubscribeAction(
                            _ =>
            {
                ExplainSelectedPacketCommand.Execute(null);
            }));

            OpenFilterDialogCommand = new AsyncAutoCommand(async() =>
            {
                var newData = await filterDialogService.OpenFilterDialog(FilterData);
                if (newData != null)
                {
                    FilterData = newData;
                    await ApplyFilterCommand.ExecuteAsync();
                }
            });

            JumpToPacketCommand = new DelegateCommand <int?>(packetId =>
            {
                if (!packetId.HasValue)
                {
                    return;
                }
                var packet = filteredPackets.FirstOrDefault(p => p.Id == packetId.Value);
                if (packet != null)
                {
                    SelectedPacket = packet;
                }
            });

            GoToPacketCommand = new AsyncAutoCommand(async() =>
            {
                var min    = filteredPackets[0].Id;
                var max    = filteredPackets[^ 1].Id;
Example #2
0
        public PacketDocumentViewModel(PacketDocumentSolutionItem solutionItem,
                                       IMainThread mainThread,
                                       MostRecentlySearchedService mostRecentlySearchedService,
                                       IDatabaseProvider databaseProvider,
                                       Func <INativeTextDocument> nativeTextDocumentCreator,
                                       IMessageBoxService messageBoxService,
                                       IPacketFilteringService filteringService,
                                       IDocumentManager documentManager,
                                       ITextDocumentService textDocumentService,
                                       IWindowManager windowManager,
                                       IPacketViewerSettings packetSettings,
                                       IEnumerable <IPacketDumperProvider> dumperProviders,
                                       ISniffLoader sniffLoader)
        {
            this.solutionItem      = solutionItem;
            this.mainThread        = mainThread;
            this.messageBoxService = messageBoxService;
            this.filteringService  = filteringService;
            this.sniffLoader       = sniffLoader;
            packetViewModelCreator = new PacketViewModelFactory(databaseProvider);
            MostRecentlySearched   = mostRecentlySearchedService.MostRecentlySearched;
            Title                 = "Sniff " + Path.GetFileNameWithoutExtension(solutionItem.File);
            SolutionItem          = solutionItem;
            FilterText            = nativeTextDocumentCreator();
            SelectedPacketPreview = nativeTextDocumentCreator();
            Watch(this, t => t.FilteringProgress, nameof(ProgressUnknown));

            filteredPackets    = AllPackets;
            ApplyFilterCommand = new AsyncCommand(async() =>
            {
                if (inApplyFilterCommand)
                {
                    return;
                }

                inApplyFilterCommand     = true;
                MostRecentlySearchedItem = FilterText.ToString();
                inApplyFilterCommand     = false;

                if (!string.IsNullOrEmpty(FilterText.ToString()))
                {
                    mostRecentlySearchedService.Add(FilterText.ToString());
                }

                if (currentActionToken != filteringToken)
                {
                    throw new Exception("Invalid concurrent access!");
                }
                filteringToken?.Cancel();
                filteringToken     = null;
                currentActionToken = null;
                await FilterPackets(FilterText.ToString());
            });

            SaveToFileCommand = new AsyncCommand(async() =>
            {
                var path = await windowManager.ShowSaveFileDialog("Text file|txt");
                if (path == null)
                {
                    return;
                }

                LoadingInProgress   = true;
                FilteringInProgress = true;
                try
                {
                    await using StreamWriter writer = File.CreateText(path);
                    int i     = 0;
                    int count = FilteredPackets.Count;
                    foreach (var packet in FilteredPackets)
                    {
                        await writer.WriteLineAsync(packet.Text);
                        if ((i % 100) == 0)
                        {
                            Report(i * 1.0f / count);
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    await this.messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                            .SetTitle("Fatal error")
                                                            .SetMainInstruction("Fatal error during saving file")
                                                            .SetContent(e.Message)
                                                            .WithOkButton(false)
                                                            .Build());
                }
                LoadingInProgress   = false;
                FilteringInProgress = false;
            });

            OpenHelpCommand = new DelegateCommand(() => windowManager.OpenUrl("https://github.com/BAndysc/WoWDatabaseEditor/wiki/Packet-Viewer"));

            On(() => SelectedPacket, doc =>
            {
                if (doc != null)
                {
                    SelectedPacketPreview.FromString(doc.Text);
                }
            });

            On(() => SplitUpdate, _ =>
            {
                SplitPacketsIfNeededAsync().Wait();
                ((ICommand)ApplyFilterCommand).Execute(null);
            });

            foreach (var dumper in dumperProviders)
            {
                Processors.Add(new(dumper));
            }

            RunProcessors = new AsyncAutoCommand(async() =>
            {
                var processors = Processors.Where(s => s.IsChecked).ToList();
                if (processors.Count == 0)
                {
                    return;
                }

                LoadingInProgress   = true;
                FilteringInProgress = true;
                try
                {
                    var tokenSource = new CancellationTokenSource();
                    AssertNoOnGoingTask();
                    currentActionToken = tokenSource;

                    var output = await RunProcessorsThreaded(processors, tokenSource.Token).ConfigureAwait(true);

                    if (!tokenSource.IsCancellationRequested)
                    {
                        var extension = processors.Select(p => p.Extension).Distinct().Count() == 1
                            ? processors[0].Extension
                            : "txt";
                        documentManager.OpenDocument(textDocumentService.CreateDocument($"{processors[0].Name} ({Title})", output, extension));
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    await messageBoxService.ShowDialog(new MessageBoxFactory <bool>()
                                                       .SetIcon(MessageBoxIcon.Error)
                                                       .SetTitle("Fatal error")
                                                       .SetMainInstruction("Fatal error during processing")
                                                       .SetContent(
                                                           "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e)
                                                       .WithOkButton(true)
                                                       .Build());
                }
                LoadingInProgress   = false;
                FilteringInProgress = false;
                currentActionToken  = null;
            }, _ => Processors.Any(c => c.IsChecked));

            foreach (var proc in Processors)
            {
                AutoDispose(proc.ToObservable(p => p.IsChecked)
                            .SubscribeAction(_ => RunProcessors.RaiseCanExecuteChanged()));
            }

            wrapLines   = packetSettings.Settings.WrapLines;
            splitUpdate = packetSettings.Settings.AlwaysSplitUpdates;
            if (!string.IsNullOrEmpty(packetSettings.Settings.DefaultFilter))
            {
                FilterText.FromString(packetSettings.Settings.DefaultFilter);
            }
            LoadSniff().ListenErrors();

            AutoDispose(new ActionDisposable(() => currentActionToken?.Cancel()));
        }