MetaforceInstaller/MetaforceInstaller.Core/Services/ZipScrapper.cs
2026-01-01 22:58:33 +05:00

160 lines
No EOL
5.5 KiB
C#

using System.IO.Compression;
using MetaforceInstaller.Core.Models;
namespace MetaforceInstaller.Core.Services;
public class ZipScrapper
{
public static InstallationParts PeekFiles(ZipArchive archive)
{
return new InstallationParts
{
AndroidContentPath = FindAndroidContent(archive),
OculusClientPath = FindOculusClient(archive),
PicoClientPath = FindPicoClient(archive),
AndroidAdminPath = FindAndroidAdmin(archive),
WindowsAdminPath = FindPcAdmin(archive),
WindowsContentPath = FindWindowsContent(archive),
WindowsServerPath = FindServer(archive),
};
}
/// <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(installationFolder, entry.FullName);
var destinationDir = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(destinationDir))
{
Directory.CreateDirectory(destinationDir);
}
entry.ExtractToFile(destinationPath, overwrite: true);
processedEntries++;
progress?.Report((double)processedEntries / totalEntries * 100);
}
return installationFolder;
}
/// <summary>
/// Updates InstallationParts paths to reflect the actual extracted location
/// </summary>
public static InstallationParts UpdatePathsAfterExtraction(
InstallationParts parts,
string extractedFolderPath)
{
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;
}
/// <summary>
/// Finds an executable in archive, excluding crash handlers
/// </summary>
private static string? FindExecutable(ZipArchive archive, string containsName)
{
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));
return entry?.FullName;
}
}