mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
Leave shaders/tonemapping.glsl alone and validate the sim executable
Shaders: drop ApplyHdrShader and FindBundled. The Pascal starter only copied starter/Reinhard.glsl over shaders/tonemapping.glsl when the user picked an operator in the HDR combo (uSettings.pas ChangeHDR, its only caller being cbHDRChange), never on an ordinary save. We ran it from SaveTo, so every save replaced the file the game ships. That file has been tracked in the game repo since 2020 and now defaults to AgX, so overwriting it with a 2021-vintage Reinhard was a two-generation downgrade that also lost the NaN/Inf sanitize. Also comments out gfx.postfx.tonemapping on save; the engine has no such key. Executable: ResolveExecutable now validates its pick (file exists, has an execute bit outside Windows, and the MZ/ELF header matches the running OS) and returns the first candidate that passes instead of the first that matches by name. .exe is no longer a Linux candidate - a Steam install with the native binary still zipped would otherwise resolve to the Windows PE. On failure the problem reaches the caller, and MainWindow shows it instead of swallowing the exception, so a launch that does nothing is no longer silent. A manual pick whose file is temporarily missing is kept in the combo rather than falling back to auto and being overwritten on the next save. Claude-Session: https://claude.ai/code/session_01HamVNWE9mwmz1vs2iEpNQq
This commit is contained in:
@@ -9,6 +9,8 @@ namespace StarterNG.Classes;
|
||||
|
||||
public enum BatteryDefault { Default = 0, AlwaysOff = 1, AlwaysOn = 2 }
|
||||
|
||||
public enum ExeProblem { None, NotFound, NotExecutable, WrongPlatform }
|
||||
|
||||
public sealed class Settings
|
||||
{
|
||||
public static Settings Instance { get; } = new();
|
||||
@@ -114,7 +116,6 @@ public sealed class Settings
|
||||
public bool DrivableOnly = true;
|
||||
public bool ShowArchivalSceneries = true;
|
||||
public bool HideArchivalVehicles = true;
|
||||
|
||||
public string LastScenery = "";
|
||||
|
||||
public static readonly string[] RenderEngines =
|
||||
@@ -159,33 +160,87 @@ public sealed class Settings
|
||||
public static string UserConfigDirectory() =>
|
||||
Path.GetDirectoryName(UserConfigPath()) ?? AppContext.BaseDirectory;
|
||||
|
||||
public string ResolveExecutable()
|
||||
public string ResolveExecutable() => ResolveExecutable(out _);
|
||||
|
||||
public string ResolveExecutable(out ExeProblem problem)
|
||||
{
|
||||
if (!SelectExeAutomatically && !string.IsNullOrWhiteSpace(ExecutablePath))
|
||||
{
|
||||
problem = ValidateExecutable(FullPath(ExecutablePath));
|
||||
return ExecutablePath;
|
||||
}
|
||||
|
||||
string canonical = OperatingSystem.IsWindows() ? "eu07.exe" : "eu07";
|
||||
string? fallback = null;
|
||||
try
|
||||
{
|
||||
string? best = null;
|
||||
foreach (string path in Directory.GetFiles(Directory.GetCurrentDirectory(), "eu07*"))
|
||||
var candidates = Directory.GetFiles(Directory.GetCurrentDirectory(), "eu07*")
|
||||
.Where(IsExecutableCandidate)
|
||||
.OrderBy(p => string.Equals(Path.GetFileName(p), canonical, StringComparison.OrdinalIgnoreCase) ? 0 : 1)
|
||||
.ThenBy(p => Path.GetFileName(p)!.Length);
|
||||
|
||||
foreach (string path in candidates)
|
||||
{
|
||||
if (!IsExecutableCandidate(path))
|
||||
continue;
|
||||
string file = Path.GetFileName(path);
|
||||
if (string.Equals(file, canonical, StringComparison.OrdinalIgnoreCase))
|
||||
if (ValidateExecutable(path) == ExeProblem.None)
|
||||
{
|
||||
problem = ExeProblem.None;
|
||||
return path;
|
||||
if (best == null || file.Length < Path.GetFileName(best).Length)
|
||||
best = path;
|
||||
}
|
||||
fallback ??= path;
|
||||
}
|
||||
if (best != null)
|
||||
return best;
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (fallback != null)
|
||||
{
|
||||
problem = ValidateExecutable(fallback);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
problem = ExeProblem.NotFound;
|
||||
return canonical;
|
||||
}
|
||||
|
||||
private static string FullPath(string path) =>
|
||||
Path.IsPathRooted(path) ? path : Path.Combine(Directory.GetCurrentDirectory(), path);
|
||||
|
||||
public static ExeProblem ValidateExecutable(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return ExeProblem.NotFound;
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
const UnixFileMode anyExecute =
|
||||
UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute;
|
||||
if ((File.GetUnixFileMode(path) & anyExecute) == 0)
|
||||
return ExeProblem.NotExecutable;
|
||||
}
|
||||
|
||||
byte[] head = new byte[4];
|
||||
using (var fs = File.OpenRead(path))
|
||||
{
|
||||
if (fs.Read(head, 0, 4) == 4)
|
||||
{
|
||||
bool pe = head[0] == 0x4D && head[1] == 0x5A;
|
||||
bool elf = head[0] == 0x7F && head[1] == 0x45 && head[2] == 0x4C && head[3] == 0x46;
|
||||
if (OperatingSystem.IsWindows() && elf)
|
||||
return ExeProblem.WrongPlatform;
|
||||
if (OperatingSystem.IsLinux() && pe)
|
||||
return ExeProblem.WrongPlatform;
|
||||
}
|
||||
}
|
||||
|
||||
return ExeProblem.None;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return ExeProblem.NotFound;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<string> ListCandidateExecutables(string? directory = null)
|
||||
{
|
||||
directory ??= Directory.GetCurrentDirectory();
|
||||
@@ -208,6 +263,8 @@ public sealed class Settings
|
||||
return false;
|
||||
if (OperatingSystem.IsWindows())
|
||||
return ext == ".exe";
|
||||
if (OperatingSystem.IsLinux())
|
||||
return ext.Length == 0 || ext is ".x86_64" or ".run" or ".appimage";
|
||||
|
||||
return ext.Length == 0 || ext is ".exe" or ".x86_64" or ".run" or ".appimage";
|
||||
}
|
||||
@@ -270,7 +327,6 @@ public sealed class Settings
|
||||
{
|
||||
WriteToConfig();
|
||||
ApplyFramebufferFidelity();
|
||||
ApplyHdrShader();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -331,39 +387,6 @@ public sealed class Settings
|
||||
_config.Remove("gfx.framebuffer.fidelity");
|
||||
}
|
||||
|
||||
public void ApplyHdrShader()
|
||||
{
|
||||
|
||||
if (RenderEngine is not (0 or 1 or 5))
|
||||
return;
|
||||
|
||||
string? src = FindBundled("Reinhard.glsl");
|
||||
if (src is null) return;
|
||||
|
||||
try
|
||||
{
|
||||
string destDir = Path.Combine(Directory.GetCurrentDirectory(), "shaders");
|
||||
Directory.CreateDirectory(destDir);
|
||||
File.Copy(src, Path.Combine(destDir, "tonemapping.glsl"), overwrite: true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static string? FindBundled(string fileName)
|
||||
{
|
||||
foreach (string dir in new[]
|
||||
{
|
||||
Path.Combine(Directory.GetCurrentDirectory(), "starter"),
|
||||
Path.Combine(AppContext.BaseDirectory, "startercfg"),
|
||||
Path.Combine(Directory.GetCurrentDirectory(), "startercfg")
|
||||
})
|
||||
{
|
||||
string path = Path.Combine(dir, fileName);
|
||||
if (File.Exists(path)) return path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DumpMissingVehicleLog()
|
||||
{
|
||||
if (!LogMissingVehicleFiles) return;
|
||||
@@ -506,10 +529,18 @@ public sealed class Settings
|
||||
LastScenery = c.GetString("starter.last.scenery", "");
|
||||
}
|
||||
|
||||
private static readonly string[] ObsoleteKeys =
|
||||
{
|
||||
"gfx.postfx.tonemapping",
|
||||
};
|
||||
|
||||
private void WriteToConfig()
|
||||
{
|
||||
var c = _config;
|
||||
|
||||
foreach (string key in ObsoleteKeys)
|
||||
c.Remove(key);
|
||||
|
||||
c.Set("lang", Language == "Polski" ? "pl" : "en");
|
||||
c.SetBool("fullscreen", Fullscreen);
|
||||
c.SetBool("inactivepause", PauseWhenInactive);
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
@@ -223,6 +224,18 @@ public partial class MainWindow : Window
|
||||
return trainset.Vehicles.Any(CanStart);
|
||||
}
|
||||
|
||||
private Task<bool> ShowExeProblem(ExeProblem problem, string exe)
|
||||
{
|
||||
string key = problem switch
|
||||
{
|
||||
ExeProblem.NotExecutable => "ExeNotExecutable",
|
||||
ExeProblem.WrongPlatform => "ExeWrongPlatform",
|
||||
_ => "ExeNotFound"
|
||||
};
|
||||
return MessageBox.Show(this, $"{exe}\n\n{App.Loc[key]}",
|
||||
App.Loc["ExeLaunchFailed"], MessageBoxButtons.Ok);
|
||||
}
|
||||
|
||||
private async void StartButton_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var scenery = AppState.Instance.CurrentScenery;
|
||||
@@ -271,10 +284,16 @@ public partial class MainWindow : Window
|
||||
|
||||
SettingsModel.Instance.CaptureAndSave();
|
||||
|
||||
string exe = Path.GetFullPath(SettingsModel.Instance.ResolveExecutable(out var exeProblem));
|
||||
if (exeProblem != ExeProblem.None)
|
||||
{
|
||||
await ShowExeProblem(exeProblem, exe);
|
||||
return;
|
||||
}
|
||||
|
||||
Process? sim;
|
||||
try
|
||||
{
|
||||
string exe = Path.GetFullPath(SettingsModel.Instance.ResolveExecutable());
|
||||
sim = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = exe,
|
||||
@@ -283,8 +302,10 @@ public partial class MainWindow : Window
|
||||
UseShellExecute = false
|
||||
});
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MessageBox.Show(this, $"{exe}\n\n{ex.Message}",
|
||||
App.Loc["ExeLaunchFailed"], MessageBoxButtons.Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace StarterNG.Views;
|
||||
|
||||
public enum MessageBoxButtons
|
||||
{
|
||||
YesNo
|
||||
YesNo,
|
||||
Ok
|
||||
}
|
||||
|
||||
public static class MessageBox
|
||||
{
|
||||
|
||||
public static async Task<bool> Show(Window owner, string message, string title, MessageBoxButtons _)
|
||||
public static async Task<bool> Show(Window owner, string message, string title, MessageBoxButtons buttons)
|
||||
{
|
||||
var win = new Window
|
||||
{
|
||||
@@ -33,6 +33,12 @@ public static class MessageBox
|
||||
noBtn.Classes.Add("Flat");
|
||||
noBtn.Click += (_, _) => win.Close();
|
||||
|
||||
if (buttons == MessageBoxButtons.Ok)
|
||||
{
|
||||
yesBtn.Content = App.Loc["Ok"];
|
||||
noBtn.IsVisible = false;
|
||||
}
|
||||
|
||||
win.Content = new StackPanel
|
||||
{
|
||||
Margin = new Avalonia.Thickness(16),
|
||||
|
||||
@@ -158,9 +158,20 @@ public partial class Settings : UserControl
|
||||
UartRadioChannelCb.IsChecked = s.UartRadioChannel;
|
||||
UpdateFeedbackDetailVisibility();
|
||||
|
||||
SelectExeCb.SelectedIndex = s.SelectExeAutomatically
|
||||
? 0
|
||||
: Math.Max(0, FindComboIndexByContent(SelectExeCb, s.ExecutablePath));
|
||||
if (s.SelectExeAutomatically)
|
||||
{
|
||||
SelectExeCb.SelectedIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int exeIdx = FindComboIndexByContent(SelectExeCb, s.ExecutablePath);
|
||||
if (exeIdx < 0)
|
||||
{
|
||||
SelectExeCb.Items.Add(new ComboBoxItem { Content = s.ExecutablePath });
|
||||
exeIdx = SelectExeCb.Items.Count - 1;
|
||||
}
|
||||
SelectExeCb.SelectedIndex = exeIdx;
|
||||
}
|
||||
DebugModeCb.IsChecked = s.DebugMode;
|
||||
VirtualShuntingCb.IsChecked = s.VirtualShunting;
|
||||
LogMissingVehicleFilesCb.IsChecked = s.LogMissingVehicleFiles;
|
||||
|
||||
@@ -222,6 +222,11 @@
|
||||
<String key="Cancel">Zrušit</String>
|
||||
<String key="Yes">Ano</String>
|
||||
<String key="No">Ne</String>
|
||||
<String key="Ok">OK</String>
|
||||
<String key="ExeLaunchFailed">Nepodařilo se spustit simulátor</String>
|
||||
<String key="ExeNotFound">Spustitelný soubor simulátoru nebyl nalezen.</String>
|
||||
<String key="ExeNotExecutable">Soubor není označen jako spustitelný (chmod +x).</String>
|
||||
<String key="ExeWrongPlatform">Tento binární soubor je pro jiný operační systém.</String>
|
||||
<String key="TabInfo">Informace</String>
|
||||
<String key="SettingsFileChangedTitle">Nastavení</String>
|
||||
<String key="SettingsFileChanged">Soubor eu07.ini byl změněn mimo starter. Načíst jej znovu?</String>
|
||||
|
||||
@@ -222,6 +222,11 @@
|
||||
<String key="Cancel">Cancel</String>
|
||||
<String key="Yes">Yes</String>
|
||||
<String key="No">No</String>
|
||||
<String key="Ok">OK</String>
|
||||
<String key="ExeLaunchFailed">Could not start the simulator</String>
|
||||
<String key="ExeNotFound">The simulator executable was not found.</String>
|
||||
<String key="ExeNotExecutable">The file is not marked as executable (chmod +x).</String>
|
||||
<String key="ExeWrongPlatform">This binary is built for a different operating system.</String>
|
||||
<String key="TabInfo">Info</String>
|
||||
<String key="SettingsFileChangedTitle">Settings</String>
|
||||
<String key="SettingsFileChanged">eu07.ini was changed outside the starter. Reload it?</String>
|
||||
|
||||
@@ -222,6 +222,11 @@
|
||||
<String key="Cancel">Mégse</String>
|
||||
<String key="Yes">Igen</String>
|
||||
<String key="No">Nem</String>
|
||||
<String key="Ok">OK</String>
|
||||
<String key="ExeLaunchFailed">A szimulátor indítása sikertelen</String>
|
||||
<String key="ExeNotFound">A szimulátor futtatható fájlja nem található.</String>
|
||||
<String key="ExeNotExecutable">A fájl nincs futtathatóként megjelölve (chmod +x).</String>
|
||||
<String key="ExeWrongPlatform">Ez a bináris másik operációs rendszerhez készült.</String>
|
||||
<String key="TabInfo">Információ</String>
|
||||
<String key="SettingsFileChangedTitle">Beállítások</String>
|
||||
<String key="SettingsFileChanged">Az eu07.ini fájl a starteren kívül módosult. Újratölti?</String>
|
||||
|
||||
@@ -222,6 +222,11 @@
|
||||
<String key="Cancel">Anuluj</String>
|
||||
<String key="Yes">Tak</String>
|
||||
<String key="No">Nie</String>
|
||||
<String key="Ok">OK</String>
|
||||
<String key="ExeLaunchFailed">Nie udało się uruchomić symulatora</String>
|
||||
<String key="ExeNotFound">Nie znaleziono pliku wykonywalnego symulatora.</String>
|
||||
<String key="ExeNotExecutable">Plik nie ma prawa wykonywania (chmod +x).</String>
|
||||
<String key="ExeWrongPlatform">To jest plik wykonywalny dla innego systemu niż uruchomiony.</String>
|
||||
<String key="TabInfo">Informacje</String>
|
||||
<String key="SettingsFileChangedTitle">Ustawienia</String>
|
||||
<String key="SettingsFileChanged">Plik eu07.ini został zmieniony poza starterem. Wczytać ponownie?</String>
|
||||
|
||||
@@ -222,6 +222,11 @@
|
||||
<String key="Cancel">Отмена</String>
|
||||
<String key="Yes">Да</String>
|
||||
<String key="No">Нет</String>
|
||||
<String key="Ok">OK</String>
|
||||
<String key="ExeLaunchFailed">Не удалось запустить симулятор</String>
|
||||
<String key="ExeNotFound">Исполняемый файл симулятора не найден.</String>
|
||||
<String key="ExeNotExecutable">Файл не помечен как исполняемый (chmod +x).</String>
|
||||
<String key="ExeWrongPlatform">Этот исполняемый файл собран для другой операционной системы.</String>
|
||||
<String key="TabInfo">Информация</String>
|
||||
<String key="SettingsFileChangedTitle">Настройки</String>
|
||||
<String key="SettingsFileChanged">Файл eu07.ini изменён вне стартера. Загрузить заново?</String>
|
||||
|
||||
Reference in New Issue
Block a user