some refactoring

This commit is contained in:
Вячеслав 2026-01-01 22:58:33 +05:00
parent 89c3dcb424
commit bbf905495c
8 changed files with 160 additions and 106 deletions

View file

@ -5,4 +5,5 @@ public class InstallationData
public Guid Id { get; set; } = Guid.NewGuid();
public required string Title { get; set; }
public required InstallationParts Parts { get; set; }
public DateTime InstalledAt { get; set; } = DateTime.Now;
}

View file

@ -9,18 +9,4 @@ public class InstallationParts
public string? WindowsContentPath { get; init; }
public string? WindowsAdminPath { get; init; }
public string? WindowsServerPath { get; init; }
public InstallationParts ExtendPaths(string prefixPath)
{
return new InstallationParts
{
OculusClientPath = OculusClientPath is null ? null : Path.Combine(prefixPath, OculusClientPath),
PicoClientPath = PicoClientPath is null ? null : Path.Combine(prefixPath, PicoClientPath),
AndroidAdminPath = AndroidAdminPath is null ? null : Path.Combine(prefixPath, AndroidAdminPath),
AndroidContentPath = AndroidContentPath is null ? null : Path.Combine(prefixPath, AndroidContentPath),
WindowsContentPath = WindowsContentPath is null ? null : Path.Combine(prefixPath, WindowsContentPath),
WindowsAdminPath = WindowsAdminPath is null ? null : Path.Combine(prefixPath, WindowsAdminPath),
WindowsServerPath = WindowsServerPath is null ? null : Path.Combine(prefixPath, WindowsServerPath)
};
}
}

View file

@ -9,42 +9,46 @@ public class ZipScrapper
{
return new InstallationParts
{
AndroidContentPath = DoesAndroidContentExists(archive),
OculusClientPath = DoesOculusClientExists(archive),
PicoClientPath = DoesPicoClientExists(archive),
AndroidAdminPath = DoesAndroidAdminExists(archive),
WindowsAdminPath = DoesPcAdminExists(archive),
WindowsContentPath = DoesWindowsContentExists(archive),
WindowsServerPath = DoesServerExists(archive),
AndroidContentPath = FindAndroidContent(archive),
OculusClientPath = FindOculusClient(archive),
PicoClientPath = FindPicoClient(archive),
AndroidAdminPath = FindAndroidAdmin(archive),
WindowsAdminPath = FindPcAdmin(archive),
WindowsContentPath = FindWindowsContent(archive),
WindowsServerPath = FindServer(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, IProgress<double>? progress = null)
/// <summary>
/// Extracts ZIP archive to a unique folder based on installation GUID
/// </summary>
/// <param name="archive">ZIP archive to extract</param>
/// <param name="baseOutputPath">Base storage path</param>
/// <param name="installationGuid">Unique GUID for this installation</param>
/// <param name="progress">Progress reporter</param>
/// <returns>Full path to the extracted folder</returns>
public static string ExtractZip(
ZipArchive archive,
string baseOutputPath,
Guid installationGuid,
IProgress<double>? progress = null)
{
// Create unique folder for this installation
var installationFolder = Path.Combine(baseOutputPath, installationGuid.ToString());
Directory.CreateDirectory(installationFolder);
var entries = archive.Entries.Where(e => !string.IsNullOrEmpty(e.Name)).ToList();
var totalEntries = entries.Count;
var processedEntries = 0;
foreach (var entry in entries)
{
var destinationPath = Path.Combine(outputPath, entry.FullName);
var destinationPath = Path.Combine(installationFolder, entry.FullName);
var destinationDir = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(destinationDir))
{
Directory.CreateDirectory(destinationDir);
Console.WriteLine($"Extracting {entry.FullName} to {destinationPath}");
}
entry.ExtractToFile(destinationPath, overwrite: true);
@ -53,66 +57,104 @@ public class ZipScrapper
progress?.Report((double)processedEntries / totalEntries * 100);
}
return outputPath;
return installationFolder;
}
private static string? DoesPicoClientExists(ZipArchive archive)
/// <summary>
/// Updates InstallationParts paths to reflect the actual extracted location
/// </summary>
public static InstallationParts UpdatePathsAfterExtraction(
InstallationParts parts,
string extractedFolderPath)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.Name.Contains("MetaforcePico")
&& entry.Name.EndsWith(".apk"));
return new InstallationParts
{
AndroidContentPath = UpdatePath(parts.AndroidContentPath, extractedFolderPath),
OculusClientPath = UpdatePath(parts.OculusClientPath, extractedFolderPath),
PicoClientPath = UpdatePath(parts.PicoClientPath, extractedFolderPath),
AndroidAdminPath = UpdatePath(parts.AndroidAdminPath, extractedFolderPath),
WindowsAdminPath = UpdatePath(parts.WindowsAdminPath, extractedFolderPath),
WindowsContentPath = UpdatePath(parts.WindowsContentPath, extractedFolderPath),
WindowsServerPath = UpdatePath(parts.WindowsServerPath, extractedFolderPath),
};
}
private static string? UpdatePath(string? relativePath, string basePath)
{
if (string.IsNullOrEmpty(relativePath))
return null;
return Path.Combine(basePath, relativePath);
}
private static string? FindPicoClient(ZipArchive archive)
{
return FindEntry(archive,
name: "MetaforcePico",
extension: ".apk");
}
private static string? FindOculusClient(ZipArchive archive)
{
return FindEntry(archive,
name: "MetaforceOculus",
extension: ".apk");
}
private static string? FindAndroidAdmin(ZipArchive archive)
{
return FindEntry(archive,
name: "MetaforceAdmin",
extension: ".apk");
}
private static string? FindAndroidContent(ZipArchive archive)
{
return FindEntry(archive,
name: "Content_Android",
extension: ".zip");
}
private static string? FindWindowsContent(ZipArchive archive)
{
return FindEntry(archive,
name: "Content_StandaloneWindows",
extension: ".zip");
}
private static string? FindPcAdmin(ZipArchive archive)
{
return FindExecutable(archive, "MetaforceAdminPC");
}
private static string? FindServer(ZipArchive archive)
{
return FindExecutable(archive, "MetaforceServer");
}
/// <summary>
/// Finds an entry in archive by name and extension
/// </summary>
private static string? FindEntry(ZipArchive archive, string name, string extension)
{
var entry = archive.Entries.FirstOrDefault(e =>
e.Name.Contains(name, StringComparison.OrdinalIgnoreCase) &&
e.Name.EndsWith(extension, StringComparison.OrdinalIgnoreCase));
return entry?.FullName;
}
private static string? DoesOculusClientExists(ZipArchive archive)
/// <summary>
/// Finds an executable in archive, excluding crash handlers
/// </summary>
private static string? FindExecutable(ZipArchive archive, string containsName)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.Name.Contains("MetaforceOculus")
&& entry.Name.EndsWith(".apk"));
return entry?.FullName;
}
var entry = archive.Entries.FirstOrDefault(e =>
e.FullName.Contains(containsName, StringComparison.OrdinalIgnoreCase) &&
e.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) &&
!e.Name.Contains("UnityCrashHandler", StringComparison.OrdinalIgnoreCase) &&
!e.Name.Contains("crashpad_handler", StringComparison.OrdinalIgnoreCase));
private static string? DoesAndroidAdminExists(ZipArchive archive)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.Name.Contains("MetaforceAdmin")
&& entry.Name.EndsWith(".apk"));
return entry?.FullName;
}
private static string? DoesAndroidContentExists(ZipArchive archive)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.Name.Contains("Content_Android")
&& entry.Name.EndsWith(".zip"));
return entry?.FullName;
}
private static string? DoesWindowsContentExists(ZipArchive archive)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.Name.Contains("Content_StandaloneWindows")
&& entry.Name.EndsWith(".zip"));
return entry?.FullName;
}
private static string? DoesPcAdminExists(ZipArchive archive)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.FullName.Contains("MetaforceAdminPC") &&
entry.Name.EndsWith(".exe")
&& !entry.Name.Contains("UnityCrashHandler") &&
!entry.Name.Contains("crashpad_handler"));
return entry?.FullName;
}
private static string? DoesServerExists(ZipArchive archive)
{
var entry = archive.Entries.FirstOrDefault(entry =>
entry.FullName.Contains("MetaforceServer") &&
entry.Name.EndsWith(".exe")
&& !entry.Name.Contains("UnityCrashHandler") &&
!entry.Name.Contains("crashpad_handler"));
return entry?.FullName;
}
}

View file

@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using MetaforceInstaller.UI.Windows;
namespace MetaforceInstaller.UI;

View file

@ -6,7 +6,7 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:CompileBindings="True"
x:DataType="vm:MainWindowViewModel"
x:Class="MetaforceInstaller.UI.MainWindow"
x:Class="MetaforceInstaller.UI.Windows.MainWindow"
Title="MetaforceInstaller">
<Window.Resources>
@ -24,7 +24,7 @@
</Window.Resources>
<Design.DataContext>
<vm:MainWindowViewModel/>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid>
@ -49,9 +49,13 @@
<ItemsControl ItemsSource="{Binding Installations}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Title}" Margin="20, 0" HorizontalAlignment="Stretch">
<StackPanel Margin="10">
<Expander Header="{Binding Title}" Margin="16, 8" HorizontalAlignment="Stretch">
<StackPanel Orientation="Horizontal">
<Button Name="LaunchServerButton">Launch server</Button>
<Button Name="LaunchPcAdminButton">Launch PC admin</Button>
<Button Name="InstallVrClientButton">Install VR client</Button>
<Button Name="InstallAndroidAdminButton">Install Android admin</Button>
<Button Name="DeleteButton">Delete</Button>
</StackPanel>
</Expander>
</DataTemplate>

View file

@ -4,7 +4,7 @@ using MetaforceInstaller.Core.Intefaces;
using MetaforceInstaller.Core.Services;
using MetaforceInstaller.UI.ViewModels;
namespace MetaforceInstaller.UI;
namespace MetaforceInstaller.UI.Windows;
public partial class MainWindow : Window
{
@ -16,9 +16,8 @@ public partial class MainWindow : Window
InitializeComponent();
_viewModel = new MainWindowViewModel();
DataContext = _viewModel;
_storageService = new StorageService();
DataContext = _viewModel;
NewInstallationButton.Click += OnNewInstalltionClick;
@ -33,7 +32,7 @@ public partial class MainWindow : Window
public async void OnNewInstalltionClick(object? sender, RoutedEventArgs e)
{
var newInstallationDialog = new NewInstallationDialog();
var newInstallationDialog = new NewInstallationDialog(_storageService);
await newInstallationDialog.ShowDialog<NewInstallationDialog>(this);
LoadInstallations();
}

View file

@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="400"
x:Class="MetaforceInstaller.UI.NewInstallationDialog"
x:Class="MetaforceInstaller.UI.Windows.NewInstallationDialog"
Title="MetaforceInstaller - Add new installation"
SizeToContent="WidthAndHeight"
CanResize="False">

View file

@ -8,19 +8,24 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using MetaforceInstaller.Core;
using MetaforceInstaller.Core.Intefaces;
using MetaforceInstaller.Core.Models;
using MetaforceInstaller.Core.Services;
namespace MetaforceInstaller.UI;
namespace MetaforceInstaller.UI.Windows;
public partial class NewInstallationDialog : Window
{
private string? _zipPath;
private InstallationParts? _installationParts;
private readonly IStorageService _storageService;
public NewInstallationDialog()
public NewInstallationDialog(IStorageService storageService)
{
InitializeComponent();
_storageService = storageService;
RefreshCheckboxes();
CancelButton.Click += OnCancelClick;
ChooseZip.Click += OnChooseZipClick;
@ -74,19 +79,35 @@ public partial class NewInstallationDialog : Window
{
using var archive = ZipFile.OpenRead(_zipPath);
var title = TitleTextBox.Text ?? Path.GetFileNameWithoutExtension(_zipPath);
var installationGuid = Guid.NewGuid();
var progress = new Progress<double>(value => { ProgressBar.Value = value; });
string extractedPath = null;
await Task.Run(() =>
ZipScrapper.ExtractZip(archive, Defaults.StoragePath, progress));
{
extractedPath = ZipScrapper.ExtractZip(
archive,
Defaults.StoragePath,
installationGuid,
progress);
});
InstallButton.IsEnabled = false;
var storageService = new StorageService();
var appData = storageService.Load();
var appData = _storageService.Load();
var updatedParts = ZipScrapper.UpdatePathsAfterExtraction(_installationParts, extractedPath);
var installationData = new InstallationData
{
Id = installationGuid,
Title = title,
Parts = _installationParts.ExtendPaths(Defaults.StoragePath + Path.DirectorySeparatorChar)
Parts = updatedParts
};
appData.Installations.Add(installationData);
storageService.Save(appData);
_storageService.Save(appData);
Close();
}