mirror of https://github.com/PCSX2/pcsx2.git
Qt: Ensure settings are writable before running setup wizard
This commit is contained in:
parent
332be6c771
commit
f8b18d406f
|
@ -88,7 +88,8 @@ static u32 s_total_drawn_frames = 0;
|
||||||
|
|
||||||
bool GSRunner::InitializeConfig()
|
bool GSRunner::InitializeConfig()
|
||||||
{
|
{
|
||||||
if (!EmuFolders::InitializeCriticalFolders())
|
EmuFolders::SetAppRoot();
|
||||||
|
if (!EmuFolders::SetResourcesDirectory() || !EmuFolders::SetDataDirectory(nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const char* error;
|
const char* error;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
#include "common/CrashHandler.h"
|
#include "common/CrashHandler.h"
|
||||||
|
#include "common/Error.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/HTTPDownloader.h"
|
#include "common/HTTPDownloader.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
|
@ -1197,23 +1198,41 @@ void Host::OnCaptureStopped()
|
||||||
|
|
||||||
bool QtHost::InitializeConfig()
|
bool QtHost::InitializeConfig()
|
||||||
{
|
{
|
||||||
if (!EmuFolders::InitializeCriticalFolders())
|
Error error;
|
||||||
|
|
||||||
|
EmuFolders::SetAppRoot();
|
||||||
|
|
||||||
|
if (!EmuFolders::SetResourcesDirectory())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(nullptr, QStringLiteral("PCSX2"),
|
QMessageBox::critical(nullptr, QStringLiteral("PCSX2"),
|
||||||
QStringLiteral("One or more critical directories are missing, your installation may be incomplete."));
|
QStringLiteral("Resources directory is missing, your installation is incomplete."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EmuFolders::SetDataDirectory(&error))
|
||||||
|
{
|
||||||
|
// no point translating, config isn't loaded
|
||||||
|
QMessageBox::critical(
|
||||||
|
nullptr, QStringLiteral("PCSX2"),
|
||||||
|
QStringLiteral("Failed to create data directory at path\n\n%1\n\n"
|
||||||
|
"The error was: %2\n"
|
||||||
|
"Please ensure this directory is writable. You can also try portable mode "
|
||||||
|
"by creating portable.txt in the same directory you installed PCSX2 into.")
|
||||||
|
.arg(QString::fromStdString(EmuFolders::DataRoot))
|
||||||
|
.arg(QString::fromStdString(error.GetDescription())));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write crash dumps to the data directory, since that'll be accessible for certain.
|
// Write crash dumps to the data directory, since that'll be accessible for certain.
|
||||||
CrashHandler::SetWriteDirectory(EmuFolders::DataRoot);
|
CrashHandler::SetWriteDirectory(EmuFolders::DataRoot);
|
||||||
|
|
||||||
const std::string path(Path::Combine(EmuFolders::Settings, "PCSX2.ini"));
|
const std::string path = Path::Combine(EmuFolders::Settings, "PCSX2.ini");
|
||||||
s_run_setup_wizard = s_run_setup_wizard || !FileSystem::FileExists(path.c_str());
|
const bool settings_exists = FileSystem::FileExists(path.c_str());
|
||||||
Console.WriteLn("Loading config from %s.", path.c_str());
|
Console.WriteLnFmt("Loading config from {}.", path);
|
||||||
|
|
||||||
s_base_settings_interface = std::make_unique<INISettingsInterface>(std::move(path));
|
s_base_settings_interface = std::make_unique<INISettingsInterface>(std::move(path));
|
||||||
Host::Internal::SetBaseSettingsLayer(s_base_settings_interface.get());
|
Host::Internal::SetBaseSettingsLayer(s_base_settings_interface.get());
|
||||||
if (!s_base_settings_interface->Load() || !VMManager::Internal::CheckSettingsVersion())
|
if (!settings_exists || !s_base_settings_interface->Load() || !VMManager::Internal::CheckSettingsVersion())
|
||||||
{
|
{
|
||||||
// If the config file doesn't exist, assume this is a new install and don't prompt to overwrite.
|
// If the config file doesn't exist, assume this is a new install and don't prompt to overwrite.
|
||||||
if (FileSystem::FileExists(s_base_settings_interface->GetFileName().c_str()) &&
|
if (FileSystem::FileExists(s_base_settings_interface->GetFileName().c_str()) &&
|
||||||
|
@ -1226,6 +1245,22 @@ bool QtHost::InitializeConfig()
|
||||||
|
|
||||||
VMManager::SetDefaultSettings(*s_base_settings_interface, true, true, true, true, true);
|
VMManager::SetDefaultSettings(*s_base_settings_interface, true, true, true, true, true);
|
||||||
|
|
||||||
|
// Flag for running the setup wizard if this is our first run. We want to run it next time if they don't finish it.
|
||||||
|
s_base_settings_interface->SetBoolValue("UI", "SetupWizardIncomplete", true);
|
||||||
|
|
||||||
|
// Make sure we can actually save the config, and the user doesn't have some permission issue.
|
||||||
|
if (!s_base_settings_interface->Save(&error))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(
|
||||||
|
nullptr, QStringLiteral("PCSX2"),
|
||||||
|
QStringLiteral(
|
||||||
|
"Failed to save configuration to\n\n%1\n\nThe error was: %2\n\nPlease ensure this directory is writable. You "
|
||||||
|
"can also try portable mode by creating portable.txt in the same directory you installed PCSX2 into.")
|
||||||
|
.arg(QString::fromStdString(s_base_settings_interface->GetFileName()))
|
||||||
|
.arg(QString::fromStdString(error.GetDescription())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't save if we're running the setup wizard. We want to run it next time if they don't finish it.
|
// Don't save if we're running the setup wizard. We want to run it next time if they don't finish it.
|
||||||
if (!s_run_setup_wizard)
|
if (!s_run_setup_wizard)
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
|
@ -1379,8 +1414,7 @@ std::optional<bool> QtHost::DownloadFile(QWidget* parent, const QString& title,
|
||||||
const std::string::size_type url_file_part_pos = url.rfind('/');
|
const std::string::size_type url_file_part_pos = url.rfind('/');
|
||||||
QtModalProgressCallback progress(parent);
|
QtModalProgressCallback progress(parent);
|
||||||
progress.GetDialog().setLabelText(
|
progress.GetDialog().setLabelText(
|
||||||
qApp->translate("EmuThread", "Downloading %1...").arg(QtUtils::StringViewToQString(
|
qApp->translate("EmuThread", "Downloading %1...").arg(QtUtils::StringViewToQString(std::string_view(url).substr((url_file_part_pos != std::string::npos) ? (url_file_part_pos + 1) : 0))));
|
||||||
std::string_view(url).substr((url_file_part_pos != std::string::npos) ? (url_file_part_pos + 1) : 0))));
|
|
||||||
progress.GetDialog().setWindowTitle(title);
|
progress.GetDialog().setWindowTitle(title);
|
||||||
progress.GetDialog().setWindowIcon(GetAppIcon());
|
progress.GetDialog().setWindowIcon(GetAppIcon());
|
||||||
progress.SetCancellable(true);
|
progress.SetCancellable(true);
|
||||||
|
@ -1403,8 +1437,8 @@ std::optional<bool> QtHost::DownloadFile(QWidget* parent, const QString& title,
|
||||||
QMessageBox::critical(parent, qApp->translate("EmuThread", "Error"),
|
QMessageBox::critical(parent, qApp->translate("EmuThread", "Error"),
|
||||||
qApp->translate("EmuThread", "Download failed: Data is empty.").arg(status_code));
|
qApp->translate("EmuThread", "Download failed: Data is empty.").arg(status_code));
|
||||||
|
|
||||||
download_result = false;
|
download_result = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*data = std::move(hdata);
|
*data = std::move(hdata);
|
||||||
|
@ -1851,10 +1885,6 @@ void QtHost::RegisterTypes()
|
||||||
|
|
||||||
bool QtHost::RunSetupWizard()
|
bool QtHost::RunSetupWizard()
|
||||||
{
|
{
|
||||||
// Set a flag in the config so that even though we created the ini, we'll run the wizard next time.
|
|
||||||
Host::SetBaseBoolSettingValue("UI", "SetupWizardIncomplete", true);
|
|
||||||
Host::CommitBaseSettingChanges();
|
|
||||||
|
|
||||||
SetupWizardDialog dialog;
|
SetupWizardDialog dialog;
|
||||||
if (dialog.exec() == QDialog::Rejected)
|
if (dialog.exec() == QDialog::Rejected)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
} \
|
} \
|
||||||
;
|
;
|
||||||
|
|
||||||
|
class Error;
|
||||||
class SettingsInterface;
|
class SettingsInterface;
|
||||||
class SettingsWrapper;
|
class SettingsWrapper;
|
||||||
|
|
||||||
|
@ -1226,7 +1227,9 @@ namespace EmuFolders
|
||||||
extern std::string Videos;
|
extern std::string Videos;
|
||||||
|
|
||||||
/// Initializes critical folders (AppRoot, DataRoot, Settings). Call once on startup.
|
/// Initializes critical folders (AppRoot, DataRoot, Settings). Call once on startup.
|
||||||
bool InitializeCriticalFolders();
|
void SetAppRoot();
|
||||||
|
bool SetResourcesDirectory();
|
||||||
|
bool SetDataDirectory(Error* error);
|
||||||
|
|
||||||
// Assumes that AppRoot and DataRoot have been initialized.
|
// Assumes that AppRoot and DataRoot have been initialized.
|
||||||
void SetDefaults(SettingsInterface& si);
|
void SetDefaults(SettingsInterface& si);
|
||||||
|
|
|
@ -167,10 +167,7 @@ namespace EmuFolders
|
||||||
std::string InputProfiles;
|
std::string InputProfiles;
|
||||||
std::string Videos;
|
std::string Videos;
|
||||||
|
|
||||||
static void SetAppRoot();
|
|
||||||
static void SetResourcesDirectory();
|
|
||||||
static bool ShouldUsePortableMode();
|
static bool ShouldUsePortableMode();
|
||||||
static void SetDataDirectory();
|
|
||||||
} // namespace EmuFolders
|
} // namespace EmuFolders
|
||||||
|
|
||||||
TraceFiltersEE::TraceFiltersEE()
|
TraceFiltersEE::TraceFiltersEE()
|
||||||
|
@ -1845,26 +1842,28 @@ void Pcsx2Config::ClearConfiguration(SettingsInterface* dest_si)
|
||||||
temp.LoadSaveCore(wrapper);
|
temp.LoadSaveCore(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuFolders::InitializeCriticalFolders()
|
void EmuFolders::SetAppRoot()
|
||||||
{
|
{
|
||||||
SetAppRoot();
|
const std::string program_path = FileSystem::GetProgramPath();
|
||||||
SetResourcesDirectory();
|
Console.WriteLnFmt("Program Path: {}", program_path);
|
||||||
SetDataDirectory();
|
|
||||||
|
AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
|
||||||
|
|
||||||
// logging of directories in case something goes wrong super early
|
// logging of directories in case something goes wrong super early
|
||||||
Console.WriteLn("AppRoot Directory: %s", AppRoot.c_str());
|
Console.WriteLnFmt("AppRoot Directory: {}", AppRoot);
|
||||||
Console.WriteLn("DataRoot Directory: %s", DataRoot.c_str());
|
}
|
||||||
Console.WriteLn("Resources Directory: %s", Resources.c_str());
|
|
||||||
|
|
||||||
// allow SetDataDirectory() to change settings directory (if we want to split config later on)
|
bool EmuFolders::SetResourcesDirectory()
|
||||||
if (Settings.empty())
|
{
|
||||||
{
|
#ifndef __APPLE__
|
||||||
Settings = Path::Combine(DataRoot, "inis");
|
// On Windows/Linux, these are in the binary directory.
|
||||||
|
Resources = Path::Combine(AppRoot, "resources");
|
||||||
|
#else
|
||||||
|
// On macOS, this is in the bundle resources directory.
|
||||||
|
Resources = Path::Canonicalize(Path::Combine(AppRoot, "../Resources"));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Create settings directory if it doesn't exist. If we're not using portable mode, it won't.
|
Console.WriteLnFmt("Resources Directory: {}", Resources);
|
||||||
if (!FileSystem::DirectoryExists(Settings.c_str()))
|
|
||||||
FileSystem::CreateDirectoryPath(Settings.c_str(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the resources directory should exist, bail out if not
|
// the resources directory should exist, bail out if not
|
||||||
if (!FileSystem::DirectoryExists(Resources.c_str()))
|
if (!FileSystem::DirectoryExists(Resources.c_str()))
|
||||||
|
@ -1876,87 +1875,65 @@ bool EmuFolders::InitializeCriticalFolders()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuFolders::SetAppRoot()
|
|
||||||
{
|
|
||||||
const std::string program_path = FileSystem::GetProgramPath();
|
|
||||||
Console.WriteLn("Program Path: %s", program_path.c_str());
|
|
||||||
|
|
||||||
AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuFolders::SetResourcesDirectory()
|
|
||||||
{
|
|
||||||
#ifndef __APPLE__
|
|
||||||
// On Windows/Linux, these are in the binary directory.
|
|
||||||
Resources = Path::Combine(AppRoot, "resources");
|
|
||||||
#else
|
|
||||||
// On macOS, this is in the bundle resources directory.
|
|
||||||
Resources = Path::Canonicalize(Path::Combine(AppRoot, "../Resources"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EmuFolders::ShouldUsePortableMode()
|
bool EmuFolders::ShouldUsePortableMode()
|
||||||
{
|
{
|
||||||
// Check whether portable.ini exists in the program directory.
|
// Check whether portable.ini exists in the program directory.
|
||||||
return FileSystem::FileExists(Path::Combine(AppRoot, "portable.ini").c_str()) || FileSystem::FileExists(Path::Combine(AppRoot, "portable.txt").c_str());
|
return FileSystem::FileExists(Path::Combine(AppRoot, "portable.ini").c_str()) || FileSystem::FileExists(Path::Combine(AppRoot, "portable.txt").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuFolders::SetDataDirectory()
|
bool EmuFolders::SetDataDirectory(Error* error)
|
||||||
{
|
{
|
||||||
if (ShouldUsePortableMode())
|
if (!ShouldUsePortableMode())
|
||||||
{
|
{
|
||||||
DataRoot = AppRoot;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
// On Windows, use My Documents\PCSX2 to match old installs.
|
// On Windows, use My Documents\PCSX2 to match old installs.
|
||||||
PWSTR documents_directory;
|
PWSTR documents_directory;
|
||||||
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory)))
|
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory)))
|
||||||
{
|
{
|
||||||
if (std::wcslen(documents_directory) > 0)
|
if (std::wcslen(documents_directory) > 0)
|
||||||
DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2");
|
DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2");
|
||||||
CoTaskMemFree(documents_directory);
|
CoTaskMemFree(documents_directory);
|
||||||
}
|
}
|
||||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||||
// Use $XDG_CONFIG_HOME/PCSX2 if it exists.
|
// Use $XDG_CONFIG_HOME/PCSX2 if it exists.
|
||||||
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
|
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
|
||||||
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
|
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
|
||||||
{
|
{
|
||||||
DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "PCSX2"));
|
DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "PCSX2"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Use ~/PCSX2 for non-XDG, and ~/.config/PCSX2 for XDG.
|
// Use ~/PCSX2 for non-XDG, and ~/.config/PCSX2 for XDG.
|
||||||
|
const char* home_dir = getenv("HOME");
|
||||||
|
if (home_dir)
|
||||||
|
{
|
||||||
|
// ~/.config should exist, but just in case it doesn't and this is a fresh profile..
|
||||||
|
const std::string config_dir(Path::Combine(home_dir, ".config"));
|
||||||
|
if (!FileSystem::DirectoryExists(config_dir.c_str()))
|
||||||
|
FileSystem::CreateDirectoryPath(config_dir.c_str(), false);
|
||||||
|
|
||||||
|
DataRoot = Path::RealPath(Path::Combine(config_dir, "PCSX2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
static constexpr char MAC_DATA_DIR[] = "Library/Application Support/PCSX2";
|
||||||
const char* home_dir = getenv("HOME");
|
const char* home_dir = getenv("HOME");
|
||||||
if (home_dir)
|
if (home_dir)
|
||||||
{
|
DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
|
||||||
// ~/.config should exist, but just in case it doesn't and this is a fresh profile..
|
|
||||||
const std::string config_dir(Path::Combine(home_dir, ".config"));
|
|
||||||
if (!FileSystem::DirectoryExists(config_dir.c_str()))
|
|
||||||
FileSystem::CreateDirectoryPath(config_dir.c_str(), false);
|
|
||||||
|
|
||||||
DataRoot = Path::RealPath(Path::Combine(config_dir, "PCSX2"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
static constexpr char MAC_DATA_DIR[] = "Library/Application Support/PCSX2";
|
|
||||||
const char* home_dir = getenv("HOME");
|
|
||||||
if (home_dir)
|
|
||||||
DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// make sure it exists
|
|
||||||
if (!DataRoot.empty() && !FileSystem::DirectoryExists(DataRoot.c_str()))
|
|
||||||
{
|
|
||||||
// we're in trouble if we fail to create this directory... but try to hobble on with portable
|
|
||||||
if (!FileSystem::CreateDirectoryPath(DataRoot.c_str(), false))
|
|
||||||
DataRoot.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// couldn't determine the data directory? fallback to portable.
|
// couldn't determine the data directory, or using portable mode? fallback to portable.
|
||||||
if (DataRoot.empty())
|
if (DataRoot.empty())
|
||||||
DataRoot = AppRoot;
|
DataRoot = AppRoot;
|
||||||
|
|
||||||
|
// inis is always below the data root
|
||||||
|
Settings = Path::Combine(DataRoot, "inis");
|
||||||
|
|
||||||
|
// make sure it exists
|
||||||
|
Console.WriteLnFmt("DataRoot Directory: {}", DataRoot);
|
||||||
|
return (FileSystem::EnsureDirectoryExists(DataRoot.c_str(), false, error) &&
|
||||||
|
FileSystem::EnsureDirectoryExists(Settings.c_str(), false, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuFolders::SetDefaults(SettingsInterface& si)
|
void EmuFolders::SetDefaults(SettingsInterface& si)
|
||||||
|
|
Loading…
Reference in New Issue