Skip to content
Open
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
127 changes: 127 additions & 0 deletions RetroBat/RetroBat/AutostartManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.IO;
using Microsoft.Win32;

namespace RetroBat
{
internal static class AutostartManager
{
public static void CleanupLegacyShortcut()
{
try
{
string startupFolder = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
string linkStartup = Path.Combine(startupFolder, "RetroBat.lnk");

if (File.Exists(linkStartup))
{
try { File.Delete(linkStartup); }
catch (Exception ex) { SimpleLogger.Instance.Warning("Failed to delete legacy RetroBat.lnk: " + ex.Message); }
}
}
catch (Exception ex) { SimpleLogger.Instance.Warning("CleanupStartup failed: " + ex.Message); }
}

public static void Apply(int autostartMode, string appFolder, string appExe)
{
if (autostartMode == 1)
{
AddToStartupFolder(appFolder, appExe);
RemoveFromStartupReg();
}
else if (autostartMode == 2)
{
AddToStartupReg(appFolder, appExe);
RemoveFromStartupFolder("RetroBat");
}
else
{
RemoveFromStartupReg();
RemoveFromStartupFolder("RetroBat");
}
}

private static void AddToStartupReg(string appPath, string appExe)
{
SimpleLogger.Instance.Info("Setting RetroBat to launch at startup.");

string batPath = Path.Combine(appPath, appExe);

string regValue = string.Format(
"cmd.exe /c \"cd /d {0} && start \"\" \"{1}\"\"\"",
appPath,
batPath
);

try
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
key.SetValue("RetroBat", regValue);
SimpleLogger.Instance.Info("RetroBat set in registry to startup.");
}
catch (Exception ex)
{
SimpleLogger.Instance.Warning("Failed to set startup registry key: " + ex.Message);
}
}

private static void AddToStartupFolder(string exePath, string shortcutName)
{
try
{
string startupFolder = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
string exeName = Path.GetFileNameWithoutExtension(shortcutName);
string batPath = Path.Combine(startupFolder, exeName + ".bat");
string exe = Path.Combine(exePath, shortcutName);

// Write a simple batch file to start RetroBat
string batContent = $"@echo off{Environment.NewLine}cd /d \"{exePath}\"{Environment.NewLine}\"{exe}\"";
File.WriteAllText(batPath, batContent);

SimpleLogger.Instance.Info("RetroBat batch added to Startup folder: " + batPath);
}
catch (Exception ex)
{
SimpleLogger.Instance.Warning("Failed to add RetroBat to Startup folder: " + ex.Message);
}
}

private static void RemoveFromStartupFolder(string shortcutName)
{
try
{
string startupFolder = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
string batPath = Path.Combine(startupFolder, shortcutName + ".bat");

if (File.Exists(batPath))
{
File.Delete(batPath);
SimpleLogger.Instance.Info("RetroBat removed from Startup folder: " + batPath);
}
else
{
SimpleLogger.Instance.Info("RetroBat startup batch not found, nothing to remove.");
}
}
catch (Exception ex)
{
SimpleLogger.Instance.Warning("Failed to remove RetroBat from Startup folder: " + ex.Message);
}
}

private static void RemoveFromStartupReg()
{
SimpleLogger.Instance.Info("Ensuring RetroBat does not launch at startup.");

try
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
key.DeleteValue("RetroBat");
}
catch (Exception ex)
{
SimpleLogger.Instance.Warning("Failed to remove startup registry key: " + ex.Message);
}
}
}
}
95 changes: 95 additions & 0 deletions RetroBat/RetroBat/DpiAwarenessManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Win32;

namespace RetroBat
{
internal static class DpiAwarenessManager
{
public static void ApplyOverridesIfNeeded(string appFolder)
{
if (!HasDpiScaling())
return;

string dpiFile = Path.Combine(appFolder, "system", "tools", "dpi_awareness.txt");

if (!File.Exists(dpiFile))
return;

try
{
var dpiLines = File.ReadAllLines(dpiFile);

if (dpiLines.Length > 0)
{
foreach (var dpiLine in dpiLines)
{
string dpiExePath = Path.Combine(appFolder, dpiLine.Trim());

if (File.Exists(dpiExePath))
SetDpiAwarenessOverride(dpiExePath, true);
}
}
}
catch (Exception ex) { SimpleLogger.Instance.Warning("Failed to apply DPI awareness overrides: " + ex.Message); }
}

public static bool HasDpiScaling()
{
using (var key = Registry.LocalMachine.OpenSubKey(
@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI"))
{
object val = key != null ? key.GetValue("LogPixels") : null;
if (val is int dpi)
return dpi != 96;
}

using (var key = Registry.CurrentUser.OpenSubKey(
@"Control Panel\Desktop"))
{
object val = key != null ? key.GetValue("LogPixels") : null;
if (val is int dpi)
return dpi != 96;
}

return false;
}

public static void SetDpiAwarenessOverride(string exePath, bool enable)
{
const string keyPath = @"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers";

RegistryKey key = Registry.CurrentUser.OpenSubKey(keyPath, true)
?? Registry.CurrentUser.CreateSubKey(keyPath);

if (key == null)
return;

using (key)
{
string current = key.GetValue(exePath) as string ?? string.Empty;

var flags = new HashSet<string>(current.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));

if (enable)
{
if (flags.Contains("HIGHDPIAWARE"))
return;
flags.Add("HIGHDPIAWARE");
}
else
{
if (!flags.Contains("HIGHDPIAWARE"))
return;
flags.Remove("HIGHDPIAWARE");
}

if (flags.Count == 0)
key.DeleteValue(exePath, false);
else
key.SetValue(exePath, string.Join(" ", flags), RegistryValueKind.String);
}
}
}
}
165 changes: 165 additions & 0 deletions RetroBat/RetroBat/EmulationStationLauncher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace RetroBat
{
internal static class EmulationStationLauncher
{
public static string BuildArguments(RetroBatConfig config, string esPath, Screen[] screens)
{
List<string> commandArray = new List<string>();

bool borderless = config.FullscreenBorderless;

if (config.Fullscreen && config.ForceFullscreenRes)
{
commandArray.Add("--resolution");
commandArray.Add(config.WindowXSize.ToString());
commandArray.Add(config.WindowYSize.ToString());
}
else if (!config.Fullscreen && !borderless)
{
commandArray.Add("--windowed");
commandArray.Add("--resolution");
commandArray.Add(config.WindowXSize.ToString());
commandArray.Add(config.WindowYSize.ToString());
}
else if (borderless)
{
commandArray.Add("--fullscreen-borderless");
}
else
{
commandArray.Add("--fullscreen");
}

if (config.GameListOnly)
commandArray.Add("--gamelist-only");

if (config.InterfaceMode == 2)
commandArray.Add("--force-kid");
else if (config.InterfaceMode == 1)
commandArray.Add("--force-kiosk");

if (config.MonitorIndex > 0 && config.MonitorIndex < screens.Length)
{
commandArray.Add("--monitor");
commandArray.Add(config.MonitorIndex.ToString());
}

if (config.NoExitMenu)
commandArray.Add("--no-exit");

if (config.VSync)
commandArray.Add("--vsync 1");
else
commandArray.Add("--vsync 0");

if (config.DrawFramerate)
commandArray.Add("--draw-framerate");

commandArray.Add("--home");
commandArray.Add(esPath);

return string.Join(" ", commandArray.Select(a => a.Contains(" ") ? "\"" + a + "\"" : a));
}

public static void RunWiimoteGun(string esPath)
{
SimpleLogger.Instance.Info("Running WiimoteGun.");

string wgunExe = Path.Combine(esPath, "WiimoteGun.exe");

if (!File.Exists(wgunExe))
{
SimpleLogger.Instance.Warning("WiimoteGun executable not found at: " + wgunExe);
return;
}

try
{
var wgStart = new ProcessStartInfo
{
FileName = wgunExe,
WorkingDirectory = esPath,
UseShellExecute = false,
CreateNoWindow = true
};

Process.Start(wgStart);
SimpleLogger.Instance.Info("WiimoteGun started successfully.");
}
catch (Exception ex) { SimpleLogger.Instance.Warning("Failed to start WiimoteGun: " + ex.Message); }
}

/// <summary>Starts EmulationStation and waits for/restores focus on its window. Returns false if the process failed to start.</summary>
public static bool LaunchAndFocus(ProcessStartInfo start, RetroBatConfig config, bool isExternalLauncher)
{
try
{
SimpleLogger.Instance.Info("Launching " + start.FileName + " " + start.Arguments);

var exe = Process.Start(start);
if (exe == null)
{
SimpleLogger.Instance.Error("Failed to start EmulationStation process.");
return false;
}

int maxWaitMs = 10000;
int intervalMs = 50;
int waited = 0;

IntPtr esHandle = IntPtr.Zero;

SimpleLogger.Instance.Info("Waiting for EmulationStation main window…");
while (!exe.HasExited && esHandle == IntPtr.Zero && waited < maxWaitMs)
{
Thread.Sleep(intervalMs);
waited += intervalMs;
exe.Refresh();
esHandle = exe.MainWindowHandle;

if (waited % 1000 == 0)
SimpleLogger.Instance.Info($"…still waiting ({waited / 1000}s)");
}

if (esHandle == IntPtr.Zero)
{
SimpleLogger.Instance.Warning("EmulationStation window handle not detected (likely exclusive fullscreen). Skipping focus.");
}

if (esHandle != IntPtr.Zero && !isExternalLauncher)
{
SplashVideo.CloseBlackSplash();
Thread.Sleep(300);

if (config.FocusDelay > 0)
{
Thread.Sleep(config.FocusDelay);
}

FocusHelper.BringProcessWindowToFront(exe);
}
else
{
if (exe.HasExited)
SimpleLogger.Instance.Error("EmulationStation process exited before creating a window.");
else
SimpleLogger.Instance.Warning("EmulationStation process is running but no main window detected.");
}
}
catch (Exception ex)
{
SimpleLogger.Instance.Warning("Failed to start EmulationStation: " + ex.Message);
}

return true;
}
}
}
Loading