From 351cd25b85526f0076a0062f282af99a0095bf63 Mon Sep 17 00:00:00 2001
From: Jaredl-Dev <260281143+Jaredl-Dev@users.noreply.github.com>
Date: Sun, 14 Jun 2026 21:27:49 -0700
Subject: [PATCH] Add game install location picker
Add a warning when installing into Program Files folders on Windows.
---
.../Popups/InstallGamePopup.xaml | 33 +++++-
.../Popups/InstallGamePopup.xaml.cs | 94 ++++++++++-----
.../Dictionary/LanguageResources.ar.xaml | 2 +
.../Dictionary/LanguageResources.de.xaml | 2 +
.../Dictionary/LanguageResources.en.xaml | 4 +-
.../Dictionary/LanguageResources.es.xaml | 2 +
.../Dictionary/LanguageResources.fr.xaml | 2 +
.../Dictionary/LanguageResources.hu.xaml | 2 +
.../Dictionary/LanguageResources.it.xaml | 2 +
.../Dictionary/LanguageResources.nl.xaml | 2 +
.../Dictionary/LanguageResources.no.xaml | 2 +
.../Dictionary/LanguageResources.pl.xaml | 2 +
.../Dictionary/LanguageResources.ru.xaml | 2 +
.../Dictionary/LanguageResources.sv.xaml | 2 +
.../Dictionary/LanguageResources.tr.xaml | 4 +-
.../Utils/InstallLocationUtils.cs | 109 ++++++++++++++++++
16 files changed, 230 insertions(+), 36 deletions(-)
create mode 100644 src/BfmeFoundationProject_AllInOneLauncher/Utils/InstallLocationUtils.cs
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml
index 31ce196..e7e4e24 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml
@@ -8,7 +8,7 @@
xmlns:system="clr-namespace:System;assembly=netstandard"
xmlns:generic="clr-namespace:BfmeFoundationProject.AllInOneLauncher.Elements.Generic"
mc:Ignorable="d"
- Width="700" HorizontalAlignment="Center" VerticalAlignment="Center">
+ Width="840" HorizontalAlignment="Center" VerticalAlignment="Center">
@@ -62,9 +62,34 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml.cs b/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml.cs
index 38815dd..574115d 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml.cs
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Popups/InstallGamePopup.xaml.cs
@@ -1,48 +1,82 @@
-using System;
-using System.Collections.Generic;
+using System;
using System.IO;
-using System.Linq;
using System.Windows;
-using BfmeFoundationProject.AllInOneLauncher.Elements.Disk;
+using BfmeFoundationProject.AllInOneLauncher.Core.Utils;
using BfmeFoundationProject.AllInOneLauncher.Elements.Generic;
+using Microsoft.Win32;
namespace BfmeFoundationProject.AllInOneLauncher.Popups;
public partial class InstallGamePopup : PopupBody
{
- private static readonly Dictionary Drives = DriveInfo.GetDrives().ToDictionary(x => x.RootDirectory.FullName);
+ private string selectedLocation = "";
public InstallGamePopup()
{
InitializeComponent();
- locations.Children.Clear();
- foreach (var drive in Drives.Values)
+ SetSelectedLocation(InstallLocationUtils.GetDefaultDriveRoot());
+ }
+
+ private void OnBrowseLocationClicked(object sender, RoutedEventArgs e)
+ {
+ OpenFolderDialog dialog = new()
{
- if (!drive.IsReady)
- continue;
-
- try
- {
- locations.Children.Add(new Selectable()
- {
- Title = new LibraryDriveHeader()
- {
- LibraryDriveName = string.Concat(drive.VolumeLabel, " (", drive.Name.Replace(@"\", ""), ")"),
- LibraryDriveSize = $"{Math.Floor(drive.AvailableFreeSpace / Math.Pow(1024, 3)):N0} GB {App.Current.FindResource("GenericFree")}",
- Mini = true
- },
- Tag = drive.RootDirectory.FullName,
- Margin = new Thickness(0, 0, 0, 5),
- UseLayoutRounding = true,
- SnapsToDevicePixels = true
- });
- }
- catch { }
- }
+ Multiselect = false
+ };
+
+ if (Directory.Exists(selectedLocation))
+ dialog.InitialDirectory = selectedLocation;
+ else if (InstallLocationUtils.TryGetDrive(selectedLocation, out DriveInfo? drive) && drive != null)
+ dialog.InitialDirectory = drive.RootDirectory.FullName;
+
+ if (dialog.ShowDialog() == true)
+ SetSelectedLocation(dialog.FolderName);
}
- private void OnInstallClicked(object sender, RoutedEventArgs e) => Submit(LanguageDropdown.SelectedValue, Selectable.GetSelectedTagInContainer(locations)!.ToString()!);
+ private void OnInstallClicked(object sender, RoutedEventArgs e)
+ {
+ if (InstallLocationUtils.IsValidLocation(selectedLocation))
+ Submit(LanguageDropdown.SelectedValue, selectedLocation);
+ }
private void OnCancelClicked(object sender, RoutedEventArgs e) => Dismiss();
-}
\ No newline at end of file
+
+ private void SetSelectedLocation(string location)
+ {
+ selectedLocation = InstallLocationUtils.NormalizeLocation(location);
+
+ SelectedLocationText.Text = selectedLocation;
+ SelectedLocationText.ToolTip = selectedLocation;
+
+ ButtonAccept.IsEnabled = InstallLocationUtils.IsValidLocation(selectedLocation);
+ UpdateDriveSpace();
+ UpdateWarning();
+ }
+
+ private void UpdateDriveSpace()
+ {
+ SelectedLocationDriveFreeSpace.Text = "";
+
+ if (!InstallLocationUtils.TryGetDrive(selectedLocation, out DriveInfo? drive) || drive == null)
+ return;
+
+ try
+ {
+ if (drive.IsReady)
+ SelectedLocationDriveFreeSpace.Text = $"{Math.Floor(drive.AvailableFreeSpace / Math.Pow(1024, 3)):N0} GB {App.Current.FindResource("GenericFree")}";
+ }
+ catch
+ {
+ SelectedLocationDriveFreeSpace.Text = "";
+ }
+ }
+
+ private void UpdateWarning()
+ {
+ bool showWarning = InstallLocationUtils.IsInProgramFilesFolder(selectedLocation);
+
+ LocationWarningText.Text = showWarning ? App.Current.FindResource("InstallGamePopupProgramFilesWarning")?.ToString() ?? "" : "";
+ LocationWarning.Visibility = showWarning ? Visibility.Visible : Visibility.Collapsed;
+ }
+}
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ar.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ar.xaml
index f95783a..8502819 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ar.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ar.xaml
@@ -13,6 +13,7 @@
تحميل
تشغيل
إضافة
+ استعراض
مجاني
تأكيد
نعم
@@ -113,6 +114,7 @@
تثبيت اللعبة
اللغة
الموقع
+ من المستحسن التثبيت خارج مجلدات ملفات البرامج لتجنب مشكلات الأذونات المحتملة.
إضافة إلى المكتبة
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.de.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.de.xaml
index fe20433..bf22f18 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.de.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.de.xaml
@@ -15,6 +15,7 @@
LADEN
SPIELEN
HINZUFÜGEN
+ DURCHSUCHEN
FREI
BESTÄTIGEN
JA
@@ -124,6 +125,7 @@
SPIEL INSTALLIEREN
Sprache
Standort
+ Es wird empfohlen, außerhalb der Programme-Ordner zu installieren, um mögliche Berechtigungsprobleme zu vermeiden.
ZUR BIBLIOTHEK HINZUFÜGEN
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.en.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.en.xaml
index cdb49f0..757541f 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.en.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.en.xaml
@@ -15,6 +15,7 @@
LOADING
PLAY
ADD
+ BROWSE
FREE
CONFIRM
YES
@@ -126,6 +127,7 @@
INSTALL GAME
Language
Location
+ It is recommended to install outside Program Files folders to avoid potential permission issues.
ADD TO LIBRARY
@@ -151,4 +153,4 @@
ERROR
Copy text
Copied!
-
\ No newline at end of file
+
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.es.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.es.xaml
index 2f4d016..573f526 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.es.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.es.xaml
@@ -13,6 +13,7 @@
CARGANDO
JUGAR
AÑADIR
+ EXAMINAR
GRATIS
CONFIRMAR
SÍ
@@ -113,6 +114,7 @@
INSTALAR JUEGO
Idioma
Ubicación
+ Se recomienda instalar fuera de las carpetas de programas para evitar posibles problemas de permisos.
AÑADIR A LA BIBLIOTECA
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.fr.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.fr.xaml
index 6450770..e5026aa 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.fr.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.fr.xaml
@@ -13,6 +13,7 @@
CHARGEMENT
JOUER
AJOUTER
+ PARCOURIR
GRATUIT
CONFIRMER
OUI
@@ -113,6 +114,7 @@
INSTALLER LE JEU
Langue
Emplacement
+ Il est recommandé d’installer hors des dossiers de programmes pour éviter d’éventuels problèmes d’autorisations.
AJOUTER À LA BIBLIOTHÈQUE
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.hu.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.hu.xaml
index 0d1e1ef..1f4075b 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.hu.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.hu.xaml
@@ -13,6 +13,7 @@
BETÖLTÉS
JÁTSZANI
HOZZÁADÁS
+ TALLÓZÁS
SZABAD
IGEN
IGEN
@@ -113,6 +114,7 @@
JÁTÉK TELEPÍTÉSE
Nyelv
Helyszín
+ Javasolt a programfájlok mappáin kívül telepíteni az esetleges engedélyproblémák elkerülése érdekében.
KÖNYVTÁRBAN HOZZÁADÁS
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.it.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.it.xaml
index 2cfadf8..060ad9f 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.it.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.it.xaml
@@ -13,6 +13,7 @@
CARICAMENTO
GIOCA
AGGIUNGI
+ SFOGLIA
GRATIS
CONFERMA
SÌ
@@ -113,6 +114,7 @@
INSTALLA GIOCO
Lingua
Posizione
+ Si consiglia di installare fuori dalle cartelle dei programmi per evitare potenziali problemi di autorizzazioni.
AGGIUNGI ALLA BIBLIOTECA
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.nl.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.nl.xaml
index 588fbd5..0575519 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.nl.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.nl.xaml
@@ -13,6 +13,7 @@
LADEN
SPELEN
TOEVOEGEN
+ BLADEREN
GRATIS
BEVESTIGEN
JA
@@ -113,6 +114,7 @@
GAME INSTALLEREN
Taal
Locatie
+ Het wordt aanbevolen buiten de programmamappen te installeren om mogelijke machtigingsproblemen te voorkomen.
TOEVOEGEN AAN BIBLIOTHEEK
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.no.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.no.xaml
index 2fdd266..2698651 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.no.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.no.xaml
@@ -13,6 +13,7 @@
LASTER
SPILL
LEGG TIL
+ BLA GJENNOM
GRATIS
BEKREFT
JA
@@ -113,6 +114,7 @@
INSTALLER SPILL
Språk
Beliggenhet
+ Det anbefales å installere utenfor programmappene for å unngå mulige tillatelsesproblemer.
LEGG TIL I BIBLIOTEKET
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.pl.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.pl.xaml
index a4c871a..10b8b95 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.pl.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.pl.xaml
@@ -13,6 +13,7 @@
ŁADOWANIE
GRAJ
DODAJ
+ PRZEGLĄDAJ
ZA DARMO
POTWIERDŹ
TAK
@@ -113,6 +114,7 @@
INSTALUJ GRĘ
Język
Lokalizacja
+ Zaleca się instalowanie poza folderami programów, aby uniknąć potencjalnych problemów z uprawnieniami.
DODAJ DO BIBLIOTEKI
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ru.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ru.xaml
index 9f1cd65..a83a1c6 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ru.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.ru.xaml
@@ -13,6 +13,7 @@
ЗАГРУЗКА
ИГРАТЬ
ДОБАВИТЬ
+ ОБЗОР
БЕСПЛАТНО
ПОДТВЕРДИТЬ
ДА
@@ -113,6 +114,7 @@
УСТАНОВКА ИГРЫ
Язык
Местоположение
+ Рекомендуется устанавливать вне папок программ, чтобы избежать возможных проблем с разрешениями.
ДОБАВИТЬ В БИБЛИОТЕКУ
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.sv.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.sv.xaml
index 87dde51..ffbded0 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.sv.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.sv.xaml
@@ -13,6 +13,7 @@
LADDAR
SPELA
LÄGG TILL
+ BLÄDDRA
FRI
BEKRÄFTA
JA
@@ -113,6 +114,7 @@
INSTALLERA SPEL
Språk
Plats
+ Det rekommenderas att installera utanför programmapparna för att undvika möjliga behörighetsproblem.
LÄGG TILL I BIBLIOTEKET
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.tr.xaml b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.tr.xaml
index 7385563..6f9d702 100644
--- a/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.tr.xaml
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Resources/Dictionary/LanguageResources.tr.xaml
@@ -15,6 +15,7 @@
YÜKLENİYOR
OYNA
EKLE
+ GÖZ AT
ÜCRETSİZ
ONAYLA
EVET
@@ -126,6 +127,7 @@
OYUNU YÜKLE
Dil
Konum
+ Olası izin sorunlarını önlemek için program dosyaları klasörlerinin dışına kurulum yapılması önerilir.
KÜTÜPHANEYE EKLE
@@ -151,4 +153,4 @@
HATA
Metni kopyala
Kopyalandı!
-
\ No newline at end of file
+
diff --git a/src/BfmeFoundationProject_AllInOneLauncher/Utils/InstallLocationUtils.cs b/src/BfmeFoundationProject_AllInOneLauncher/Utils/InstallLocationUtils.cs
new file mode 100644
index 0000000..99f211d
--- /dev/null
+++ b/src/BfmeFoundationProject_AllInOneLauncher/Utils/InstallLocationUtils.cs
@@ -0,0 +1,109 @@
+using System.IO;
+
+namespace BfmeFoundationProject.AllInOneLauncher.Core.Utils
+{
+ public static class InstallLocationUtils
+ {
+ private static readonly HashSet ProgramFilesFolders = GetProgramFilesFolders();
+
+ public static string GetDefaultDriveRoot()
+ {
+ var drives = DriveInfo.GetDrives();
+ var drive = drives.FirstOrDefault(IsReadyFixedDrive) ?? drives.FirstOrDefault(IsReadyDrive);
+ return drive?.RootDirectory.FullName ?? "";
+ }
+
+ public static bool IsValidLocation(string location)
+ {
+ try
+ {
+ return !string.IsNullOrWhiteSpace(location)
+ && Path.IsPathFullyQualified(location)
+ && !string.IsNullOrWhiteSpace(Path.GetPathRoot(location));
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static string NormalizeLocation(string location)
+ {
+ if (string.IsNullOrWhiteSpace(location))
+ return "";
+
+ try
+ {
+ string fullPath = Path.GetFullPath(location.Trim());
+ string? root = Path.GetPathRoot(fullPath);
+
+ if (!string.IsNullOrWhiteSpace(root) && string.Equals(fullPath, root, StringComparison.OrdinalIgnoreCase))
+ return root;
+
+ return fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+ }
+ catch
+ {
+ return location.Trim();
+ }
+ }
+
+ public static bool TryGetDrive(string location, out DriveInfo? drive)
+ {
+ drive = null;
+
+ try
+ {
+ string? root = Path.GetPathRoot(location);
+ if (string.IsNullOrWhiteSpace(root))
+ return false;
+
+ drive = new DriveInfo(root);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static bool IsInProgramFilesFolder(string location)
+ {
+ string normalizedLocation = NormalizeLocation(location).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+
+ return ProgramFilesFolders.Any(programFilesFolder =>
+ string.Equals(normalizedLocation, programFilesFolder, StringComparison.OrdinalIgnoreCase)
+ || normalizedLocation.StartsWith(programFilesFolder + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase));
+ }
+
+ private static bool IsReadyFixedDrive(DriveInfo drive) => IsReadyDrive(drive) && drive.DriveType == DriveType.Fixed;
+
+ private static bool IsReadyDrive(DriveInfo drive)
+ {
+ try
+ {
+ return drive.IsReady;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static HashSet GetProgramFilesFolders()
+ {
+ string[] candidates =
+ [
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
+ ];
+
+ return candidates
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Select(NormalizeLocation)
+ .Select(x => x.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
+ }
+ }
+}