diff --git a/src/GeneralUpdate.Tools.V12/App.axaml b/src/GeneralUpdate.Tools.V12/App.axaml
new file mode 100644
index 0000000..16c7906
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/App.axaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GeneralUpdate.Tools.V12/App.axaml.cs b/src/GeneralUpdate.Tools.V12/App.axaml.cs
new file mode 100644
index 0000000..6625bc1
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/App.axaml.cs
@@ -0,0 +1,18 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using GeneralUpdate.Tools.V12.ViewModels;
+using GeneralUpdate.Tools.V12.Views;
+
+namespace GeneralUpdate.Tools.V12;
+
+public partial class App : Application
+{
+ public override void Initialize() { AvaloniaXamlLoader.Load(this); }
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow { DataContext = new MainWindowViewModel() };
+ base.OnFrameworkInitializationCompleted();
+ }
+}
diff --git a/src/GeneralUpdate.Tools.V12/Assets/avalonia-logo.ico b/src/GeneralUpdate.Tools.V12/Assets/avalonia-logo.ico
new file mode 100644
index 0000000..f7da8bb
Binary files /dev/null and b/src/GeneralUpdate.Tools.V12/Assets/avalonia-logo.ico differ
diff --git a/src/GeneralUpdate.Tools.V12/GeneralUpdate.Tools.V12.csproj b/src/GeneralUpdate.Tools.V12/GeneralUpdate.Tools.V12.csproj
new file mode 100644
index 0000000..6c97196
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/GeneralUpdate.Tools.V12.csproj
@@ -0,0 +1,32 @@
+
+
+ WinExe
+ net10.0
+ enable
+ app.manifest
+ true
+ GeneralUpdate.Tools.V12
+
+
+
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GeneralUpdate.Tools.V12/Models/ExtensionConfigModel.cs b/src/GeneralUpdate.Tools.V12/Models/ExtensionConfigModel.cs
new file mode 100644
index 0000000..822e269
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Models/ExtensionConfigModel.cs
@@ -0,0 +1,21 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace GeneralUpdate.Tools.V12.Models;
+
+public partial class ExtensionConfigModel : ObservableObject
+{
+ [ObservableProperty] private string _name = "";
+ [ObservableProperty] private string _version = "1.0.0.0";
+ [ObservableProperty] private string _description = "";
+ [ObservableProperty] private string _extensionDirectory = "";
+ [ObservableProperty] private string _exportPath = "";
+ [ObservableProperty] private string _dependencies = "";
+ [ObservableProperty] private string _publisher = "";
+ [ObservableProperty] private string _license = "";
+ [ObservableProperty] private string _categoriesText = "";
+ [ObservableProperty] private string _minHostVersion = "";
+ [ObservableProperty] private string _maxHostVersion = "";
+ [ObservableProperty] private int _platformValue = 1;
+ [ObservableProperty] private bool _isPreRelease;
+ [ObservableProperty] private string _outputPath = "";
+}
diff --git a/src/GeneralUpdate.Tools.V12/Models/OSSConfigModel.cs b/src/GeneralUpdate.Tools.V12/Models/OSSConfigModel.cs
new file mode 100644
index 0000000..153fbf8
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Models/OSSConfigModel.cs
@@ -0,0 +1,12 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace GeneralUpdate.Tools.V12.Models;
+
+public partial class OSSConfigModel : ObservableObject
+{
+ [ObservableProperty] private string _packetName = "Packet";
+ [ObservableProperty] private string _hash = "";
+ [ObservableProperty] private string _version = "1.0.0.0";
+ [ObservableProperty] private string _url = "http://127.0.0.1";
+ [ObservableProperty] private string _releaseDate = "";
+}
diff --git a/src/GeneralUpdate.Tools.V12/Models/PatchConfigModel.cs b/src/GeneralUpdate.Tools.V12/Models/PatchConfigModel.cs
new file mode 100644
index 0000000..dbafd04
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Models/PatchConfigModel.cs
@@ -0,0 +1,13 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace GeneralUpdate.Tools.V12.Models;
+
+public partial class PatchConfigModel : ObservableObject
+{
+ [ObservableProperty] private string _oldDirectory = "";
+ [ObservableProperty] private string _newDirectory = "";
+ [ObservableProperty] private string _packageName = "";
+ [ObservableProperty] private string _version = "1.0.0.0";
+ [ObservableProperty] private string _format = ".zip";
+ [ObservableProperty] private string _outputPath = "";
+}
diff --git a/src/GeneralUpdate.Tools.V12/Program.cs b/src/GeneralUpdate.Tools.V12/Program.cs
new file mode 100644
index 0000000..74be8c9
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Program.cs
@@ -0,0 +1,18 @@
+using Avalonia;
+using System;
+
+namespace GeneralUpdate.Tools.V12;
+
+sealed class Program
+{
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace()
+ .UseSkia();
+}
diff --git a/src/GeneralUpdate.Tools.V12/Services/DiffService.cs b/src/GeneralUpdate.Tools.V12/Services/DiffService.cs
new file mode 100644
index 0000000..6e1418a
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Services/DiffService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using GeneralUpdate.Differential;
+
+namespace GeneralUpdate.Tools.V12.Services;
+
+public class DiffService
+{
+ public async Task GeneratePatchAsync(string oldDir, string newDir, string patchDir)
+ {
+ if (!Directory.Exists(oldDir)) throw new DirectoryNotFoundException("Old: " + oldDir);
+ if (!Directory.Exists(newDir)) throw new DirectoryNotFoundException("New: " + newDir);
+ Directory.CreateDirectory(patchDir);
+ await Task.Run(() => DifferentialCore.Clean(oldDir, newDir, patchDir).GetAwaiter().GetResult());
+ }
+}
diff --git a/src/GeneralUpdate.Tools.V12/Services/PackageService.cs b/src/GeneralUpdate.Tools.V12/Services/PackageService.cs
new file mode 100644
index 0000000..5352173
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Services/PackageService.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace GeneralUpdate.Tools.V12.Services;
+
+public class PackageService
+{
+ public async Task CompressDirectoryAsync(string sourceDir, string outputPath)
+ {
+ await Task.Run(() => { if (File.Exists(outputPath)) File.Delete(outputPath); ZipFile.CreateFromDirectory(sourceDir, outputPath, CompressionLevel.Optimal, false); });
+ }
+ public async Task CreateManifestAsync(string zipPath, object manifest)
+ {
+ await Task.Run(() => {
+ var json = JsonConvert.SerializeObject(manifest, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
+ using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update);
+ var entry = archive.CreateEntry("manifest.json", CompressionLevel.Optimal);
+ using var writer = new StreamWriter(entry.Open(), Encoding.UTF8);
+ writer.Write(json);
+ });
+ }
+}
+
+public class HashService
+{
+ public async Task ComputeHashAsync(string filePath)
+ {
+ return await Task.Run(() => {
+ using var sha256 = SHA256.Create();
+ using var stream = File.OpenRead(filePath);
+ var hash = sha256.ComputeHash(stream);
+ return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
+ });
+ }
+}
diff --git a/src/GeneralUpdate.Tools.V12/ViewLocator.cs b/src/GeneralUpdate.Tools.V12/ViewLocator.cs
new file mode 100644
index 0000000..e9169f4
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/ViewLocator.cs
@@ -0,0 +1,19 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using GeneralUpdate.Tools.V12.ViewModels;
+
+namespace GeneralUpdate.Tools.V12;
+
+public class ViewLocator : IDataTemplate
+{
+ public Control? Build(object? param)
+ {
+ if (param is null) return null;
+ var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
+ var type = Type.GetType(name);
+ if (type != null) { var c = (Control)Activator.CreateInstance(type)!; c.DataContext = param; return c; }
+ return new TextBlock { Text = "Not Found: " + name };
+ }
+ public bool Match(object? data) => data is ViewModelBase;
+}
diff --git a/src/GeneralUpdate.Tools.V12/ViewModels/ExtensionViewModel.cs b/src/GeneralUpdate.Tools.V12/ViewModels/ExtensionViewModel.cs
new file mode 100644
index 0000000..15f598e
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/ViewModels/ExtensionViewModel.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using GeneralUpdate.Tools.V12.Models;
+using GeneralUpdate.Tools.V12.Services;
+
+namespace GeneralUpdate.Tools.V12.ViewModels;
+
+public partial class ExtensionViewModel : ViewModelBase
+{
+ private readonly PackageService _pkg = new();
+ public ExtensionConfigModel Config { get; } = new();
+ [ObservableProperty] private bool _isBuilding;
+ [ObservableProperty] private string _status = "就绪";
+ [ObservableProperty] private string _newPropKey = "";
+ [ObservableProperty] private string _newPropValue = "";
+ public ObservableCollection CustomProps { get; } = new();
+
+ async Task Pick() { var tl = Avalonia.Controls.TopLevel.GetTopLevel((Avalonia.Application.Current?.ApplicationLifetime as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime)?.MainWindow); if (tl == null) return null; var r = await tl.StorageProvider.OpenFolderPickerAsync(new Avalonia.Platform.Storage.FolderPickerOpenOptions { Title = "选择目录", AllowMultiple = false }); return r.Count > 0 ? r[0].Path.LocalPath : null; }
+
+ [RelayCommand] async Task SelectExt() { var p = await Pick(); if (p != null) Config.ExtensionDirectory = p; }
+ [RelayCommand] async Task SelectExport() { var p = await Pick(); if (p != null) Config.ExportPath = p; }
+ [RelayCommand] void AddProp() { if (!string.IsNullOrWhiteSpace(NewPropKey) && !string.IsNullOrWhiteSpace(NewPropValue)) { CustomProps.Add(new(NewPropKey, NewPropValue)); NewPropKey = ""; NewPropValue = ""; } }
+ [RelayCommand] void RemoveProp(CustomPropModel? item) { if (item != null) CustomProps.Remove(item); }
+
+ [RelayCommand] async Task Generate()
+ {
+ if (string.IsNullOrWhiteSpace(Config.Name) || string.IsNullOrWhiteSpace(Config.Version)) { Status = "请填写扩展名称和版本"; return; }
+ if (string.IsNullOrWhiteSpace(Config.ExtensionDirectory) || !Directory.Exists(Config.ExtensionDirectory)) { Status = "请选择有效的扩展目录"; return; }
+ IsBuilding = true; Status = "正在生成扩展包...";
+ try
+ {
+ var dir = string.IsNullOrWhiteSpace(Config.ExportPath) ? Environment.GetFolderPath(Environment.SpecialFolder.Desktop) : Config.ExportPath;
+ var zip = Path.Combine(dir, $"{Sanitize(Config.Name)}_{Config.Version}.zip");
+ await _pkg.CompressDirectoryAsync(Config.ExtensionDirectory, zip);
+ await _pkg.CreateManifestAsync(zip, new {
+ name = Config.Name, version = Config.Version, description = Config.Description,
+ publisher = Config.Publisher, license = Config.License, dependencies = Config.Dependencies,
+ minHostVersion = Config.MinHostVersion, maxHostVersion = Config.MaxHostVersion,
+ isPreRelease = Config.IsPreRelease,
+ customProperties = CustomProps.ToDictionary(p => p.Key, p => p.Value)
+ });
+ Config.OutputPath = zip;
+ Status = $"成功: {Path.GetFileName(zip)}";
+ }
+ catch (Exception ex) { Status = $"失败: {ex.Message}"; }
+ finally { IsBuilding = false; }
+ }
+ static string Sanitize(string n) => string.Join("_", n.Split(Path.GetInvalidFileNameChars()));
+}
+
+public partial class CustomPropModel(string key, string value) : ObservableObject
+{
+ public string Key { get; set; } = key;
+ public string Value { get; set; } = value;
+}
diff --git a/src/GeneralUpdate.Tools.V12/ViewModels/MainWindowViewModel.cs b/src/GeneralUpdate.Tools.V12/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..d20098b
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,31 @@
+using System.Collections.ObjectModel;
+using System.Linq;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using GeneralUpdate.Tools.V12.Models;
+
+namespace GeneralUpdate.Tools.V12.ViewModels;
+
+public partial class MainWindowViewModel : ViewModelBase
+{
+ [ObservableProperty] private ViewModelBase _currentPage = new PatchViewModel();
+ public ObservableCollection NavItems { get; } = new();
+
+ public MainWindowViewModel()
+ {
+ NavItems.Add(new("Patch", "补丁包", typeof(PatchViewModel), true));
+ NavItems.Add(new("Extension", "扩展包", typeof(ExtensionViewModel), false));
+ NavItems.Add(new("OSS", "OSS配置", typeof(OSSViewModel), false));
+ }
+
+ [RelayCommand] private void Navigate(NavItem item) { foreach (var n in NavItems) n.IsSelected = false; item.IsSelected = true; CurrentPage = item.Key switch { "Patch" => new PatchViewModel(), "Extension" => new ExtensionViewModel(), _ => new OSSViewModel() }; }
+}
+
+public partial class NavItem : ObservableObject
+{
+ public string Key { get; }
+ public string Title { get; }
+ public System.Type PageType { get; }
+ [ObservableProperty] private bool _isSelected;
+ public NavItem(string key, string title, System.Type pageType, bool selected) { Key = key; Title = title; PageType = pageType; _isSelected = selected; }
+}
diff --git a/src/GeneralUpdate.Tools.V12/ViewModels/OSSViewModel.cs b/src/GeneralUpdate.Tools.V12/ViewModels/OSSViewModel.cs
new file mode 100644
index 0000000..43f1cf4
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/ViewModels/OSSViewModel.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using GeneralUpdate.Tools.V12.Models;
+using GeneralUpdate.Tools.V12.Services;
+using Newtonsoft.Json;
+
+namespace GeneralUpdate.Tools.V12.ViewModels;
+
+public partial class OSSViewModel : ViewModelBase
+{
+ private readonly HashService _hash = new();
+ public ObservableCollection Configs { get; } = new();
+ [ObservableProperty] private OSSConfigModel _current = new();
+ [ObservableProperty] private string _status = "就绪";
+
+ [RelayCommand] async Task ComputeHash() { var tl = Avalonia.Controls.TopLevel.GetTopLevel((Avalonia.Application.Current?.ApplicationLifetime as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime)?.MainWindow); if (tl == null) return; var files = await tl.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions { Title = "选择文件", AllowMultiple = false }); if (files.Count > 0) { Current.Hash = await _hash.ComputeHashAsync(files[0].Path.LocalPath); Status = $"SHA256: {Current.Hash}"; } }
+ [RelayCommand] void Append() { Configs.Add(new() { PacketName = Current.PacketName, Hash = Current.Hash, Version = Current.Version, Url = Current.Url, ReleaseDate = Current.ReleaseDate }); Status = "已添加"; }
+ [RelayCommand] void Remove(OSSConfigModel? item) { if (item != null) Configs.Remove(item); }
+ [RelayCommand] void Clear() { Configs.Clear(); Status = "已清空"; }
+ [RelayCommand] async Task Export() { var tl = Avalonia.Controls.TopLevel.GetTopLevel((Avalonia.Application.Current?.ApplicationLifetime as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime)?.MainWindow); if (tl == null) return; var file = await tl.StorageProvider.SaveFilePickerAsync(new Avalonia.Platform.Storage.FilePickerSaveOptions { Title = "导出JSON", DefaultExtension = ".json", SuggestedFileName = "oss_config.json" }); if (file != null) { await File.WriteAllTextAsync(file.Path.LocalPath, JsonConvert.SerializeObject(Configs, Formatting.Indented), System.Text.Encoding.UTF8); Status = $"导出: {Configs.Count} 条"; } }
+}
diff --git a/src/GeneralUpdate.Tools.V12/ViewModels/PatchViewModel.cs b/src/GeneralUpdate.Tools.V12/ViewModels/PatchViewModel.cs
new file mode 100644
index 0000000..107e8c8
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/ViewModels/PatchViewModel.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using GeneralUpdate.Tools.V12.Models;
+using GeneralUpdate.Tools.V12.Services;
+
+namespace GeneralUpdate.Tools.V12.ViewModels;
+
+public partial class PatchViewModel : ViewModelBase
+{
+ private readonly DiffService _diff = new();
+ private readonly PackageService _pkg = new();
+ public PatchConfigModel Config { get; } = new();
+ [ObservableProperty] private bool _isBuilding;
+ [ObservableProperty] private double _progress;
+ [ObservableProperty] private string _status = "就绪";
+ [ObservableProperty] private ObservableCollection _log = new();
+
+ async Task Pick() { var tl = Avalonia.Controls.TopLevel.GetTopLevel((Avalonia.Application.Current?.ApplicationLifetime as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime)?.MainWindow); if (tl == null) return null; var r = await tl.StorageProvider.OpenFolderPickerAsync(new Avalonia.Platform.Storage.FolderPickerOpenOptions { Title = "选择目录", AllowMultiple = false }); return r.Count > 0 ? r[0].Path.LocalPath : null; }
+
+ [RelayCommand] async Task SelectOld() { var p = await Pick(); if (p != null) { Config.OldDirectory = p; L($"旧版本: {p}"); } }
+ [RelayCommand] async Task SelectNew() { var p = await Pick(); if (p != null) { Config.NewDirectory = p; L($"新版本: {p}"); } }
+ [RelayCommand] async Task SelectOut() { var p = await Pick(); if (p != null) Config.OutputPath = p; }
+
+ [RelayCommand] async Task Build()
+ {
+ if (string.IsNullOrWhiteSpace(Config.OldDirectory) || string.IsNullOrWhiteSpace(Config.NewDirectory)) { Status = "请选择新旧版本目录"; return; }
+ IsBuilding = true; Log.Clear(); Progress = 0; Status = "正在生成差分补丁...";
+ try
+ {
+ var tmp = Path.Combine(Path.GetTempPath(), $"gupatch_{DateTime.Now:yyyyMMddHHmmss}"); Directory.CreateDirectory(tmp);
+ L($"临时目录: {tmp}"); Progress = 20;
+ L("对比目录差异 + 生成 BSDiff40 补丁...");
+ await _diff.GeneratePatchAsync(Config.OldDirectory, Config.NewDirectory, tmp);
+ Progress = 70; L("补丁生成完成");
+ var outDir = string.IsNullOrWhiteSpace(Config.OutputPath) ? Environment.GetFolderPath(Environment.SpecialFolder.Desktop) : Config.OutputPath;
+ var name = string.IsNullOrWhiteSpace(Config.PackageName) ? $"patch_{DateTime.Now:yyyyMMddHHmmss}" : Config.PackageName;
+ var zip = Path.Combine(outDir, $"{name}.zip");
+ L($"打包: {Path.GetFileName(zip)}");
+ await _pkg.CompressDirectoryAsync(tmp, zip);
+ Directory.Delete(tmp, true);
+ Progress = 100; Config.OutputPath = zip;
+ Status = $"成功: {Path.GetFileName(zip)} ({new FileInfo(zip).Length/1024.0:F1} KB)";
+ L(Status);
+ }
+ catch (Exception ex) { Status = $"失败: {ex.Message}"; L($"错误: {ex}"); }
+ finally { IsBuilding = false; }
+ }
+ void L(string m) => Log.Add($"[{DateTime.Now:HH:mm:ss}] {m}");
+}
diff --git a/src/GeneralUpdate.Tools.V12/ViewModels/ViewModelBase.cs b/src/GeneralUpdate.Tools.V12/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000..514935b
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/ViewModels/ViewModelBase.cs
@@ -0,0 +1,5 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace GeneralUpdate.Tools.V12.ViewModels;
+
+public partial class ViewModelBase : ObservableObject { }
diff --git a/src/GeneralUpdate.Tools.V12/Views/ExtensionView.axaml b/src/GeneralUpdate.Tools.V12/Views/ExtensionView.axaml
new file mode 100644
index 0000000..aaf62b9
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/ExtensionView.axaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GeneralUpdate.Tools.V12/Views/ExtensionView.axaml.cs b/src/GeneralUpdate.Tools.V12/Views/ExtensionView.axaml.cs
new file mode 100644
index 0000000..c09f29b
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/ExtensionView.axaml.cs
@@ -0,0 +1 @@
+using Avalonia.Controls; namespace GeneralUpdate.Tools.V12.Views { public partial class ExtensionView : UserControl { public ExtensionView() { InitializeComponent(); } } }
diff --git a/src/GeneralUpdate.Tools.V12/Views/MainWindow.axaml b/src/GeneralUpdate.Tools.V12/Views/MainWindow.axaml
new file mode 100644
index 0000000..6183898
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/MainWindow.axaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GeneralUpdate.Tools.V12/Views/MainWindow.axaml.cs b/src/GeneralUpdate.Tools.V12/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..f4d0c5a
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/MainWindow.axaml.cs
@@ -0,0 +1 @@
+using Avalonia.Controls; namespace GeneralUpdate.Tools.V12.Views { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
diff --git a/src/GeneralUpdate.Tools.V12/Views/OSSView.axaml b/src/GeneralUpdate.Tools.V12/Views/OSSView.axaml
new file mode 100644
index 0000000..6f52dcc
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/OSSView.axaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GeneralUpdate.Tools.V12/Views/OSSView.axaml.cs b/src/GeneralUpdate.Tools.V12/Views/OSSView.axaml.cs
new file mode 100644
index 0000000..ca2a117
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/OSSView.axaml.cs
@@ -0,0 +1 @@
+using Avalonia.Controls; namespace GeneralUpdate.Tools.V12.Views { public partial class OSSView : UserControl { public OSSView() { InitializeComponent(); } } }
diff --git a/src/GeneralUpdate.Tools.V12/Views/PatchView.axaml b/src/GeneralUpdate.Tools.V12/Views/PatchView.axaml
new file mode 100644
index 0000000..3f394fd
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/PatchView.axaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GeneralUpdate.Tools.V12/Views/PatchView.axaml.cs b/src/GeneralUpdate.Tools.V12/Views/PatchView.axaml.cs
new file mode 100644
index 0000000..0138d70
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/Views/PatchView.axaml.cs
@@ -0,0 +1 @@
+using Avalonia.Controls; namespace GeneralUpdate.Tools.V12.Views { public partial class PatchView : UserControl { public PatchView() { InitializeComponent(); } } }
diff --git a/src/GeneralUpdate.Tools.V12/app.manifest b/src/GeneralUpdate.Tools.V12/app.manifest
new file mode 100644
index 0000000..cde83fd
--- /dev/null
+++ b/src/GeneralUpdate.Tools.V12/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+