From c29658e7e4f5c3f9d7c96820de152ca40223cf64 Mon Sep 17 00:00:00 2001 From: shept Date: Thu, 1 Jan 2026 05:10:44 +0500 Subject: [PATCH 1/3] add storage functionality and appdata model --- .../Intefaces/IStorageService.cs | 9 +++++ MetaforceInstaller.Core/Models/AppData.cs | 6 ++++ .../Services/StorageService.cs | 36 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 MetaforceInstaller.Core/Intefaces/IStorageService.cs create mode 100644 MetaforceInstaller.Core/Models/AppData.cs create mode 100644 MetaforceInstaller.Core/Services/StorageService.cs diff --git a/MetaforceInstaller.Core/Intefaces/IStorageService.cs b/MetaforceInstaller.Core/Intefaces/IStorageService.cs new file mode 100644 index 0000000..5967309 --- /dev/null +++ b/MetaforceInstaller.Core/Intefaces/IStorageService.cs @@ -0,0 +1,9 @@ +using MetaforceInstaller.Core.Models; + +namespace MetaforceInstaller.Core.Intefaces; + +public interface IStorageService +{ + AppData Load(); + void Save(AppData data); +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/Models/AppData.cs b/MetaforceInstaller.Core/Models/AppData.cs new file mode 100644 index 0000000..5c37871 --- /dev/null +++ b/MetaforceInstaller.Core/Models/AppData.cs @@ -0,0 +1,6 @@ +namespace MetaforceInstaller.Core.Models; + +public class AppData +{ + public List Installations { get; set; } = new(); +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/Services/StorageService.cs b/MetaforceInstaller.Core/Services/StorageService.cs new file mode 100644 index 0000000..7fa1601 --- /dev/null +++ b/MetaforceInstaller.Core/Services/StorageService.cs @@ -0,0 +1,36 @@ +using System.Text.Json; +using MetaforceInstaller.Core.Intefaces; +using MetaforceInstaller.Core.Models; + +namespace MetaforceInstaller.Core.Services; + +public class StorageService : IStorageService +{ + private readonly string _storagePath; + + public StorageService() + { + var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + var appDirectory = Path.Combine(appData, "MetaforceInstaller"); + Directory.CreateDirectory(appDirectory); + _storagePath = Path.Combine(appDirectory, "installations.json"); + } + + public AppData Load() + { + if (!File.Exists(_storagePath)) + return new AppData(); + + var json = File.ReadAllText(_storagePath); + return JsonSerializer.Deserialize(json) ?? new AppData(); + } + + public void Save(AppData data) + { + var json = JsonSerializer.Serialize(data, new JsonSerializerOptions + { + WriteIndented = true + }); + File.WriteAllText(_storagePath, json); + } +} \ No newline at end of file From 3dc9c8c27997d68093c110484ac44436ab143a48 Mon Sep 17 00:00:00 2001 From: shept Date: Thu, 1 Jan 2026 05:11:05 +0500 Subject: [PATCH 2/3] split installation info to parts --- .../Models/InstallationData.cs | 10 +++ .../Models/InstallationParts.cs | 12 +++ .../Services/ZipScrapper.cs | 89 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 MetaforceInstaller.Core/Models/InstallationData.cs create mode 100644 MetaforceInstaller.Core/Models/InstallationParts.cs create mode 100644 MetaforceInstaller.Core/Services/ZipScrapper.cs diff --git a/MetaforceInstaller.Core/Models/InstallationData.cs b/MetaforceInstaller.Core/Models/InstallationData.cs new file mode 100644 index 0000000..855fae7 --- /dev/null +++ b/MetaforceInstaller.Core/Models/InstallationData.cs @@ -0,0 +1,10 @@ +namespace MetaforceInstaller.Core.Models; + +public class InstallationData +{ + public Guid Id { get; set; } = Guid.NewGuid(); + public string Title { get; set; } + public string AndroidPackagePath { get; set; } + public string WindowsServerPackagePath { get; set; } + public string WindowsAdminPackagePath { get; set; } +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/Models/InstallationParts.cs b/MetaforceInstaller.Core/Models/InstallationParts.cs new file mode 100644 index 0000000..cb531ab --- /dev/null +++ b/MetaforceInstaller.Core/Models/InstallationParts.cs @@ -0,0 +1,12 @@ +namespace MetaforceInstaller.Core.Models; + +public record InstallationParts +{ + public bool OculusClientExists { get; init; } + public bool PicoClientExists { get; init; } + public bool AndroidAdminExists { get; init; } + public bool AndroidContentExists { get; init; } + public bool WindowsContentExists { get; init; } + public bool WindowsAdminExists { get; init; } + public bool WindowsServerExists { get; init; } +} \ No newline at end of file diff --git a/MetaforceInstaller.Core/Services/ZipScrapper.cs b/MetaforceInstaller.Core/Services/ZipScrapper.cs new file mode 100644 index 0000000..b5d1a32 --- /dev/null +++ b/MetaforceInstaller.Core/Services/ZipScrapper.cs @@ -0,0 +1,89 @@ +using System.IO.Compression; +using MetaforceInstaller.Core.Models; + +namespace MetaforceInstaller.Core.Services; + +public class ZipScrapper +{ + public static InstallationParts PeekFiles(ZipArchive archive) + { + return new InstallationParts + { + AndroidContentExists = DoesAndroidContentExists(archive), + OculusClientExists = DoesOculusClientExists(archive), + PicoClientExists = DoesPicoClientExists(archive), + AndroidAdminExists = DoesAndroidAdminExists(archive), + WindowsAdminExists = DoesPcAdminExists(archive), + WindowsContentExists = DoesWindowsContentExists(archive), + WindowsServerExists = DoesServerExists(archive), + }; + // Console.WriteLine($"Contents of {archive}:"); + // Console.WriteLine("----------------------------------"); + // + // foreach (ZipArchiveEntry entry in archive.Entries) + // { + // // You can access properties like entry.Name, entry.FullName, entry.Length (uncompressed size), entry.CompressedLength + // Console.WriteLine($"Entry: {entry.FullName}"); + // Console.WriteLine($" Uncompressed Size: {entry.Length} bytes"); + // Console.WriteLine($" Compressed Size: {entry.CompressedLength} bytes"); + // Console.WriteLine("----------------------------------"); + // } + } + + public static string ExtractZip(ZipArchive archive, string outputPath) + { + archive.ExtractToDirectory(outputPath); + return outputPath; + } + + private static bool DoesPicoClientExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.Name.Contains("MetaforcePico") + && entry.Name.EndsWith(".apk")); + } + + private static bool DoesOculusClientExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.Name.Contains("MetaforceOculus") + && entry.Name.EndsWith(".apk")); + } + + private static bool DoesAndroidAdminExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.Name.Contains("MetaforceAdmin") + && entry.Name.EndsWith(".apk")); + } + + private static bool DoesAndroidContentExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.Name.Contains("Content_Android") + && entry.Name.EndsWith(".zip")); + } + + private static bool DoesWindowsContentExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.Name.Contains("Content_StandaloneWindows") + && entry.Name.EndsWith(".zip")); + } + + private static bool DoesPcAdminExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.FullName.Contains("MetaforceAdminPC") + && entry.Name.EndsWith(".exe") && !entry.Name.Contains("UnityCrashHandler") && + !entry.Name.Contains("crashpad_handler")); + } + + private static bool DoesServerExists(ZipArchive archive) + { + return archive.Entries + .Any(entry => entry.FullName.Contains("MetaforceServer") + && entry.Name.EndsWith(".exe") && !entry.Name.Contains("UnityCrashHandler") && + !entry.Name.Contains("crashpad_handler")); + } +} \ No newline at end of file From 13a076ad791957cd1bf8ce15330425dda2c0ca15 Mon Sep 17 00:00:00 2001 From: shept Date: Thu, 1 Jan 2026 05:11:23 +0500 Subject: [PATCH 3/3] brbrbr patapim --- MetaforceInstaller.UI/MainWindow.axaml | 75 ++-- MetaforceInstaller.UI/MainWindow.axaml.cs | 398 +++++++++--------- .../NewInstallationDialog.axaml | 24 ++ .../NewInstallationDialog.axaml.cs | 118 ++++++ MetaforceInstaller.UI/TextDefaults.cs | 9 + 5 files changed, 388 insertions(+), 236 deletions(-) create mode 100644 MetaforceInstaller.UI/NewInstallationDialog.axaml create mode 100644 MetaforceInstaller.UI/NewInstallationDialog.axaml.cs create mode 100644 MetaforceInstaller.UI/TextDefaults.cs diff --git a/MetaforceInstaller.UI/MainWindow.axaml b/MetaforceInstaller.UI/MainWindow.axaml index 61a426d..7b8c85a 100644 --- a/MetaforceInstaller.UI/MainWindow.axaml +++ b/MetaforceInstaller.UI/MainWindow.axaml @@ -10,11 +10,11 @@ - + - + - + @@ -22,49 +22,44 @@ + - - - - - - - + + - - - - + Install server + Install admin + Save android admin + Save VR client + + + + + + + + \ No newline at end of file diff --git a/MetaforceInstaller.UI/NewInstallationDialog.axaml.cs b/MetaforceInstaller.UI/NewInstallationDialog.axaml.cs new file mode 100644 index 0000000..85d5f28 --- /dev/null +++ b/MetaforceInstaller.UI/NewInstallationDialog.axaml.cs @@ -0,0 +1,118 @@ +using System.IO.Compression; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; +using MetaforceInstaller.Core.Models; +using MetaforceInstaller.Core.Services; + +namespace MetaforceInstaller.UI; + +public partial class NewInstallationDialog : Window +{ + private string? _zipPath; + private InstallationParts? _installationParts; + + public NewInstallationDialog() + { + InitializeComponent(); + RefreshCheckboxes(); + CancelButton.Click += OnCancelClick; + ChooseZip.Click += OnChooseZipClick; + InstallButton.IsEnabled = false; + InstallButton.Click += OnInstallClick; + } + + private async void RefreshCheckboxes() + { + var serverCheckbox = ServerCheckBox; + var pcAdminCheckbox = PcAdminCheckBox; + var androidAdminCheckbox = AndroidAdminCheckbox; + var vrClientCheckbox = VrClientCheckbox; + serverCheckbox.Content = TextDefaults.InstallServer; + serverCheckbox.IsEnabled = true; + pcAdminCheckbox.Content = TextDefaults.InstallAdmin; + pcAdminCheckbox.IsEnabled = true; + androidAdminCheckbox.Content = TextDefaults.SaveAndroidAdmin; + androidAdminCheckbox.IsEnabled = true; + vrClientCheckbox.Content = TextDefaults.SaveVRClient; + vrClientCheckbox.IsEnabled = true; + } + + private async void OnChooseZipClick(object? sender, RoutedEventArgs e) + { + var topLevel = GetTopLevel(this); + var files = await topLevel!.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + { + Title = "Выберите архив с контентом", + AllowMultiple = false, + FileTypeFilter = + [ + new FilePickerFileType("ZIP Files") + { + Patterns = ["*.zip"] + } + ] + }); + + if (files.Count >= 1) + { + _zipPath = files[0].Path.LocalPath; + using var archive = ZipFile.OpenRead(_zipPath); + _installationParts = ZipScrapper.PeekFiles(archive); + UpdateCheckboxes(); + archive.Dispose(); + } + } + + private async void OnInstallClick(object? sender, RoutedEventArgs e) + { + using var archive = ZipFile.OpenRead(_zipPath); + + } + + private void UpdateCheckboxes() + { + RefreshCheckboxes(); + var serverCheckbox = ServerCheckBox; + var pcAdminCheckbox = PcAdminCheckBox; + var androidAdminCheckbox = AndroidAdminCheckbox; + var vrClientCheckbox = VrClientCheckbox; + + if (!_installationParts.WindowsServerExists) + { + serverCheckbox.IsEnabled = false; + serverCheckbox.Content += "\nCouldn't find directory with server"; + } + + if (!_installationParts.WindowsAdminExists) + { + pcAdminCheckbox.IsEnabled = false; + pcAdminCheckbox.Content += "\nCouldn't find directory with PC admin"; + } + + if (!_installationParts.WindowsContentExists) + { + pcAdminCheckbox.IsEnabled = false; + pcAdminCheckbox.Content += "\nCouldn't find windows content"; + } + + if (!_installationParts.AndroidContentExists) + { + vrClientCheckbox.IsEnabled = false; + vrClientCheckbox.Content += "\nCouldn't find android content"; + androidAdminCheckbox.IsEnabled = false; + androidAdminCheckbox.Content += "\nCouldn't find android content"; + } + + if (!_installationParts.PicoClientExists && !_installationParts.OculusClientExists) + { + vrClientCheckbox.IsEnabled = false; + vrClientCheckbox.Content += "\nCouldn't find any VR clients"; + } + } + + private void OnCancelClick(object? sender, RoutedEventArgs e) + { + Close(); + } +} \ No newline at end of file diff --git a/MetaforceInstaller.UI/TextDefaults.cs b/MetaforceInstaller.UI/TextDefaults.cs new file mode 100644 index 0000000..176e1f6 --- /dev/null +++ b/MetaforceInstaller.UI/TextDefaults.cs @@ -0,0 +1,9 @@ +namespace MetaforceInstaller.UI; + +public static class TextDefaults +{ + public static readonly string InstallServer = "Install Server"; + public static readonly string InstallAdmin = "Install Admin"; + public static readonly string SaveAndroidAdmin = "Save Android admin"; + public static readonly string SaveVRClient = "Save VR client"; +} \ No newline at end of file