some refactoring
This commit is contained in:
parent
89c3dcb424
commit
bbf905495c
8 changed files with 160 additions and 106 deletions
|
|
@ -5,4 +5,5 @@ public class InstallationData
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
public required string Title { get; set; }
|
public required string Title { get; set; }
|
||||||
public required InstallationParts Parts { get; set; }
|
public required InstallationParts Parts { get; set; }
|
||||||
|
public DateTime InstalledAt { get; set; } = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
@ -9,18 +9,4 @@ public class InstallationParts
|
||||||
public string? WindowsContentPath { get; init; }
|
public string? WindowsContentPath { get; init; }
|
||||||
public string? WindowsAdminPath { get; init; }
|
public string? WindowsAdminPath { get; init; }
|
||||||
public string? WindowsServerPath { 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)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -9,42 +9,46 @@ public class ZipScrapper
|
||||||
{
|
{
|
||||||
return new InstallationParts
|
return new InstallationParts
|
||||||
{
|
{
|
||||||
AndroidContentPath = DoesAndroidContentExists(archive),
|
AndroidContentPath = FindAndroidContent(archive),
|
||||||
OculusClientPath = DoesOculusClientExists(archive),
|
OculusClientPath = FindOculusClient(archive),
|
||||||
PicoClientPath = DoesPicoClientExists(archive),
|
PicoClientPath = FindPicoClient(archive),
|
||||||
AndroidAdminPath = DoesAndroidAdminExists(archive),
|
AndroidAdminPath = FindAndroidAdmin(archive),
|
||||||
WindowsAdminPath = DoesPcAdminExists(archive),
|
WindowsAdminPath = FindPcAdmin(archive),
|
||||||
WindowsContentPath = DoesWindowsContentExists(archive),
|
WindowsContentPath = FindWindowsContent(archive),
|
||||||
WindowsServerPath = DoesServerExists(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 entries = archive.Entries.Where(e => !string.IsNullOrEmpty(e.Name)).ToList();
|
||||||
var totalEntries = entries.Count;
|
var totalEntries = entries.Count;
|
||||||
var processedEntries = 0;
|
var processedEntries = 0;
|
||||||
|
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
{
|
{
|
||||||
var destinationPath = Path.Combine(outputPath, entry.FullName);
|
var destinationPath = Path.Combine(installationFolder, entry.FullName);
|
||||||
var destinationDir = Path.GetDirectoryName(destinationPath);
|
var destinationDir = Path.GetDirectoryName(destinationPath);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(destinationDir))
|
if (!string.IsNullOrEmpty(destinationDir))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(destinationDir);
|
Directory.CreateDirectory(destinationDir);
|
||||||
Console.WriteLine($"Extracting {entry.FullName} to {destinationPath}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.ExtractToFile(destinationPath, overwrite: true);
|
entry.ExtractToFile(destinationPath, overwrite: true);
|
||||||
|
|
@ -53,66 +57,104 @@ public class ZipScrapper
|
||||||
progress?.Report((double)processedEntries / totalEntries * 100);
|
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 =>
|
return new InstallationParts
|
||||||
entry.Name.Contains("MetaforcePico")
|
{
|
||||||
&& entry.Name.EndsWith(".apk"));
|
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;
|
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 =>
|
var entry = archive.Entries.FirstOrDefault(e =>
|
||||||
entry.Name.Contains("MetaforceOculus")
|
e.FullName.Contains(containsName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
&& entry.Name.EndsWith(".apk"));
|
e.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) &&
|
||||||
return entry?.FullName;
|
!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;
|
return entry?.FullName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using MetaforceInstaller.UI.Windows;
|
||||||
|
|
||||||
namespace MetaforceInstaller.UI;
|
namespace MetaforceInstaller.UI;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:CompileBindings="True"
|
x:CompileBindings="True"
|
||||||
x:DataType="vm:MainWindowViewModel"
|
x:DataType="vm:MainWindowViewModel"
|
||||||
x:Class="MetaforceInstaller.UI.MainWindow"
|
x:Class="MetaforceInstaller.UI.Windows.MainWindow"
|
||||||
Title="MetaforceInstaller">
|
Title="MetaforceInstaller">
|
||||||
|
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
|
|
@ -49,9 +49,13 @@
|
||||||
<ItemsControl ItemsSource="{Binding Installations}">
|
<ItemsControl ItemsSource="{Binding Installations}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Expander Header="{Binding Title}" Margin="20, 0" HorizontalAlignment="Stretch">
|
<Expander Header="{Binding Title}" Margin="16, 8" HorizontalAlignment="Stretch">
|
||||||
<StackPanel Margin="10">
|
<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>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
@ -4,7 +4,7 @@ using MetaforceInstaller.Core.Intefaces;
|
||||||
using MetaforceInstaller.Core.Services;
|
using MetaforceInstaller.Core.Services;
|
||||||
using MetaforceInstaller.UI.ViewModels;
|
using MetaforceInstaller.UI.ViewModels;
|
||||||
|
|
||||||
namespace MetaforceInstaller.UI;
|
namespace MetaforceInstaller.UI.Windows;
|
||||||
|
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
|
@ -16,9 +16,8 @@ public partial class MainWindow : Window
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_viewModel = new MainWindowViewModel();
|
_viewModel = new MainWindowViewModel();
|
||||||
DataContext = _viewModel;
|
|
||||||
|
|
||||||
_storageService = new StorageService();
|
_storageService = new StorageService();
|
||||||
|
DataContext = _viewModel;
|
||||||
|
|
||||||
NewInstallationButton.Click += OnNewInstalltionClick;
|
NewInstallationButton.Click += OnNewInstalltionClick;
|
||||||
|
|
||||||
|
|
@ -33,7 +32,7 @@ public partial class MainWindow : Window
|
||||||
|
|
||||||
public async void OnNewInstalltionClick(object? sender, RoutedEventArgs e)
|
public async void OnNewInstalltionClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var newInstallationDialog = new NewInstallationDialog();
|
var newInstallationDialog = new NewInstallationDialog(_storageService);
|
||||||
await newInstallationDialog.ShowDialog<NewInstallationDialog>(this);
|
await newInstallationDialog.ShowDialog<NewInstallationDialog>(this);
|
||||||
LoadInstallations();
|
LoadInstallations();
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="400"
|
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"
|
Title="MetaforceInstaller - Add new installation"
|
||||||
SizeToContent="WidthAndHeight"
|
SizeToContent="WidthAndHeight"
|
||||||
CanResize="False">
|
CanResize="False">
|
||||||
|
|
@ -8,19 +8,24 @@ using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using MetaforceInstaller.Core;
|
using MetaforceInstaller.Core;
|
||||||
|
using MetaforceInstaller.Core.Intefaces;
|
||||||
using MetaforceInstaller.Core.Models;
|
using MetaforceInstaller.Core.Models;
|
||||||
using MetaforceInstaller.Core.Services;
|
using MetaforceInstaller.Core.Services;
|
||||||
|
|
||||||
namespace MetaforceInstaller.UI;
|
namespace MetaforceInstaller.UI.Windows;
|
||||||
|
|
||||||
public partial class NewInstallationDialog : Window
|
public partial class NewInstallationDialog : Window
|
||||||
{
|
{
|
||||||
private string? _zipPath;
|
private string? _zipPath;
|
||||||
private InstallationParts? _installationParts;
|
private InstallationParts? _installationParts;
|
||||||
|
private readonly IStorageService _storageService;
|
||||||
|
|
||||||
public NewInstallationDialog()
|
public NewInstallationDialog(IStorageService storageService)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
_storageService = storageService;
|
||||||
|
|
||||||
RefreshCheckboxes();
|
RefreshCheckboxes();
|
||||||
CancelButton.Click += OnCancelClick;
|
CancelButton.Click += OnCancelClick;
|
||||||
ChooseZip.Click += OnChooseZipClick;
|
ChooseZip.Click += OnChooseZipClick;
|
||||||
|
|
@ -74,19 +79,35 @@ public partial class NewInstallationDialog : Window
|
||||||
{
|
{
|
||||||
using var archive = ZipFile.OpenRead(_zipPath);
|
using var archive = ZipFile.OpenRead(_zipPath);
|
||||||
var title = TitleTextBox.Text ?? Path.GetFileNameWithoutExtension(_zipPath);
|
var title = TitleTextBox.Text ?? Path.GetFileNameWithoutExtension(_zipPath);
|
||||||
|
var installationGuid = Guid.NewGuid();
|
||||||
|
|
||||||
var progress = new Progress<double>(value => { ProgressBar.Value = value; });
|
var progress = new Progress<double>(value => { ProgressBar.Value = value; });
|
||||||
|
|
||||||
|
string extractedPath = null;
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
ZipScrapper.ExtractZip(archive, Defaults.StoragePath, progress));
|
{
|
||||||
|
extractedPath = ZipScrapper.ExtractZip(
|
||||||
|
archive,
|
||||||
|
Defaults.StoragePath,
|
||||||
|
installationGuid,
|
||||||
|
progress);
|
||||||
|
});
|
||||||
|
|
||||||
InstallButton.IsEnabled = false;
|
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
|
var installationData = new InstallationData
|
||||||
{
|
{
|
||||||
|
Id = installationGuid,
|
||||||
Title = title,
|
Title = title,
|
||||||
Parts = _installationParts.ExtendPaths(Defaults.StoragePath + Path.DirectorySeparatorChar)
|
Parts = updatedParts
|
||||||
};
|
};
|
||||||
|
|
||||||
appData.Installations.Add(installationData);
|
appData.Installations.Add(installationData);
|
||||||
storageService.Save(appData);
|
_storageService.Save(appData);
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue