- Channel radio (Release / Master (Instabil)); master installs the rolling 'master' release from OmniNX/OmniNX-Master. Preselected from release_channel in the card's manifest.ini. - Variant, RAM (8gb) and Hekate-Pro choice are prefilled from the card (manifest.ini / config.ini) like the payload does, instead of silently switching users back to 4 GB / Standard. - Clear read-only attributes before deleting folders. - Remove leftover "OmniNX <Variant>" pack folders after installing. - Create empty folders from the pack. - Save every run's log to %LocalAppData%\OmniNX-Desktop-Installer\logs, plus a "Save log" button.
151 lines
4.6 KiB
C#
151 lines
4.6 KiB
C#
using OmniNX.DesktopInstaller.Models;
|
|
|
|
namespace OmniNX.DesktopInstaller.Services;
|
|
|
|
public static class VariantHelper
|
|
{
|
|
public static string GetDisplayName(OmniNxVariant variant) => variant switch
|
|
{
|
|
OmniNxVariant.Light => "Light",
|
|
OmniNxVariant.Standard => "Standard",
|
|
OmniNxVariant.Oc => "OC",
|
|
_ => "None",
|
|
};
|
|
|
|
public static string GetStagingFolderName(OmniNxVariant variant) => variant switch
|
|
{
|
|
OmniNxVariant.Light => "OmniNX Light",
|
|
OmniNxVariant.Standard => "OmniNX Standard",
|
|
OmniNxVariant.Oc => "OmniNX OC",
|
|
_ => string.Empty,
|
|
};
|
|
|
|
public static string GetZipAssetPrefix(OmniNxVariant variant) => variant switch
|
|
{
|
|
OmniNxVariant.Light => "OmniNX-Light",
|
|
OmniNxVariant.Standard => "OmniNX-Standard",
|
|
OmniNxVariant.Oc => "OmniNX-OC",
|
|
_ => string.Empty,
|
|
};
|
|
|
|
public static OmniNxVariant ParsePackName(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
return OmniNxVariant.None;
|
|
|
|
return value.Trim() switch
|
|
{
|
|
"Light" => OmniNxVariant.Light,
|
|
"Standard" => OmniNxVariant.Standard,
|
|
"OC" => OmniNxVariant.Oc,
|
|
_ => OmniNxVariant.None,
|
|
};
|
|
}
|
|
}
|
|
|
|
public static class PathUtils
|
|
{
|
|
public static string NormalizeDriveRoot(string driveRoot)
|
|
{
|
|
var root = driveRoot.Trim();
|
|
if (!root.EndsWith(Path.DirectorySeparatorChar))
|
|
root += Path.DirectorySeparatorChar;
|
|
return root;
|
|
}
|
|
|
|
public static string ToDrivePath(string driveRoot, string sdRelative)
|
|
{
|
|
var rel = sdRelative.Replace("sd:/", string.Empty).Replace("sd:\\", string.Empty)
|
|
.Replace('/', Path.DirectorySeparatorChar);
|
|
return Path.Combine(NormalizeDriveRoot(driveRoot), rel);
|
|
}
|
|
|
|
public static string RelativeFromDrive(string driveRoot, string fullPath)
|
|
{
|
|
var root = NormalizeDriveRoot(driveRoot);
|
|
if (fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase))
|
|
return fullPath[root.Length..];
|
|
return fullPath;
|
|
}
|
|
|
|
public static bool PathExists(string path)
|
|
{
|
|
return Directory.Exists(path) || File.Exists(path);
|
|
}
|
|
|
|
public static void EnsureDirectory(string path)
|
|
{
|
|
Directory.CreateDirectory(path);
|
|
}
|
|
|
|
public static void CopyFile(string src, string dst, bool overwrite = true)
|
|
{
|
|
var dir = Path.GetDirectoryName(dst);
|
|
if (!string.IsNullOrEmpty(dir))
|
|
Directory.CreateDirectory(dir);
|
|
File.Copy(src, dst, overwrite);
|
|
}
|
|
|
|
public static void CopyDirectory(string src, string dst, IProgress<(int current, int total, string file)>? progress = null)
|
|
{
|
|
if (!Directory.Exists(src))
|
|
return;
|
|
|
|
// Folders first, so empty folders from the pack exist on the SD as well.
|
|
Directory.CreateDirectory(dst);
|
|
foreach (var dir in Directory.GetDirectories(src, "*", SearchOption.AllDirectories))
|
|
Directory.CreateDirectory(Path.Combine(dst, Path.GetRelativePath(src, dir)));
|
|
|
|
var files = Directory.GetFiles(src, "*", SearchOption.AllDirectories);
|
|
var total = files.Length;
|
|
var current = 0;
|
|
|
|
foreach (var file in files)
|
|
{
|
|
var rel = Path.GetRelativePath(src, file);
|
|
var target = Path.Combine(dst, rel);
|
|
CopyFile(file, target);
|
|
current++;
|
|
progress?.Report((current, total, rel));
|
|
}
|
|
}
|
|
|
|
public static void DeletePath(string path)
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
File.SetAttributes(path, FileAttributes.Normal);
|
|
File.Delete(path);
|
|
return;
|
|
}
|
|
|
|
if (Directory.Exists(path))
|
|
{
|
|
// Directory.Delete fails on read-only entries (common on cards from other packs or macOS).
|
|
var root = new DirectoryInfo(path);
|
|
foreach (var entry in root.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
|
|
{
|
|
if (entry.Attributes.HasFlag(FileAttributes.ReadOnly))
|
|
entry.Attributes &= ~FileAttributes.ReadOnly;
|
|
}
|
|
if (root.Attributes.HasFlag(FileAttributes.ReadOnly))
|
|
root.Attributes &= ~FileAttributes.ReadOnly;
|
|
|
|
root.Delete(recursive: true);
|
|
}
|
|
}
|
|
|
|
public static int CountItemsRecursive(string path)
|
|
{
|
|
if (File.Exists(path))
|
|
return 1;
|
|
if (!Directory.Exists(path))
|
|
return 0;
|
|
|
|
var count = 1;
|
|
foreach (var entry in Directory.EnumerateFileSystemEntries(path, "*", SearchOption.AllDirectories))
|
|
count++;
|
|
return count;
|
|
}
|
|
}
|