// // MainWindowViewModel.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reflection; using System.Text.Json; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Dock.Model.Controls; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NosSmooth.Comms.Local.Extensions; using NosSmooth.Core.Extensions; using NosSmooth.PacketSerializer.Abstractions.Attributes; using NosSmooth.PacketSerializer.Extensions; using NosSmooth.PacketSerializer.Packets; using NosSmooth.Pcap; using PacketLogger.Models; using PacketLogger.Models.Filters; using PacketLogger.Models.Packets; using PacketLogger.ViewModels.Log; using ReactiveUI; namespace PacketLogger.ViewModels; /// public class MainWindowViewModel : ViewModelBase { private readonly DockFactory _factory; private readonly NostaleProcesses _processes; /// /// Initializes a new instance of the class. /// public MainWindowViewModel() { var filterProfiles = new FilterProfiles(); if (Path.Exists("settings.json")) { using var file = File.OpenRead("settings.json"); var deserialized = JsonSerializer.Deserialize(file); if (deserialized is not null) { filterProfiles = deserialized; } } var services = new ServiceCollection() .AddLogging(b => b.ClearProviders().AddConsole()) .AddSingleton(_ => filterProfiles) .AddSingleton() .AddSingleton() .AddSingleton>(_ => Providers) .AddSingleton() .AddSingleton() .AddNostaleCore() .AddStatefulInjector() .AddStatefulEntity() .AddLocalComms() .AddPacketResponder(typeof(PacketResponder)) .BuildServiceProvider(); _processes = services.GetRequiredService(); _factory = services.GetRequiredService(); Layout = _factory.CreateLayout(); if (Layout is { }) { _factory.InitLayout(Layout); if (Layout is { } root) { root.Navigate.Execute("Home"); } } _factory.DocumentLoaded += doc => { if (doc.Provider is not null) { RxApp.MainThreadScheduler.Schedule(() => Providers.Add(doc.Provider)); } }; _factory.DocumentClosed += doc => { if (doc.Provider is not null) { RxApp.MainThreadScheduler.Schedule(() => Providers.Remove(doc.Provider)); } }; SaveAll = ReactiveCommand.CreateFromTask ( async () => { if (Layout?.FocusedDockable is DocumentViewModel activeDocument && activeDocument.Loaded && activeDocument.NestedViewModel is not null) { var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime) ?.MainWindow; var result = await new SaveFileDialog() { DefaultExtension = ".log", InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }.ShowAsync(mainWindow!); if (result is null) { return; } if (activeDocument.Provider is null) { return; } using var file = File.OpenWrite(result); using var streamWriter = new StreamWriter(file); foreach (var packet in activeDocument.Provider.Packets.Items) { await streamWriter.WriteLineAsync ( $"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}" ); } } } ); SaveFiltered = ReactiveCommand.CreateFromTask ( async () => { if (Layout?.FocusedDockable is DocumentViewModel activeDocument && activeDocument.Loaded && activeDocument.NestedViewModel is not null) { var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime) ?.MainWindow; var result = await new SaveFileDialog() { DefaultExtension = ".log", }.ShowAsync(mainWindow!); if (result is null) { return; } if (activeDocument.NestedViewModel is not PacketLogViewModel packetLogVM) { return; } using var file = File.OpenWrite(result); using var streamWriter = new StreamWriter(file); foreach (var packet in packetLogVM.FilteredPackets) { await streamWriter.WriteLineAsync ( $"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}" ); } } } ); SaveSettings = ReactiveCommand.CreateFromTask ( async () => { using var file = File.Open("settings.json", FileMode.Create); await JsonSerializer.SerializeAsync(file, filterProfiles); } ); OpenFile = ReactiveCommand.Create (() => _factory.CreateLoadedDocument(doc => doc.OpenFile.Execute(Unit.Default))); OpenEmpty = ReactiveCommand.Create (() => _factory.CreateLoadedDocument(doc => doc.OpenDummy.Execute(Unit.Default))); OpenSettings = ReactiveCommand.Create (() => _factory.CreateLoadedDocument(doc => doc.OpenSettings.Execute(Unit.Default))); Connect = ReactiveCommand.Create (process => _factory.CreateLoadedDocument(doc => doc.OpenProcess.Execute((NostaleProcess)process[0]!))); OpenSender = ReactiveCommand.Create (provider => _factory.CreateLoadedDocument(doc => doc.OpenSender.Execute((IPacketProvider)provider[0]!))); NewTab = ReactiveCommand.Create (() => _factory.DocumentDock.CreateDocument?.Execute(null)); QuitApplication = ReactiveCommand.Create (() => (Application.Current?.ApplicationLifetime as IControlledApplicationLifetime)?.Shutdown()); } /// /// Gets the nostale processes. /// public ObservableCollection Processes => _processes.Processes; /// /// Gets the packet provider. /// public ObservableCollection Providers { get; } = new ObservableCollection(); /// /// Gets or sets the layout. /// public IRootDock? Layout { get; set; } /// /// Gets a command that quits the application. /// public ReactiveCommand QuitApplication { get; } /// /// Gets a command that saves all packets. /// public ReactiveCommand SaveAll { get; } /// /// Gets a command that saves filtered packets. /// public ReactiveCommand SaveFiltered { get; } /// /// Gets the comamnd that opens a file. /// public ReactiveCommand OpenFile { get; } /// /// Gets the command that opens empty logger. /// public ReactiveCommand OpenEmpty { get; } /// /// Gets the command that opens empty logger. /// public ReactiveCommand OpenSender { get; } /// /// Gets the command that opens empty logger. /// public ReactiveCommand Connect { get; } /// /// Gets the command that opens a new tab. /// public ReactiveCommand NewTab { get; } /// /// Gets the command used for opening settings. /// public ReactiveCommand OpenSettings { get; } /// /// Gets the command used for saving settings. /// public ReactiveCommand SaveSettings { get; } }