Initial commit: SpdUp Windows Gaming Booster v1.0.0

- .NET 8 WPF app with full MVVM architecture
- One-click boost: CPU, RAM, GPU, network, services, timer resolution
- Real-time system monitor (LibreHardwareMonitor)
- Auto game detection (30+ games)
- RAM optimizer with kernel-level cleanup
- Network optimizer (TCP registry tweaks)
- Service manager, process manager, shader cache cleaner
- Desktop widget overlay
- Debug log viewer
- Custom dark gaming theme with trans pride colors
- 84 unit tests (xUnit)
- Inno Setup installer script
- Landing page website
This commit is contained in:
Phil-icyou
2026-03-08 17:34:45 +01:00
commit 19ba425e79
68 changed files with 6176 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
using System.Windows.Input;
using SpdUp.Core;
using SpdUp.Services;
namespace SpdUp.ViewModels;
/// <summary>
/// Boost page ViewModel.
/// </summary>
public sealed class BoostViewModel : ObservableObject
{
private readonly MainViewModel _main;
private readonly OptimizationService _optimizationService;
private string _boostLog = string.Empty;
public string BoostLog
{
get => _boostLog;
set => SetProperty(ref _boostLog, value);
}
public MainViewModel Main => _main;
public ICommand BoostCommand { get; }
public ICommand CleanRamCommand { get; }
public ICommand CleanShadersCommand { get; }
public BoostViewModel(MainViewModel main, OptimizationService optimizationService)
{
_main = main;
_optimizationService = optimizationService;
BoostCommand = _main.BoostCommand;
CleanRamCommand = _main.CleanRamCommand;
CleanShadersCommand = _main.CleanShaderCacheCommand;
_optimizationService.BoostActivated += msg =>
{
BoostLog = $"[{DateTime.Now:HH:mm:ss}] BOOST ON\n{msg}\n\n{BoostLog}";
};
_optimizationService.BoostDeactivated += () =>
{
BoostLog = $"[{DateTime.Now:HH:mm:ss}] BOOST OFF Restored.\n\n{BoostLog}";
};
}
}
+16
View File
@@ -0,0 +1,16 @@
using SpdUp.Core;
namespace SpdUp.ViewModels;
/// <summary>
/// Dashboard page just binds to MainViewModel's live metrics.
/// </summary>
public sealed class DashboardViewModel : ObservableObject
{
public MainViewModel Main { get; }
public DashboardViewModel(MainViewModel main)
{
Main = main;
}
}
+74
View File
@@ -0,0 +1,74 @@
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Windows.Threading;
using SpdUp.Core;
namespace SpdUp.ViewModels;
/// <summary>
/// Debug Log page live scrolling log of every action the application performs.
/// </summary>
public sealed class DebugLogViewModel : ObservableObject
{
private const int MaxLines = 2000;
private readonly Dispatcher _dispatcher;
public ObservableCollection<string> LogLines { get; } = new();
private string _filterText = string.Empty;
public string FilterText
{
get => _filterText;
set
{
if (SetProperty(ref _filterText, value))
OnPropertyChanged(nameof(FilteredLogLines));
}
}
public IEnumerable<string> FilteredLogLines =>
string.IsNullOrWhiteSpace(_filterText)
? LogLines
: LogLines.Where(l => l.Contains(_filterText, StringComparison.OrdinalIgnoreCase));
private bool _autoScroll = true;
public bool AutoScroll
{
get => _autoScroll;
set => SetProperty(ref _autoScroll, value);
}
public ICommand ClearCommand { get; }
public ICommand CopyAllCommand { get; }
public DebugLogViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
ClearCommand = new RelayCommand(() => { LogLines.Clear(); OnPropertyChanged(nameof(FilteredLogLines)); });
CopyAllCommand = new RelayCommand(() =>
{
try
{
var text = string.Join(Environment.NewLine, LogLines);
System.Windows.Clipboard.SetText(text);
}
catch { }
});
// Subscribe to all log events
Logger.LogWritten += OnLogWritten;
}
private void OnLogWritten(string line)
{
_dispatcher.BeginInvoke(() =>
{
LogLines.Add(line);
while (LogLines.Count > MaxLines)
LogLines.RemoveAt(0);
OnPropertyChanged(nameof(FilteredLogLines));
});
}
}
+72
View File
@@ -0,0 +1,72 @@
using System.Collections.ObjectModel;
using System.Windows.Input;
using Microsoft.Win32;
using SpdUp.Core;
using SpdUp.Models;
using SpdUp.Services;
namespace SpdUp.ViewModels;
/// <summary>
/// Game Library page manage detected and manually-added games.
/// </summary>
public sealed class GameLibraryViewModel : ObservableObject
{
private readonly AppSettings _settings;
private readonly GameDetectionService _detectionService;
public ObservableCollection<GameEntry> Games { get; }
private GameEntry? _selectedGame;
public GameEntry? SelectedGame
{
get => _selectedGame;
set => SetProperty(ref _selectedGame, value);
}
public ICommand AddGameCommand { get; }
public ICommand RemoveGameCommand { get; }
public GameLibraryViewModel(AppSettings settings, GameDetectionService detectionService)
{
_settings = settings;
_detectionService = detectionService;
Games = new ObservableCollection<GameEntry>(_settings.Games);
AddGameCommand = new RelayCommand(AddGame);
RemoveGameCommand = new RelayCommand(RemoveGame, () => SelectedGame is not null);
}
private void AddGame()
{
var dlg = new OpenFileDialog
{
Filter = "Executables (*.exe)|*.exe",
Title = "Select Game Executable"
};
if (dlg.ShowDialog() == true)
{
var entry = new GameEntry
{
Name = System.IO.Path.GetFileNameWithoutExtension(dlg.FileName),
ExecutablePath = dlg.FileName,
ProcessName = GameEntry.ProcessNameFromPath(dlg.FileName),
LauncherSource = "Manual"
};
Games.Add(entry);
_settings.Games.Add(entry);
_detectionService.AddGameProcess(entry.ProcessName);
_settings.Save();
}
}
private void RemoveGame()
{
if (SelectedGame is null) return;
_settings.Games.Remove(SelectedGame);
Games.Remove(SelectedGame);
_settings.Save();
}
}
+264
View File
@@ -0,0 +1,264 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using SpdUp.Core;
using SpdUp.Models;
using SpdUp.Services;
namespace SpdUp.ViewModels;
/// <summary>
/// Root ViewModel drives the main window, navigation, boost, and monitoring.
/// </summary>
public sealed class MainViewModel : ObservableObject, IDisposable
{
// ── Services ───────────────────────────────────────────────────────
private readonly AppSettings _settings;
private readonly OptimizationService _optimizationService;
private readonly GameDetectionService _gameDetectionService;
private readonly MonitoringService _monitoringService;
private readonly NetworkService _networkService = new();
private readonly Dispatcher _dispatcher;
// ── Sub-ViewModels ─────────────────────────────────────────────────
public DashboardViewModel Dashboard { get; }
public BoostViewModel Boost { get; }
public MonitorViewModel Monitor { get; }
public GameLibraryViewModel GameLibrary { get; }
public ProcessManagerViewModel ProcessManager { get; }
public ServiceManagerViewModel ServiceManager { get; }
public SettingsViewModel Settings { get; }
public DebugLogViewModel DebugLog { get; }
// ── Navigation ─────────────────────────────────────────────────────
private object _currentPage;
public object CurrentPage
{
get => _currentPage;
set => SetProperty(ref _currentPage, value);
}
private string _currentPageTitle = "Dashboard";
public string CurrentPageTitle
{
get => _currentPageTitle;
set => SetProperty(ref _currentPageTitle, value);
}
// ── Status ─────────────────────────────────────────────────────────
private string _statusText = "Ready";
public string StatusText
{
get => _statusText;
set => SetProperty(ref _statusText, value);
}
private bool _isBoosted;
public bool IsBoosted
{
get => _isBoosted;
set => SetProperty(ref _isBoosted, value);
}
// ── Live Metrics (bound by views & widget) ─────────────────────────
private float _cpuUsage;
public float CpuUsage { get => _cpuUsage; set => SetProperty(ref _cpuUsage, value); }
private float _ramUsagePercent;
public float RamUsagePercent { get => _ramUsagePercent; set => SetProperty(ref _ramUsagePercent, value); }
private float _ramUsedMB;
public float RamUsedMB { get => _ramUsedMB; set => SetProperty(ref _ramUsedMB, value); }
private float _ramTotalMB;
public float RamTotalMB { get => _ramTotalMB; set => SetProperty(ref _ramTotalMB, value); }
private float _gpuUsage;
public float GpuUsage { get => _gpuUsage; set => SetProperty(ref _gpuUsage, value); }
private float _cpuTemp;
public float CpuTemp { get => _cpuTemp; set => SetProperty(ref _cpuTemp, value); }
private float _gpuTemp;
public float GpuTemp { get => _gpuTemp; set => SetProperty(ref _gpuTemp, value); }
private float _pingMs;
public float PingMs { get => _pingMs; set => SetProperty(ref _pingMs, value); }
// ── Commands ───────────────────────────────────────────────────────
public ICommand NavigateCommand { get; }
public ICommand BoostCommand { get; }
public ICommand ToggleWidgetCommand { get; }
public ICommand CleanRamCommand { get; }
public ICommand CleanShaderCacheCommand { get; }
// ── Widget ─────────────────────────────────────────────────────────
private bool _isWidgetVisible;
public bool IsWidgetVisible
{
get => _isWidgetVisible;
set
{
if (SetProperty(ref _isWidgetVisible, value))
_settings.ShowWidget = value;
}
}
public MainViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
_settings = AppSettings.Load();
// Initialise services
_optimizationService = new OptimizationService(_settings);
_gameDetectionService = new GameDetectionService(_settings);
_monitoringService = new MonitoringService(_settings.PingTarget);
// Sub-ViewModels
Dashboard = new DashboardViewModel(this);
Boost = new BoostViewModel(this, _optimizationService);
Monitor = new MonitorViewModel(this);
GameLibrary = new GameLibraryViewModel(_settings, _gameDetectionService);
ProcessManager = new ProcessManagerViewModel();
ServiceManager = new ServiceManagerViewModel(_settings);
Settings = new SettingsViewModel(_settings, this);
DebugLog = new DebugLogViewModel();
_currentPage = Dashboard;
// Commands
NavigateCommand = new RelayCommand<string>(Navigate);
BoostCommand = new RelayCommand(async () => await ToggleBoostAsync());
ToggleWidgetCommand = new RelayCommand(() => IsWidgetVisible = !IsWidgetVisible);
CleanRamCommand = new RelayCommand(async () => await CleanRamAsync());
CleanShaderCacheCommand = new RelayCommand(async () => await CleanShaderCacheAsync());
// Wire up monitoring
_monitoringService.MetricsUpdated += OnMetricsUpdated;
_monitoringService.Start(_settings.MonitoringIntervalMs);
// Wire up game detection
_gameDetectionService.GameStarted += OnGameStarted;
_gameDetectionService.GameStopped += OnGameStopped;
if (_settings.AutoDetectGames)
_gameDetectionService.Start();
// Widget state
_isWidgetVisible = _settings.ShowWidget;
Logger.PurgeOldLogs();
Logger.Info("Application started.");
}
// ── Navigation ─────────────────────────────────────────────────────
private void Navigate(string? pageName)
{
(CurrentPage, CurrentPageTitle) = pageName switch
{
"Dashboard" => ((object)Dashboard, "Dashboard"),
"Boost" => (Boost, "Game Boost"),
"Monitor" => (Monitor, "System Monitor"),
"Games" => (GameLibrary, "Game Library"),
"Processes" => (ProcessManager, "Process Manager"),
"Services" => (ServiceManager, "Service Manager"),
"Settings" => (Settings, "Settings"),
"Debug" => (DebugLog, "Debug Log"),
_ => (Dashboard, "Dashboard")
};
}
// ── Boost Toggle ───────────────────────────────────────────────────
private async Task ToggleBoostAsync()
{
if (_optimizationService.IsBoosted)
{
StatusText = "Deactivating boost...";
await _optimizationService.DeactivateBoostAsync();
IsBoosted = false;
StatusText = "Boost deactivated settings restored.";
}
else
{
StatusText = "Activating boost...";
var gameProcess = _gameDetectionService.CurrentGameProcess;
var result = await _optimizationService.ActivateBoostAsync(gameProcess);
IsBoosted = result.Success;
StatusText = result.Success ? "🚀 Boost Active" : result.Message;
}
}
// ── Quick Actions ──────────────────────────────────────────────────
private async Task CleanRamAsync()
{
StatusText = "Cleaning RAM...";
var result = await Task.Run(() => new RamOptimizationService().FullCleanup());
StatusText = $"RAM cleaned: {result.TrimmedCount} processes trimmed.";
}
private async Task CleanShaderCacheAsync()
{
StatusText = "Cleaning shader caches...";
var result = await Task.Run(() => ShaderCacheCleanerService.CleanAll());
StatusText = $"Shader cache: {result.FilesDeleted} files, {result.MBFreed:F1} MB freed.";
}
// ── Monitoring Callback ────────────────────────────────────────────
private void OnMetricsUpdated(SystemMetrics m)
{
_dispatcher.BeginInvoke(() =>
{
CpuUsage = m.CpuUsage;
RamUsagePercent = m.RamUsagePercent;
RamUsedMB = m.RamUsageMB;
RamTotalMB = m.RamTotalMB;
GpuUsage = m.GpuUsage;
CpuTemp = m.CpuTemperature;
GpuTemp = m.GpuTemperature;
PingMs = m.NetworkPingMs;
});
}
// ── Game Detection Callbacks ───────────────────────────────────────
#pragma warning disable CS4014 // Fire-and-forget is intentional for event handlers
private async void OnGameStarted(string processName)
{
_dispatcher.BeginInvoke(() => StatusText = $"Game detected: {processName}");
if (_settings.AutoBoostOnGameLaunch && !_optimizationService.IsBoosted)
{
var result = await _optimizationService.ActivateBoostAsync(processName);
_dispatcher.BeginInvoke(() =>
{
IsBoosted = result.Success;
StatusText = result.Success ? $"🚀 Auto-Boost for {processName}" : result.Message;
});
}
}
private async void OnGameStopped(string processName)
{
if (_optimizationService.IsBoosted)
{
await _optimizationService.DeactivateBoostAsync();
_dispatcher.BeginInvoke(() =>
{
IsBoosted = false;
StatusText = $"Game exited: {processName} boost deactivated.";
});
}
}
#pragma warning restore CS4014
public void SaveSettings() => _settings.Save();
public void Dispose()
{
_monitoringService.MetricsUpdated -= OnMetricsUpdated;
_monitoringService.Dispose();
_gameDetectionService.Dispose();
_settings.Save();
Logger.Info("Application closed.");
}
}
+16
View File
@@ -0,0 +1,16 @@
using SpdUp.Core;
namespace SpdUp.ViewModels;
/// <summary>
/// System Monitor page just binds to MainViewModel's live metrics.
/// </summary>
public sealed class MonitorViewModel : ObservableObject
{
public MainViewModel Main { get; }
public MonitorViewModel(MainViewModel main)
{
Main = main;
}
}
+100
View File
@@ -0,0 +1,100 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;
using System.Windows.Threading;
using SpdUp.Core;
using SpdUp.Models;
using SpdUp.Services;
namespace SpdUp.ViewModels;
/// <summary>
/// Process Manager ViewModel.
/// </summary>
public sealed class ProcessManagerViewModel : ObservableObject
{
private readonly DispatcherTimer _refreshTimer;
public ObservableCollection<ProcessEntry> Processes { get; } = new();
private ProcessEntry? _selectedProcess;
public ProcessEntry? SelectedProcess
{
get => _selectedProcess;
set => SetProperty(ref _selectedProcess, value);
}
public ICommand RefreshCommand { get; }
public ICommand KillProcessCommand { get; }
public ICommand SetHighPriorityCommand { get; }
public ICommand SetNormalPriorityCommand { get; }
public ProcessManagerViewModel()
{
RefreshCommand = new RelayCommand(RefreshProcesses);
KillProcessCommand = new RelayCommand(KillSelected, () => SelectedProcess is not null);
SetHighPriorityCommand = new RelayCommand(() => SetPriority(ProcessPriorityClass.High), () => SelectedProcess is not null);
SetNormalPriorityCommand = new RelayCommand(() => SetPriority(ProcessPriorityClass.Normal), () => SelectedProcess is not null);
_refreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(3) };
_refreshTimer.Tick += (_, _) => RefreshProcesses();
RefreshProcesses();
_refreshTimer.Start();
}
private void RefreshProcesses()
{
var procs = Process.GetProcesses()
.Select(p =>
{
try
{
return new ProcessEntry
{
Pid = p.Id,
Name = p.ProcessName,
WindowTitle = string.IsNullOrWhiteSpace(p.MainWindowTitle) ? null : p.MainWindowTitle,
MemoryMB = p.WorkingSet64 / 1048576f,
PriorityClass = TryGetPriority(p),
IsSystem = p.Id <= 4
};
}
catch { return null; }
finally { p.Dispose(); }
})
.Where(e => e is not null)
.OrderByDescending(e => e!.MemoryMB)
.Take(200)
.ToList();
Processes.Clear();
foreach (var p in procs)
Processes.Add(p!);
}
private static string TryGetPriority(Process p)
{
try { return p.PriorityClass.ToString(); }
catch { return "N/A"; }
}
private void KillSelected()
{
if (SelectedProcess is null) return;
OptimizationService.KillProcess(SelectedProcess.Pid);
RefreshProcesses();
}
private void SetPriority(ProcessPriorityClass priority)
{
if (SelectedProcess is null) return;
try
{
using var proc = Process.GetProcessById(SelectedProcess.Pid);
proc.PriorityClass = priority;
}
catch { }
RefreshProcesses();
}
}
+67
View File
@@ -0,0 +1,67 @@
using System.Collections.ObjectModel;
using System.ServiceProcess;
using System.Windows.Input;
using SpdUp.Core;
using SpdUp.Models;
using SpdUp.Services;
namespace SpdUp.ViewModels;
/// <summary>
/// Service Manager ViewModel.
/// </summary>
public sealed class ServiceManagerViewModel : ObservableObject
{
private readonly AppSettings _settings;
public ObservableCollection<ServiceEntry> Services { get; } = new();
private ServiceEntry? _selectedService;
public ServiceEntry? SelectedService
{
get => _selectedService;
set => SetProperty(ref _selectedService, value);
}
public ICommand RefreshCommand { get; }
public ICommand StopServiceCommand { get; }
public ICommand StartServiceCommand { get; }
public ServiceManagerViewModel(AppSettings settings)
{
_settings = settings;
RefreshCommand = new RelayCommand(Refresh);
StopServiceCommand = new RelayCommand(StopSelected, () => SelectedService is not null);
StartServiceCommand = new RelayCommand(StartSelected, () => SelectedService is not null);
Refresh();
}
private void Refresh()
{
Services.Clear();
// Show the configured services plus all known non-critical ones
var names = new HashSet<string>(_settings.ServicesToStop, StringComparer.OrdinalIgnoreCase);
foreach (var k in ServiceManagementService.KnownNonCriticalServices.Keys)
names.Add(k);
foreach (var entry in ServiceManagementService.GetServiceList(names))
Services.Add(entry);
}
private void StopSelected()
{
if (SelectedService is null) return;
ServiceManagementService.StopService(SelectedService.ServiceName);
Refresh();
}
private void StartSelected()
{
if (SelectedService is null) return;
ServiceManagementService.StartService(SelectedService.ServiceName);
Refresh();
}
}
+91
View File
@@ -0,0 +1,91 @@
using System.Windows.Input;
using SpdUp.Core;
using SpdUp.Models;
namespace SpdUp.ViewModels;
/// <summary>
/// Settings page ViewModel.
/// </summary>
public sealed class SettingsViewModel : ObservableObject
{
private readonly AppSettings _settings;
private readonly MainViewModel _main;
public bool AutoDetectGames
{
get => _settings.AutoDetectGames;
set { _settings.AutoDetectGames = value; OnPropertyChanged(); Save(); }
}
public bool AutoBoostOnGameLaunch
{
get => _settings.AutoBoostOnGameLaunch;
set { _settings.AutoBoostOnGameLaunch = value; OnPropertyChanged(); Save(); }
}
public bool MinimizeToTray
{
get => _settings.MinimizeToTray;
set { _settings.MinimizeToTray = value; OnPropertyChanged(); Save(); }
}
public bool StartWithWindows
{
get => _settings.StartWithWindows;
set { _settings.StartWithWindows = value; OnPropertyChanged(); Save(); }
}
public bool WidgetCompactMode
{
get => _settings.WidgetCompactMode;
set { _settings.WidgetCompactMode = value; OnPropertyChanged(); Save(); }
}
public double WidgetOpacity
{
get => _settings.WidgetOpacity;
set { _settings.WidgetOpacity = value; OnPropertyChanged(); Save(); }
}
public int WidgetUpdateInterval
{
get => _settings.WidgetUpdateIntervalMs;
set { _settings.WidgetUpdateIntervalMs = value; OnPropertyChanged(); Save(); }
}
public bool WidgetClickThrough
{
get => _settings.WidgetClickThrough;
set { _settings.WidgetClickThrough = value; OnPropertyChanged(); Save(); }
}
public int MonitoringInterval
{
get => _settings.MonitoringIntervalMs;
set { _settings.MonitoringIntervalMs = value; OnPropertyChanged(); Save(); }
}
public string PingTarget
{
get => _settings.PingTarget;
set { _settings.PingTarget = value; OnPropertyChanged(); Save(); }
}
public string PreferredDns
{
get => _settings.PreferredDns;
set { _settings.PreferredDns = value; OnPropertyChanged(); Save(); }
}
public ICommand SaveCommand { get; }
public SettingsViewModel(AppSettings settings, MainViewModel main)
{
_settings = settings;
_main = main;
SaveCommand = new RelayCommand(Save);
}
private void Save() => _settings.Save();
}