initial commit

This commit is contained in:
2025-12-10 10:59:48 +01:00
commit b3605e725f
30 changed files with 2363 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows10.0.17763.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>bb.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="bb.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="AppPayload.zip">
<Visible>false</Visible>
</EmbeddedResource>
</ItemGroup>
</Project>

195
BlueberryUpdater/Program.cs Normal file
View File

@@ -0,0 +1,195 @@
using System.Diagnostics;
using System.IO.Compression;
using System.Reflection;
namespace BlueberryUpdater
{
internal class Program
{
static string appName = "Blueberry";
static string updaterName = appName + "Updater";
static string installPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), appName);
static void Main(string[] args)
{
if (Directory.Exists(installPath) && File.Exists(Path.Combine(installPath, appName + ".exe")))
Uninstall();
else
Install();
}
private static void Uninstall()
{
Console.WriteLine("Would you like to uninstall Blueberry? [y/N]");
var key = Console.ReadLine();
if (key == null ||key.ToLower() != "y")
return;
var appdata = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), appName);
Console.WriteLine("Removing Blueberry...");
var dirs = Directory.GetDirectories(installPath);
var files = Directory.GetFiles(installPath);
var total = dirs.Length + files.Length;
var i = 0;
foreach (var dir in dirs)
{
i++;
Directory.Delete(dir, true);
DrawProgressBar((int)((double)i / total * 100), dir.Split('\\').Last());
}
foreach (var file in files)
{
i++;
if (file.Split('\\').Last() == updaterName + ".exe")
continue;
File.Delete(file);
i++;
DrawProgressBar((int)((double)i / total * 100), file.Split('\\').Last());
}
Directory.Delete(appdata, true);
DrawProgressBar(100, "Done");
Console.WriteLine();
Console.WriteLine("Uninstall Complete!");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
SelfDelete();
}
private static void Install()
{
try
{
string resourceName = "BlueberryUpdater.AppPayload.zip"; // Format: Namespace.Filename
Console.WriteLine($"Installing to {installPath}...");
// 1. Clean existing install if necessary
if (Directory.Exists(installPath))
{
Directory.Delete(installPath, true);
}
Directory.CreateDirectory(installPath);
// 2. Extract Embedded Resource
var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
throw new Exception($"Resource '{resourceName}' not found. Check Embedded Resource settings.");
}
using (ZipArchive archive = new(stream))
{
int totalEntries = archive.Entries.Count;
int currentEntry = 0;
foreach (ZipArchiveEntry entry in archive.Entries)
{
currentEntry++;
// Calculate percentage
int percent = (int)((double)currentEntry / totalEntries * 100);
// Draw Progress Bar
DrawProgressBar(percent, entry.Name);
// Create the full path
string destinationPath = Path.GetFullPath(Path.Combine(installPath, entry.FullName));
// Security check: prevent ZipSlip (writing outside target folder)
if (!destinationPath.StartsWith(installPath, StringComparison.OrdinalIgnoreCase))
continue;
// Handle folders vs files
if (string.IsNullOrEmpty(entry.Name)) // It's a directory
{
Directory.CreateDirectory(destinationPath);
}
else // It's a file
{
// Ensure the directory exists (zipped files might not list their dir first)
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
entry.ExtractToFile(destinationPath, overwrite: true);
}
}
}
}
MoveUpdater();
DrawProgressBar(100, "Done");
Console.WriteLine();
Console.WriteLine("Installation Complete!");
// Optional: Create Shortcut logic here
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Error: {ex.Message}");
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
public static void SelfDelete()
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
string directoryToDelete = Path.GetDirectoryName(exePath);
string args = $"/C timeout /t 2 /nobreak > Nul & del \"{exePath}\" & rmdir /q \"{directoryToDelete}\"";
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = args,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(psi);
Environment.Exit(0);
}
public static void MoveUpdater()
{
string currentExe = Process.GetCurrentProcess().MainModule.FileName;
var updaterPath = Path.Combine(installPath, updaterName + ".exe");
Directory.CreateDirectory(Path.GetDirectoryName(updaterPath));
File.Copy(currentExe, updaterPath, overwrite: true);
}
static void DrawProgressBar(int percent, string filename)
{
// Move cursor to start of line
Console.CursorLeft = 0;
// Limit filename length for clean display
string shortName = filename.Length > 20 ? filename.Substring(0, 17) + "..." : filename.PadRight(20);
Console.Write("[");
int width = Console.WindowWidth - 1; // Width of the bar
int progress = (int)((percent / 100.0) * width);
// Draw filled part
Console.Write(new string('#', progress));
// Draw empty part
Console.Write(new string('-', width - progress));
Console.Write($"] {percent}% {shortName}");
}
}
}

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
-->
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

BIN
BlueberryUpdater/bb.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB