aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/tool_bethinipie/src/bethinipie.cpp19
-rw-r--r--src/src/settingsdialog.ui12
-rw-r--r--src/src/settingsdialogproton.cpp262
-rw-r--r--src/src/settingsdialogproton.h2
4 files changed, 15 insertions, 280 deletions
diff --git a/libs/tool_bethinipie/src/bethinipie.cpp b/libs/tool_bethinipie/src/bethinipie.cpp
index 152e809..659a7d6 100644
--- a/libs/tool_bethinipie/src/bethinipie.cpp
+++ b/libs/tool_bethinipie/src/bethinipie.cpp
@@ -30,7 +30,18 @@ static const char* GITHUB_REPO = "SulfurNitride/Fluorine-Bethini-Pie-Performanc
static const char* RELEASE_TAG = "latest";
static const char* ASSET_NAME = "BethiniPie-linux.tar.gz";
-// Maps Fluorine gameName() to BethIni Pie's Bethini.json INI directory key
+// Maps Fluorine managedGame()->gameName() to the BethINI Pie app name.
+// MO2 game plugins sometimes return a short name (e.g. "New Vegas") that
+// differs from BethINI Pie's app folder name ("Fallout New Vegas").
+static const QMap<QString, QString> s_MO2ToBethiniName = {
+ {"Skyrim Special Edition", "Skyrim Special Edition"},
+ {"Fallout 4", "Fallout 4"},
+ {"Fallout New Vegas", "Fallout New Vegas"},
+ {"New Vegas", "Fallout New Vegas"},
+ {"Starfield", "Starfield"},
+};
+
+// Maps the BethINI Pie app name to its Bethini.json INI directory key.
static const QMap<QString, QString> s_GameIniKeys = {
{"Skyrim Special Edition", "sSkyrim Special EditionINIPath"},
{"Fallout 4", "sFallout 4INIPath"},
@@ -125,10 +136,8 @@ QString BethiniPie::bethiniGameName() const
{
if (!m_MOInfo || !m_MOInfo->managedGame())
return {};
- QString game = m_MOInfo->managedGame()->gameName();
- if (s_GameIniKeys.contains(game))
- return game;
- return {};
+ const QString mo2Name = m_MOInfo->managedGame()->gameName();
+ return s_MO2ToBethiniName.value(mo2Name);
}
QString BethiniPie::iniDirectoryKey() const
diff --git a/src/src/settingsdialog.ui b/src/src/settingsdialog.ui
index 6b4ebc6..cb158f2 100644
--- a/src/src/settingsdialog.ui
+++ b/src/src/settingsdialog.ui
@@ -1783,17 +1783,7 @@ If you disable this feature, MO will only display official DLCs this way. Please
</property>
</widget>
</item>
- <item row="3" column="0" colspan="2">
- <widget class="QPushButton" name="fixGameRegistriesButton">
- <property name="text">
- <string>Fix Game Registries</string>
- </property>
- <property name="toolTip">
- <string>Re-apply Wine registry settings for the current prefix. Use this if games fail to detect their installation or have registry-related issues.</string>
- </property>
- </widget>
- </item>
- <item row="3" column="2" colspan="2">
+ <item row="3" column="0" colspan="4">
<widget class="QPushButton" name="winetricksButton">
<property name="text">
<string>Winetricks</string>
diff --git a/src/src/settingsdialogproton.cpp b/src/src/settingsdialogproton.cpp
index 2117a09..5217092 100644
--- a/src/src/settingsdialogproton.cpp
+++ b/src/src/settingsdialogproton.cpp
@@ -8,23 +8,18 @@
#include <QtConcurrent/QtConcurrentRun>
#include <log.h>
#include <uibase/utility.h>
-#include "knowngames.h"
#include "steamdetection.h"
-#include "gamedetection.h"
#include "slrmanager.h"
#include <atomic>
-#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
#include <QDateTime>
#include <QDialog>
-#include <QDialogButtonBox>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QLabel>
-#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QtConcurrent/QtConcurrent>
@@ -40,128 +35,6 @@
namespace
{
std::atomic<ProtonSettingsTab*> g_activeInstallTab = nullptr;
-
-/// Find the wine binary inside a Proton installation directory.
-static QString findWineBinary(const QString& protonPath)
-{
- for (const char* subdir : {"files/bin", "dist/bin"}) {
- const QString candidate =
- QDir(protonPath).filePath(QString::fromLatin1(subdir) + "/wine");
- if (QFileInfo::exists(candidate))
- return candidate;
- }
- return {};
-}
-
-/// Apply a single game's registry entry via wine regedit.
-///
-/// Looks up the game by name in NaK's KNOWN_GAMES, builds a .reg file that
-/// maps the install path to the game's registry key, and imports it with
-/// wine regedit (wrapped in SLR if available).
-static QString applyGameRegistryNative(const QString& prefixPath,
- const QString& protonPath,
- const QString& gameName,
- const QString& installPath,
- void (*logCb)(const char*))
-{
- // Find wine binary.
- const QString wineBin = findWineBinary(protonPath);
- if (wineBin.isEmpty())
- return QStringLiteral("Wine binary not found in Proton at %1").arg(protonPath);
-
- // Look up registry path/value from known games.
- const KnownGame* foundGame = nullptr;
- for (int i = 0; i < KNOWN_GAMES_COUNT; ++i) {
- if (gameName == QString::fromLatin1(KNOWN_GAMES[i].name)) {
- foundGame = &KNOWN_GAMES[i];
- break;
- }
- }
-
- if (!foundGame || !foundGame->registry_path || !foundGame->registry_value)
- return QStringLiteral("Unknown game: %1").arg(gameName);
-
- const QString rPath = QString::fromLatin1(foundGame->registry_path);
- const QString rVal = QString::fromLatin1(foundGame->registry_value);
-
- // Convert Linux path → Wine Z: drive path with escaped backslashes for .reg.
- const QString winePath = "Z:" + QString(installPath).replace('/', "\\\\");
-
- // Wow6432Node key: strip the leading "Software\" prefix.
- const int firstBackslash = rPath.indexOf('\\');
- const QString wow64Key =
- (firstBackslash >= 0) ? rPath.mid(firstBackslash + 1) : rPath;
-
- const QString regContent = QStringLiteral(
- "Windows Registry Editor Version 5.00\n\n"
- "[HKEY_LOCAL_MACHINE\\%1]\n\"%2\"=\"%3\"\n\n"
- "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\%4]\n\"%5\"=\"%6\"\n\n")
- .arg(rPath, rVal, winePath, wow64Key, rVal, winePath);
-
- // Write temp .reg file.
- const QString tmpDir = fluorineDataDir() + "/tmp";
- QDir().mkpath(tmpDir);
- const QString regFile = tmpDir + "/game_reg_apply.reg";
-
- {
- QFile f(regFile);
- if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
- return QStringLiteral("Failed to write registry file: %1").arg(regFile);
- f.write(regContent.toUtf8());
- }
-
- if (logCb) {
- const QByteArray msg =
- QStringLiteral("Applying registry for %1...").arg(gameName).toUtf8();
- logCb(msg.constData());
- }
-
- // Build the command — wrap in SLR if available.
- QProcess proc;
- QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
- env.insert("WINEPREFIX", prefixPath);
- env.insert("WINEDLLOVERRIDES", "mshtml=d");
- env.insert("PROTON_USE_XALIA", "0");
- proc.setProcessEnvironment(env);
-
- const QString slr = getSlrRunScript();
- if (!slr.isEmpty()) {
-
- QStringList args;
- // Expose directories to the container.
- const QString wineDir = QFileInfo(wineBin).absolutePath();
- if (!wineDir.isEmpty())
- args << QStringLiteral("--filesystem=%1").arg(wineDir);
- if (!prefixPath.isEmpty())
- args << QStringLiteral("--filesystem=%1").arg(prefixPath);
- if (!protonPath.isEmpty())
- args << QStringLiteral("--filesystem=%1").arg(protonPath);
- if (QDir(tmpDir).exists())
- args << QStringLiteral("--filesystem=%1").arg(tmpDir);
- args << "--" << wineBin << "regedit" << regFile;
-
- proc.setProgram(slr);
- proc.setArguments(args);
- } else {
- proc.setProgram(wineBin);
- proc.setArguments({"regedit", regFile});
- }
-
- proc.setProcessChannelMode(QProcess::MergedChannels);
- proc.start();
- proc.waitForFinished(60000);
- QFile::remove(regFile);
-
- if (proc.exitStatus() != QProcess::NormalExit || proc.exitCode() != 0)
- return QStringLiteral("wine regedit failed (exit code %1)").arg(proc.exitCode());
-
- if (logCb) {
- const QByteArray msg =
- QStringLiteral("Registry applied for %1").arg(gameName).toUtf8();
- logCb(msg.constData());
- }
- return {}; // success
-}
}
ProtonSettingsTab::ProtonSettingsTab(Settings& s, SettingsDialog& d)
@@ -218,8 +91,6 @@ ProtonSettingsTab::ProtonSettingsTab(Settings& s, SettingsDialog& d)
&ProtonSettingsTab::onRecreatePrefix);
QObject::connect(ui->openPrefixFolderButton, &QPushButton::clicked, this,
&ProtonSettingsTab::onOpenPrefixFolder);
- QObject::connect(ui->fixGameRegistriesButton, &QPushButton::clicked, this,
- &ProtonSettingsTab::onFixGameRegistries);
QObject::connect(ui->winetricksButton, &QPushButton::clicked, this,
&ProtonSettingsTab::onWinetricks);
QObject::connect(ui->prefixLocationBrowseButton, &QPushButton::clicked, this,
@@ -310,7 +181,6 @@ void ProtonSettingsTab::refreshState()
ui->deletePrefixButton->setEnabled(!m_busy && active);
ui->recreatePrefixButton->setEnabled(!m_busy && active);
ui->openPrefixFolderButton->setEnabled(!m_busy && active);
- ui->fixGameRegistriesButton->setEnabled(!m_busy && active);
ui->winetricksButton->setEnabled(!m_busy && active);
ui->protonVersionCombo->setEnabled(!m_busy);
}
@@ -623,138 +493,6 @@ void ProtonSettingsTab::onWinetricks()
proc.startDetached();
}
-void ProtonSettingsTab::onFixGameRegistries()
-{
- if (m_busy) {
- return;
- }
-
- showGameRegistryDialog();
-}
-
-void ProtonSettingsTab::showGameRegistryDialog()
-{
- auto cfg = FluorineConfig::load();
- if (!cfg.has_value() || !cfg->prefixExists()) {
- ui->protonStatusLabel->setText(tr("No existing prefix"));
- refreshState();
- return;
- }
-
- QDialog dialog(parentWidget());
- dialog.setWindowTitle(tr("Fix Game Registries"));
- dialog.setMinimumWidth(500);
-
- auto* layout = new QVBoxLayout(&dialog);
-
- layout->addWidget(new QLabel(tr("Select the game to apply registry settings for:"),
- &dialog));
-
- auto* gameCombo = new QComboBox(&dialog);
-
- for (int i = 0; i < KNOWN_GAMES_COUNT; ++i) {
- const QString name = QString::fromLatin1(KNOWN_GAMES[i].name);
- if (!name.isEmpty()) {
- gameCombo->addItem(name);
- }
- }
-
- layout->addWidget(gameCombo);
-
- // Game installation path
- layout->addWidget(new QLabel(tr("Game installation path:"), &dialog));
-
- auto* pathLayout = new QHBoxLayout();
- auto* pathEdit = new QLineEdit(&dialog);
- pathEdit->setPlaceholderText(tr("e.g. /home/user/.local/share/Steam/steamapps/common/Skyrim Special Edition"));
- auto* browseBtn = new QPushButton(tr("Browse..."), &dialog);
- pathLayout->addWidget(pathEdit);
- pathLayout->addWidget(browseBtn);
- layout->addLayout(pathLayout);
-
- QObject::connect(browseBtn, &QPushButton::clicked, &dialog, [&dialog, pathEdit]() {
- const QString dir = QFileDialog::getExistingDirectory(
- &dialog, QObject::tr("Select Game Installation Folder"),
- pathEdit->text().isEmpty() ? QDir::homePath() : pathEdit->text());
- if (!dir.isEmpty()) {
- pathEdit->setText(dir);
- }
- });
-
- // Try to auto-detect path when game selection changes
- auto autoDetect = [pathEdit, gameCombo]() {
- const QString gameName = gameCombo->currentText();
- if (gameName.isEmpty()) return;
-
- // Use detectAllGames to find the install path
- const GameScanResult scanResult = detectAllGames();
- for (int i = 0; i < scanResult.games.size(); ++i) {
- const DetectedGame& detected = scanResult.games[i];
- if (detected.name.contains(gameName, Qt::CaseInsensitive) ||
- gameName.contains(detected.name, Qt::CaseInsensitive)) {
- if (!detected.install_path.isEmpty()) {
- pathEdit->setText(detected.install_path);
- break;
- }
- }
- }
- };
-
- QObject::connect(gameCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
- &dialog, autoDetect);
- // Auto-detect for initially selected game
- autoDetect();
-
- auto* buttons =
- new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
- layout->addWidget(buttons);
-
- QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
- QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
-
- if (dialog.exec() != QDialog::Accepted) {
- return;
- }
-
- const QString selectedGame = gameCombo->currentText();
- const QString gamePath = pathEdit->text();
- const QString prefixPath = cfg->prefix_path;
- const QString protonName = cfg->proton_name;
- const QString protonPath = cfg->proton_path;
-
- if (gamePath.isEmpty()) {
- QMessageBox::warning(parentWidget(), tr("No Path"),
- tr("Please provide the game installation path."));
- return;
- }
-
- setBusy(true);
- ui->protonStatusLabel->setText(tr("Fixing game registries..."));
- ui->nakInstallLog->clear();
- ui->nakInstallLog->setVisible(true);
- ui->toggleInstallLog->setChecked(true);
-
- g_activeInstallTab.store(this);
-
- m_pendingPrefixPath = prefixPath;
- m_pendingProtonName = protonName;
- m_pendingProtonPath = protonPath;
-
- m_installWatcher.setFuture(
- QtConcurrent::run([prefixPath, protonPath,
- selectedGame, gamePath]() -> InstallResult {
- const QString err = applyGameRegistryNative(
- prefixPath, protonPath, selectedGame, gamePath,
- &ProtonSettingsTab::logCallback);
-
- InstallResult r;
- if (!err.isEmpty())
- r.error = err;
-
- return r;
- }));
-}
-
void ProtonSettingsTab::runPrefixSetupDialog(uint32_t appId,
const QString& prefixPath,
const QString& protonName,
diff --git a/src/src/settingsdialogproton.h b/src/src/settingsdialogproton.h
index db0770c..966a980 100644
--- a/src/src/settingsdialogproton.h
+++ b/src/src/settingsdialogproton.h
@@ -30,12 +30,10 @@ private:
void onDeletePrefix();
void onRecreatePrefix();
void onOpenPrefixFolder();
- void onFixGameRegistries();
void onWinetricks();
void onBrowsePrefixLocation();
void onDownloadSLR();
- void showGameRegistryDialog();
QString ensureWinetricks();
QString findProtonWine(const QString& protonPath);