Basic update system, help message.
parent
4d594a6637
commit
22e54a2333
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace DotN64.Desktop
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class AssemblyReleaseStreamAttribute : Attribute
|
||||
{
|
||||
#region Properties
|
||||
public string Stream { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public AssemblyReleaseStreamAttribute(string stream)
|
||||
{
|
||||
Stream = stream;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -29,6 +29,8 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DotN64\DotN64.csproj">
|
||||
|
@ -42,6 +44,8 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="AssemblyReleaseStreamAttribute.cs" />
|
||||
<Compile Include="Updater.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,15 +1,39 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using DotN64.Desktop;
|
||||
|
||||
namespace DotN64
|
||||
[assembly: AssemblyTitle(nameof(DotN64))]
|
||||
[assembly: AssemblyDescription("Nintendo 64 emulator.")]
|
||||
[assembly: AssemblyVersion("0.0.*")]
|
||||
[assembly: AssemblyReleaseStream("master")]
|
||||
namespace DotN64.Desktop
|
||||
{
|
||||
using Diagnostics;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
#region Properties
|
||||
private static DateTime BuildDate
|
||||
{
|
||||
get
|
||||
{
|
||||
var version = Assembly.GetEntryAssembly().GetName().Version;
|
||||
|
||||
return new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseStream { get; } = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyReleaseStreamAttribute>().Stream;
|
||||
|
||||
public static Uri Website { get; } = new Uri("https://nabile.duckdns.org/DotN64");
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
var nintendo64 = new Nintendo64();
|
||||
Debugger debugger = null;
|
||||
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
var options = new Options();
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
|
@ -18,18 +42,97 @@ namespace DotN64
|
|||
switch (arg)
|
||||
{
|
||||
case "--pif-rom":
|
||||
nintendo64.PIF.BootROM = File.ReadAllBytes(args[++i]);
|
||||
options.BootROM = args[++i];
|
||||
break;
|
||||
case "--debug":
|
||||
case "-d":
|
||||
debugger = new Debugger(nintendo64);
|
||||
options.UseDebugger = true;
|
||||
break;
|
||||
case "--update":
|
||||
case "-u":
|
||||
switch (args.Length - 1 > i ? args[++i] : null)
|
||||
{
|
||||
case "check":
|
||||
case "c":
|
||||
Check();
|
||||
return;
|
||||
case "list":
|
||||
case "l":
|
||||
foreach (var stream in Updater.Streams)
|
||||
{
|
||||
Console.WriteLine($"{stream}{(stream == ReleaseStream ? " (current)" : string.Empty)}");
|
||||
}
|
||||
return;
|
||||
case "stream":
|
||||
case "s":
|
||||
Update(args[++i]);
|
||||
return;
|
||||
default:
|
||||
Update();
|
||||
return;
|
||||
}
|
||||
case "--repair":
|
||||
case "-r":
|
||||
Repair();
|
||||
return;
|
||||
case "--help":
|
||||
case "-h":
|
||||
ShowHelp();
|
||||
return;
|
||||
default:
|
||||
nintendo64.Cartridge = Cartridge.FromFile(new FileInfo(arg));
|
||||
options.Cartridge = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory).Length <= 1) // Fresh install.
|
||||
Repair();
|
||||
|
||||
Run(options);
|
||||
}
|
||||
|
||||
private static bool Check(string releaseStream = null)
|
||||
{
|
||||
var newVersion = Updater.Check(releaseStream);
|
||||
|
||||
if (newVersion == null)
|
||||
{
|
||||
Console.WriteLine("Already up to date.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLine($"New version available: {newVersion}.");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void Update(string releaseStream = null, bool force = false)
|
||||
{
|
||||
if (!force && !Check(releaseStream))
|
||||
return;
|
||||
|
||||
Console.WriteLine("Downloading update...");
|
||||
Updater.Download(releaseStream);
|
||||
|
||||
Console.WriteLine("Applying update...");
|
||||
Updater.Apply();
|
||||
}
|
||||
|
||||
private static void Repair() => Update(force: true);
|
||||
|
||||
private static void Run(Options options)
|
||||
{
|
||||
var nintendo64 = new Nintendo64();
|
||||
Debugger debugger = null;
|
||||
|
||||
if (options.BootROM != null)
|
||||
nintendo64.PIF.BootROM = File.ReadAllBytes(options.BootROM);
|
||||
|
||||
if (options.UseDebugger)
|
||||
debugger = new Debugger(nintendo64);
|
||||
|
||||
if (options.Cartridge != null)
|
||||
nintendo64.Cartridge = Cartridge.FromFile(new FileInfo(options.Cartridge));
|
||||
|
||||
nintendo64.PowerOn();
|
||||
|
||||
if (debugger == null)
|
||||
|
@ -37,5 +140,44 @@ namespace DotN64
|
|||
else
|
||||
debugger.Run(true);
|
||||
}
|
||||
|
||||
private static void ShowInfo()
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
var title = assembly.GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
||||
var version = assembly.GetName().Version;
|
||||
var description = assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
|
||||
|
||||
Console.WriteLine($"{title} v{version} ({BuildDate}){(description != null ? $": {description}" : string.Empty)} ({Website})");
|
||||
}
|
||||
|
||||
private static void ShowHelp()
|
||||
{
|
||||
ShowInfo();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Usage: {Path.GetFileName(Assembly.GetEntryAssembly().Location)} [Options] <ROM image>");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("ROM image: Opens the file as a game cartridge.");
|
||||
Console.WriteLine("Options:");
|
||||
Console.WriteLine("\t--pif-rom <path>: Loads the PIF's boot ROM into the machine.");
|
||||
Console.WriteLine("\t-d, --debug: Launches the debugger for the Nintendo 64's CPU.");
|
||||
Console.WriteLine("\t-u, --update [action]: Updates the program.");
|
||||
Console.WriteLine("\t[action = 'check', 'c']: Checks for a new update.");
|
||||
Console.WriteLine("\t[action = 'list', 'l']: Lists the release streams available for download.");
|
||||
Console.WriteLine("\t[action = 'stream', 's'] <stream>: Downloads an update from the specified release stream.");
|
||||
Console.WriteLine("\t-r, --repair: Repairs the installation by redownloading the full program.");
|
||||
Console.WriteLine("\t-h, --help: Shows this help.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Structures
|
||||
private struct Options
|
||||
{
|
||||
#region Fields
|
||||
public bool UseDebugger;
|
||||
public string BootROM, Cartridge;
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
|
||||
namespace DotN64.Desktop
|
||||
{
|
||||
public static class Updater
|
||||
{
|
||||
#region Fields
|
||||
private const string StagingDirectory = ".update", ProjectName = nameof(DotN64), NewFileExtension = ".new";
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
private static string InstallDirectory => AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
private static string PlatformSuffix => string.Join(".", Environment.OSVersion.Platform, Environment.Is64BitOperatingSystem ? "64" : "32");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available release streams.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> Streams
|
||||
{
|
||||
get
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
using (var reader = new StreamReader(client.OpenRead(new Uri(Program.Website, $"{ProjectName}/Download/streams"))))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
yield return reader.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Checks for a new update.
|
||||
/// </summary>
|
||||
public static Version Check(string releaseStream = null)
|
||||
{
|
||||
if (releaseStream == null)
|
||||
releaseStream = Program.ReleaseStream;
|
||||
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
var remoteVersion = new Version(client.DownloadString(new Uri(Program.Website, $"{ProjectName}/Download/{releaseStream}/version")));
|
||||
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
return remoteVersion > currentVersion ? remoteVersion : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the latest update.
|
||||
/// </summary>
|
||||
public static void Download(string releaseStream = null)
|
||||
{
|
||||
if (releaseStream == null)
|
||||
releaseStream = Program.ReleaseStream;
|
||||
|
||||
var updateDirectory = new DirectoryInfo(Path.Combine(InstallDirectory, StagingDirectory));
|
||||
|
||||
if (updateDirectory.Exists)
|
||||
updateDirectory.Delete(true);
|
||||
|
||||
updateDirectory.Create();
|
||||
updateDirectory.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
|
||||
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
using (var archive = new ZipArchive(client.OpenRead(new Uri(Program.Website, $"{ProjectName}/Download/{releaseStream}/{ProjectName}.zip"))))
|
||||
{
|
||||
archive.ExtractToDirectory(updateDirectory.FullName);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var archive = new ZipArchive(client.OpenRead(new Uri(Program.Website, $"{ProjectName}/Download/{releaseStream}/{ProjectName}.{PlatformSuffix}.zip"))))
|
||||
{
|
||||
archive.ExtractToDirectory(updateDirectory.FullName);
|
||||
}
|
||||
}
|
||||
catch (WebException e) when ((e.Response as HttpWebResponse).StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the update.
|
||||
/// </summary>
|
||||
public static void Apply()
|
||||
{
|
||||
var updateDirectory = new DirectoryInfo(Path.Combine(InstallDirectory, StagingDirectory));
|
||||
var executableName = Path.GetFileName(Assembly.GetEntryAssembly().Location);
|
||||
var isWindow = Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
var shouldRestart = false;
|
||||
|
||||
foreach (var file in updateDirectory.GetFiles())
|
||||
{
|
||||
var destination = Path.Combine(InstallDirectory, file.Name);
|
||||
|
||||
if (isWindow && file.Name == executableName)
|
||||
{
|
||||
file.MoveTo(destination + NewFileExtension);
|
||||
|
||||
shouldRestart = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
File.Delete(destination);
|
||||
file.MoveTo(destination);
|
||||
}
|
||||
|
||||
updateDirectory.Delete(true);
|
||||
|
||||
if (isWindow && shouldRestart)
|
||||
ApplyWindowsUpdate(executableName);
|
||||
}
|
||||
|
||||
private static void ApplyWindowsUpdate(string executableName)
|
||||
{
|
||||
var newExecutableName = executableName + NewFileExtension;
|
||||
var scriptFile = new FileInfo("CompleteUpdate.cmd");
|
||||
var processID = Process.GetCurrentProcess().Id;
|
||||
|
||||
using (var writer = scriptFile.CreateText())
|
||||
{
|
||||
writer.WriteLine(":CHECK");
|
||||
writer.WriteLine("timeout /t 1");
|
||||
writer.WriteLine($"tasklist /fi \"pid eq {processID}\" | find \"{processID}\"");
|
||||
|
||||
writer.WriteLine("if errorlevel 1 goto UPDATE");
|
||||
|
||||
writer.WriteLine("goto CHECK");
|
||||
|
||||
writer.WriteLine(":UPDATE");
|
||||
writer.WriteLine($"move /Y \"{newExecutableName}\" \"{executableName}\"");
|
||||
writer.WriteLine($"start \"\" \"{executableName}\"");
|
||||
writer.WriteLine($"del /A:H %0");
|
||||
}
|
||||
|
||||
scriptFile.Attributes |= FileAttributes.Hidden;
|
||||
|
||||
Process.Start(new ProcessStartInfo(scriptFile.FullName)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
});
|
||||
Environment.Exit(0);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue