Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,11 @@ dotnet_diagnostic.SA1602.severity = none
dotnet_diagnostic.SA1649.severity = none
dotnet_diagnostic.SA1633.severity = none

dotnet_diagnostic.NUnit2007.severity = error

dotnet_diagnostic.RCS1217.severity = none

dotnet_diagnostic.RS0030.severity = error
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_prefer_simple_property_accessors = true:suggestion
4 changes: 4 additions & 0 deletions Common/Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@
<LangVersion>preview</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NuGet.Versioning" Version="6.14.0" />
</ItemGroup>

</Project>
54 changes: 54 additions & 0 deletions Common/PlatformSpecific.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Common;

public static class PlatformSpecific
{
// For Unix-like systems (Linux, macOS)
static class UnixUserChecker
{
// Imports the geteuid() function from libc (the standard C library)
// geteuid() returns the effective user ID of the calling process.
// A value of 0 typically indicates the root user.
[DllImport("libc", EntryPoint = "geteuid")]
internal static extern uint geteuid();

public static bool IsRoot()
=> geteuid() == 0;
}

public static bool RunningAsAdmin()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return UnixUserChecker.IsRoot();
}

return false;
}

public static string EditorPlatformZipName(OSPlatform platform) =>
platform == OSPlatform.Windows ? "win-x64.zip" :
platform == OSPlatform.OSX ? "osx-x64.tar" :
platform == OSPlatform.Linux ? "linux-x64.tar" :
throw new PlatformNotSupportedException();

public static string EditorPlatformBinaryName(OSPlatform platform) =>
platform == OSPlatform.Windows ? $"{VersionHelpers.ObjectEditorName}.exe" :
platform == OSPlatform.OSX ? VersionHelpers.ObjectEditorName :
platform == OSPlatform.Linux ? VersionHelpers.ObjectEditorName :
throw new PlatformNotSupportedException();

public static OSPlatform GetPlatform =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? OSPlatform.Windows :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? OSPlatform.OSX :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? OSPlatform.Linux :
throw new PlatformNotSupportedException();
}
75 changes: 44 additions & 31 deletions Gui/VersionHelpers.cs → Common/VersionHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
using Common.Logging;
using NuGet.Versioning;
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

#if !DEBUG
using Common;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System;
#endif

namespace Gui;
namespace Common;

public static class VersionHelpers
{
public const string GithubApplicationName = "ObjectEditor";
public const string ObjectEditorName = "ObjectEditor";
public const string ObjectEditorUpdaterName = "ObjectEditorUpdater";
public const string GithubIssuePage = "https://github.com/OpenLoco/ObjectEditor/issues";
public const string GithubLatestReleaseDownloadPage = "https://github.com/OpenLoco/ObjectEditor/releases";
public const string GithubLatestReleaseAPI = "https://api.github.com/repos/OpenLoco/ObjectEditor/releases/latest";

// todo: instead of going to downloads, start the auto-updater (ObjectEditorUpdater.exe) with the right args
public static Process? OpenDownloadPage()
=> Process.Start(new ProcessStartInfo(GithubLatestReleaseDownloadPage) { UseShellExecute = true });

public static void StartAutoUpdater(ILogger logger, SemanticVersion latestVersion)
// win: object-editor-5.3.5-win-x64.zip
// osx: object-editor-5.3.5-osx-x64.tar
// linux: object-editor-5.3.5-linux-x64.tar

static string DownloadFilename(SemanticVersion latestVersion, OSPlatform platform)
=> $"object-editor-{latestVersion}-{PlatformSpecific.EditorPlatformZipName(platform)}";

public static string UrlForDownload(SemanticVersion latestVersion, OSPlatform platform)
=> $"{GithubLatestReleaseDownloadPage}/download/{latestVersion}/{DownloadFilename(latestVersion, platform)}";

public static void StartAutoUpdater(ILogger logger, SemanticVersion currentVersion, SemanticVersion latestVersion)
{
logger.Debug("Attempting to kill existing updater processes");
try
{
// kill any existing processes of the updater
Expand All @@ -46,38 +49,52 @@ public static void StartAutoUpdater(ILogger logger, SemanticVersion latestVersio
}
}

// win: object-editor-5.3.5-win-x64.zip
// osx: object-editor-5.3.5-osx-x64.tar
// linux: object-editor-5.3.5-linux-x64.tar
var platform = PlatformSpecific.EditorPlatformExtension;
var filename = $"object-editor-{latestVersion}-{platform}";
var editorExe = $"{ObjectEditorUpdaterName}.exe";
if (!File.Exists(editorExe))
{
logger.Error($"Cannot find the auto-updater executable: {editorExe}. You'll need to manually download the update.");
return;
}

var startInfo = new ProcessStartInfo($"{ObjectEditorUpdaterName}.exe",
var startInfo = new ProcessStartInfo(editorExe,
[
"--pid",
$"{Environment.ProcessId}",
"--url",
$"{GithubLatestReleaseDownloadPage}/download/{latestVersion}/{filename}",
"--current-version",
currentVersion.ToString(),
"--app-path",
$"{Environment.ProcessPath}",
])
{
// updater process will log to file
UseShellExecute = false,
CreateNoWindow = true,
};

logger.Debug($"CurrentProcessId: {Environment.ProcessId}");
logger.Debug($"CurrentProcessPath: {Environment.ProcessPath}");
logger.Debug($"Attempting to start auto-updater \"{startInfo}\"");
var process = Process.Start(startInfo);
Environment.Exit(0);

if (process != null)
{
logger.Info($"Started auto-updater process (PID {process.Id}) to update from {currentVersion} to {latestVersion}. Editor will now close");
Environment.Exit(0);
}
else
{
logger.Error("Failed to start auto-updater process. You'll need to manually download the update.");
}
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to start auto-updater: {ex}");
logger.Error($"Failed to start auto-updater: {ex}");
}
}

public static SemanticVersion GetCurrentAppVersion()
{
var assembly = Assembly.GetExecutingAssembly();
var assembly = Assembly.GetCallingAssembly();
if (assembly == null)
{
return UnknownVersion;
Expand All @@ -94,12 +111,11 @@ public static SemanticVersion GetCurrentAppVersion()
}
}

#if !DEBUG
// thanks for this one @IntelOrca, https://github.com/IntelOrca/PeggleEdit/blob/master/src/peggleedit/Forms/MainMDIForm.cs#L848-L861
public static SemanticVersion GetLatestAppVersion(SemanticVersion currentVersion)
public static SemanticVersion GetLatestAppVersion(string productName, SemanticVersion? currentVersion = null)
{
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(GithubApplicationName, currentVersion.ToString()));
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(productName, currentVersion?.ToString()));
var response = client.GetAsync(GithubLatestReleaseAPI).Result;
if (response.IsSuccessStatusCode)
{
Expand All @@ -109,11 +125,8 @@ public static SemanticVersion GetLatestAppVersion(SemanticVersion currentVersion
return GetVersionFromText(versionText);
}

#pragma warning disable CA2201 // Do not raise reserved exception types
throw new Exception($"Unable to get latest version. Error={response.StatusCode}");
#pragma warning restore CA2201 // Do not raise reserved exception types
return UnknownVersion;
}
#endif

public static readonly SemanticVersion UnknownVersion = new(0, 0, 0, "unknown");

Expand Down
3 changes: 2 additions & 1 deletion Gui/Gui.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0</TargetFramework>
Expand Down Expand Up @@ -87,4 +87,5 @@
<SubType>Code</SubType>
</Compile>
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions Gui/ObjectServiceClient.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Common;
using Common.Logging;
using Definitions.DTO;
using Definitions.Web;
Expand Down
36 changes: 0 additions & 36 deletions Gui/PlatformSpecific.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,12 @@
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading.Tasks;

namespace Gui;

// For Unix-like systems (Linux, macOS)
static class UnixUserChecker
{
// Imports the geteuid() function from libc (the standard C library)
// geteuid() returns the effective user ID of the calling process.
// A value of 0 typically indicates the root user.
[DllImport("libc", EntryPoint = "geteuid")]
internal static extern uint geteuid();

public static bool IsRoot()
=> geteuid() == 0;
}

public static class PlatformSpecific
{
public static bool RunningAsAdmin()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return UnixUserChecker.IsRoot();
}

return false;
}

public static void FolderOpenInDesktop(string directory, ILogger logger, string? filename = null)
{
try
Expand All @@ -55,12 +25,6 @@ public static void FolderOpenInDesktop(string directory, ILogger logger, string?
}
}

public static string EditorPlatformExtension
=> RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64.zip" :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx-x64.tar" :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux-x64.tar" :
"unknown";

static void FolderOpenInDesktopCore(string directory, string? filename = null)
{
if (!Directory.Exists(directory))
Expand Down
3 changes: 2 additions & 1 deletion Gui/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Logging;
using Avalonia.ReactiveUI;
using Common;
using System;

namespace Gui;
Expand All @@ -24,7 +25,7 @@ public static void Main(string[] args)

static void PreventRunningAsAdmin()
{
if (PlatformSpecific.RunningAsAdmin())
if (Common.PlatformSpecific.RunningAsAdmin())
{
const string errorMessage = "This application should not be run with elevated privileges. Please run it as a regular user.";

Expand Down
1 change: 1 addition & 0 deletions Gui/ViewModels/AudioViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Common;
using Common.Logging;
using Definitions.ObjectModels.Objects.Sound;
using Gui.Models.Audio;
Expand Down
2 changes: 1 addition & 1 deletion Gui/ViewModels/FolderTreeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avalonia.Controls.Selection;
using Avalonia.Media;
using Avalonia.Threading;
using Common;
using Dat.Data;
using Definitions.ObjectModels;
using Definitions.ObjectModels.Types;
Expand Down Expand Up @@ -203,7 +204,6 @@ public FolderTreeViewModel(ObjectEditorModel model)
_ = this.WhenAnyValue(o => o.CurrentLocalDirectory).Skip(1).Subscribe(async _ => await LoadDirectoryAsync(true));
_ = this.WhenAnyValue(o => o.CurrentLocalDirectory).Skip(1).Subscribe(_ => this.RaisePropertyChanged(nameof(CurrentDirectory)));


//_ = this.WhenAnyValue(o => o.SelectedTabIndex).Skip(1).Subscribe(_ => UpdateDirectoryItemsView());
_ = this.WhenAnyValue(o => o.SelectedTabIndex).Skip(1).Subscribe(_ => this.RaisePropertyChanged(nameof(CurrentDirectory)));
_ = this.WhenAnyValue(o => o.CurrentDirectory).Skip(1).Subscribe(async _ => await LoadDirectoryAsync(true));
Expand Down
1 change: 1 addition & 0 deletions Gui/ViewModels/LocoTypes/G1ViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Common;
using Dat.FileParsing;
using Gui.Models;
using Gui.ViewModels.Graphics;
Expand Down
1 change: 1 addition & 0 deletions Gui/ViewModels/LocoTypes/MusicViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Common;
using Dat.Data;
using Dat.FileParsing;
using Gui.Models;
Expand Down
1 change: 1 addition & 0 deletions Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Common;
using Dat.Converters;
using Dat.Data;
using Dat.FileParsing;
Expand Down
1 change: 1 addition & 0 deletions Gui/ViewModels/LocoTypes/SoundEffectsViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Common;
using Dat.Data;
using Dat.FileParsing;
using Definitions.ObjectModels.Objects.Sound;
Expand Down
Loading