mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-03-29 15:09:46 -06:00
New config mode (part1 of 0.15.1 branch series) (#4145)
* using new emulator_settings * the default user is now just player one * transfer install, addon dirs * fix load custom config issue --------- Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com>
This commit is contained in:
parent
0a722d69e6
commit
08168dc386
@ -871,6 +871,12 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/tls.h
|
||||
src/core/emulator_state.cpp
|
||||
src/core/emulator_state.h
|
||||
src/core/emulator_settings.cpp
|
||||
src/core/emulator_settings.h
|
||||
src/core/user_manager.cpp
|
||||
src/core/user_manager.h
|
||||
src/core/user_settings.cpp
|
||||
src/core/user_settings.h
|
||||
)
|
||||
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#endif
|
||||
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/backend.h"
|
||||
@ -24,6 +23,7 @@
|
||||
#include "common/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@ -141,7 +141,7 @@ public:
|
||||
const auto& log_dir = GetUserPath(PathType::LogDir);
|
||||
std::filesystem::create_directory(log_dir);
|
||||
Filter filter;
|
||||
filter.ParseFilterString(Config::getLogFilter());
|
||||
filter.ParseFilterString(EmulatorSettings.GetLogFilter());
|
||||
const auto& log_file_path = log_file.empty() ? LOG_FILE : log_file;
|
||||
instance = std::unique_ptr<Impl, decltype(&Deleter)>(
|
||||
new Impl(log_dir / log_file_path, filter), Deleter);
|
||||
@ -185,7 +185,7 @@ public:
|
||||
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, const char* format, const fmt::format_args& args) {
|
||||
if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) {
|
||||
if (!filter.CheckMessage(log_class, log_level) || !EmulatorSettings.IsLogEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -213,7 +213,7 @@ public:
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
if (Config::groupIdenticalLogs()) {
|
||||
if (EmulatorSettings.IsIdenticalLogGrouped()) {
|
||||
std::unique_lock entry_loc(_mutex);
|
||||
|
||||
if (_last_entry.message == message) {
|
||||
@ -226,7 +226,7 @@ public:
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
if (EmulatorSettings.GetLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
@ -258,7 +258,7 @@ public:
|
||||
.counter = 1,
|
||||
};
|
||||
|
||||
if (Config::getLogType() == "async") {
|
||||
if (EmulatorSettings.GetLogType() == "async") {
|
||||
message_queue.EmplaceWait(entry);
|
||||
} else {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
@ -296,14 +296,14 @@ private:
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
if (Config::groupIdenticalLogs()) {
|
||||
if (EmulatorSettings.IsIdenticalLogGrouped()) {
|
||||
// log last message
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
if (EmulatorSettings.GetLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
|
||||
@ -164,6 +164,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
CLS(Input) \
|
||||
CLS(Tty) \
|
||||
CLS(KeyManager) \
|
||||
CLS(EmuSettings) \
|
||||
CLS(Loader)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
|
||||
@ -132,6 +132,7 @@ enum class Class : u8 {
|
||||
Input, ///< Input emulation
|
||||
Tty, ///< Debug output from emu
|
||||
KeyManager, ///< Key management system
|
||||
EmuSettings, /// Emulator settings system
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
|
||||
@ -129,6 +129,7 @@ static auto UserPaths = [] {
|
||||
create_path(PathType::CustomConfigs, user_dir / CUSTOM_CONFIGS);
|
||||
create_path(PathType::CacheDir, user_dir / CACHE_DIR);
|
||||
create_path(PathType::FontsDir, user_dir / FONTS_DIR);
|
||||
create_path(PathType::HomeDir, user_dir / HOME_DIR);
|
||||
|
||||
std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt");
|
||||
if (notice_file.is_open()) {
|
||||
|
||||
@ -26,6 +26,7 @@ enum class PathType {
|
||||
CustomConfigs, // Where custom files for different games are stored.
|
||||
CacheDir, // Where pipeline and shader cache is stored.
|
||||
FontsDir, // Where dumped system fonts are stored.
|
||||
HomeDir, // PS4 home directory
|
||||
};
|
||||
|
||||
constexpr auto PORTABLE_DIR = "user";
|
||||
@ -46,6 +47,7 @@ constexpr auto CUSTOM_TROPHY = "custom_trophy";
|
||||
constexpr auto CUSTOM_CONFIGS = "custom_configs";
|
||||
constexpr auto CACHE_DIR = "cache";
|
||||
constexpr auto FONTS_DIR = "fonts";
|
||||
constexpr auto HOME_DIR = "home";
|
||||
|
||||
// Filenames
|
||||
constexpr auto LOG_FILE = "shad_log.txt";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -7,6 +7,7 @@
|
||||
#include "common/types.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace Serialization {
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/error.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/memory.h"
|
||||
#include "libraries/error_codes.h"
|
||||
@ -187,7 +187,7 @@ struct AddressSpace::Impl {
|
||||
user_size = supported_user_max - USER_MIN - 1;
|
||||
|
||||
// Increase BackingSize to account for config options.
|
||||
BackingSize += Config::getExtraDmemInMbytes() * 1_MB;
|
||||
BackingSize += EmulatorSettings.GetExtraDmemInMBytes() * 1_MB;
|
||||
|
||||
// Allocate backing file that represents the total physical memory.
|
||||
backing_handle = CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_ALL_ACCESS,
|
||||
@ -606,7 +606,7 @@ enum PosixPageProtection {
|
||||
|
||||
struct AddressSpace::Impl {
|
||||
Impl() {
|
||||
BackingSize += Config::getExtraDmemInMbytes() * 1_MB;
|
||||
BackingSize += EmulatorSettings.GetExtraDmemInMBytes() * 1_MB;
|
||||
// Allocate virtual address placeholder for our address space.
|
||||
system_managed_size = SystemManagedSize;
|
||||
system_reserved_size = SystemReservedSize;
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include "SDL3/SDL_log.h"
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/emulator_state.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui_internal.h"
|
||||
@ -110,11 +110,11 @@ void L::DrawMenuBar() {
|
||||
EndDisabled();
|
||||
|
||||
if (Button("Save")) {
|
||||
Config::setFsrEnabled(fsr.enable);
|
||||
Config::setRcasEnabled(fsr.use_rcas);
|
||||
Config::setRcasAttenuation(static_cast<int>(fsr.rcas_attenuation * 1000));
|
||||
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
|
||||
"config.toml");
|
||||
EmulatorSettings.SetFsrEnabled(fsr.enable);
|
||||
EmulatorSettings.SetRcasEnabled(fsr.use_rcas);
|
||||
EmulatorSettings.SetRcasAttenuation(
|
||||
static_cast<int>(fsr.rcas_attenuation * 1000));
|
||||
EmulatorSettings.Save();
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
|
||||
@ -311,7 +311,7 @@ static void LoadSettings(const char* line) {
|
||||
|
||||
void L::SetupSettings() {
|
||||
frame_graph.is_open = true;
|
||||
show_simple_fps = Config::getShowFpsCounter();
|
||||
show_simple_fps = EmulatorSettings.IsShowFpsCounter();
|
||||
|
||||
using SettingLoader = void (*)(const char*);
|
||||
|
||||
@ -472,7 +472,7 @@ void L::Draw() {
|
||||
if (ImGui::Begin("Volume Window", &show_volume,
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
||||
Text("Volume: %d", Config::getVolumeSlider());
|
||||
Text("Volume: %d", EmulatorSettings.GetVolumeSlider());
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "frame_graph.h"
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
@ -29,7 +29,7 @@ void FrameGraph::DrawFrameGraph() {
|
||||
return;
|
||||
}
|
||||
|
||||
float target_dt = 1.0f / (float)Config::vblankFreq();
|
||||
float target_dt = 1.0f / (float)EmulatorSettings.GetVblankFrequency();
|
||||
float cur_pos_x = pos.x + full_width;
|
||||
pos.y += FRAME_GRAPH_PADDING_Y;
|
||||
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -8,9 +8,9 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
@ -23,7 +23,7 @@ public:
|
||||
bool open = false;
|
||||
|
||||
static bool IsSystemModule(const std::filesystem::path& path) {
|
||||
const auto sys_modules_path = Config::getSysModulesPath();
|
||||
const auto sys_modules_path = EmulatorSettings.GetSysModulesDir();
|
||||
|
||||
const auto abs_path = std::filesystem::absolute(path).lexically_normal();
|
||||
const auto abs_sys_path = std::filesystem::absolute(sys_modules_path).lexically_normal();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
@ -8,11 +8,11 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/options.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_presenter.h"
|
||||
@ -244,8 +244,8 @@ void ShaderList::Draw() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Config::collectShadersForDebug()) {
|
||||
DrawCenteredText("Enable 'CollectShader' in config to see shaders");
|
||||
if (!EmulatorSettings.IsShaderCollect()) {
|
||||
DrawCenteredText("Enable 'shader_collect' in config to see shaders");
|
||||
End();
|
||||
return;
|
||||
}
|
||||
|
||||
647
src/core/emulator_settings.cpp
Normal file
647
src/core/emulator_settings.cpp
Normal file
@ -0,0 +1,647 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <common/path_util.h>
|
||||
#include <common/scm_rev.h>
|
||||
#include <toml.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "emulator_settings.h"
|
||||
#include "emulator_state.h"
|
||||
|
||||
#include <SDL3/SDL_messagebox.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// ── Singleton storage ─────────────────────────────────────────────────
|
||||
std::shared_ptr<EmulatorSettingsImpl> EmulatorSettingsImpl::s_instance = nullptr;
|
||||
std::mutex EmulatorSettingsImpl::s_mutex;
|
||||
|
||||
// ── nlohmann helpers for std::filesystem::path ───────────────────────
|
||||
namespace nlohmann {
|
||||
template <>
|
||||
struct adl_serializer<std::filesystem::path> {
|
||||
static void to_json(json& j, const std::filesystem::path& p) {
|
||||
j = p.string();
|
||||
}
|
||||
static void from_json(const json& j, std::filesystem::path& p) {
|
||||
p = j.get<std::string>();
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
namespace toml {
|
||||
// why is it so hard to avoid exceptions with this library
|
||||
template <typename T>
|
||||
std::optional<T> get_optional(const toml::value& v, const std::string& key) {
|
||||
if (!v.is_table())
|
||||
return std::nullopt;
|
||||
const auto& tbl = v.as_table();
|
||||
auto it = tbl.find(key);
|
||||
if (it == tbl.end())
|
||||
return std::nullopt;
|
||||
|
||||
if constexpr (std::is_same_v<T, int>) {
|
||||
if (it->second.is_integer()) {
|
||||
return static_cast<int>(toml::get<int>(it->second));
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, unsigned int>) {
|
||||
if (it->second.is_integer()) {
|
||||
return static_cast<u32>(toml::get<unsigned int>(it->second));
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, double>) {
|
||||
if (it->second.is_floating()) {
|
||||
return toml::get<double>(it->second);
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||
if (it->second.is_string()) {
|
||||
return toml::get<std::string>(it->second);
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, std::filesystem::path>) {
|
||||
if (it->second.is_string()) {
|
||||
return toml::get<std::string>(it->second);
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, bool>) {
|
||||
if (it->second.is_boolean()) {
|
||||
return toml::get<bool>(it->second);
|
||||
}
|
||||
} else {
|
||||
static_assert([] { return false; }(), "Unsupported type in get_optional<T>");
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace toml
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
void EmulatorSettingsImpl::PrintChangedSummary(const std::vector<std::string>& changed) {
|
||||
if (changed.empty()) {
|
||||
LOG_DEBUG(EmuSettings, "No game-specific overrides applied");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(EmuSettings, "Game-specific overrides applied:");
|
||||
for (const auto& k : changed)
|
||||
LOG_DEBUG(EmuSettings, " * {}", k);
|
||||
}
|
||||
|
||||
// ── Singleton ────────────────────────────────────────────────────────
|
||||
EmulatorSettingsImpl::EmulatorSettingsImpl() = default;
|
||||
|
||||
EmulatorSettingsImpl::~EmulatorSettingsImpl() {
|
||||
Save();
|
||||
}
|
||||
|
||||
std::shared_ptr<EmulatorSettingsImpl> EmulatorSettingsImpl::GetInstance() {
|
||||
std::lock_guard lock(s_mutex);
|
||||
if (!s_instance)
|
||||
s_instance = std::make_shared<EmulatorSettingsImpl>();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetInstance(std::shared_ptr<EmulatorSettingsImpl> instance) {
|
||||
std::lock_guard lock(s_mutex);
|
||||
s_instance = std::move(instance);
|
||||
}
|
||||
|
||||
// --------------------
|
||||
// General helpers
|
||||
// --------------------
|
||||
bool EmulatorSettingsImpl::AddGameInstallDir(const std::filesystem::path& dir, bool enabled) {
|
||||
for (const auto& d : m_general.install_dirs.value)
|
||||
if (d.path == dir)
|
||||
return false;
|
||||
m_general.install_dirs.value.push_back({dir, enabled});
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> EmulatorSettingsImpl::GetGameInstallDirs() const {
|
||||
std::vector<std::filesystem::path> out;
|
||||
for (const auto& d : m_general.install_dirs.value)
|
||||
if (d.enabled)
|
||||
out.push_back(d.path);
|
||||
return out;
|
||||
}
|
||||
|
||||
const std::vector<GameInstallDir>& EmulatorSettingsImpl::GetAllGameInstallDirs() const {
|
||||
return m_general.install_dirs.value;
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetAllGameInstallDirs(const std::vector<GameInstallDir>& dirs) {
|
||||
m_general.install_dirs.value = dirs;
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::RemoveGameInstallDir(const std::filesystem::path& dir) {
|
||||
auto iterator =
|
||||
std::find_if(m_general.install_dirs.value.begin(), m_general.install_dirs.value.end(),
|
||||
[&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; });
|
||||
if (iterator != m_general.install_dirs.value.end()) {
|
||||
m_general.install_dirs.value.erase(iterator);
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetGameInstallDirEnabled(const std::filesystem::path& dir,
|
||||
bool enabled) {
|
||||
auto iterator =
|
||||
std::find_if(m_general.install_dirs.value.begin(), m_general.install_dirs.value.end(),
|
||||
[&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; });
|
||||
if (iterator != m_general.install_dirs.value.end()) {
|
||||
iterator->enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetGameInstallDirs(
|
||||
const std::vector<std::filesystem::path>& dirs_config) {
|
||||
m_general.install_dirs.value.clear();
|
||||
for (const auto& dir : dirs_config) {
|
||||
m_general.install_dirs.value.push_back({dir, true});
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<bool> EmulatorSettingsImpl::GetGameInstallDirsEnabled() {
|
||||
std::vector<bool> enabled_dirs;
|
||||
for (const auto& dir : m_general.install_dirs.value) {
|
||||
enabled_dirs.push_back(dir.enabled);
|
||||
}
|
||||
return enabled_dirs;
|
||||
}
|
||||
|
||||
std::filesystem::path EmulatorSettingsImpl::GetHomeDir() {
|
||||
if (m_general.home_dir.value.empty()) {
|
||||
return Common::FS::GetUserPath(Common::FS::PathType::HomeDir);
|
||||
}
|
||||
return m_general.home_dir.value;
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetHomeDir(const std::filesystem::path& dir) {
|
||||
m_general.home_dir.value = dir;
|
||||
}
|
||||
|
||||
std::filesystem::path EmulatorSettingsImpl::GetSysModulesDir() {
|
||||
if (m_general.sys_modules_dir.value.empty()) {
|
||||
return Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
|
||||
}
|
||||
return m_general.sys_modules_dir.value;
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetSysModulesDir(const std::filesystem::path& dir) {
|
||||
m_general.sys_modules_dir.value = dir;
|
||||
}
|
||||
|
||||
std::filesystem::path EmulatorSettingsImpl::GetFontsDir() {
|
||||
if (m_general.font_dir.value.empty()) {
|
||||
return Common::FS::GetUserPath(Common::FS::PathType::FontsDir);
|
||||
}
|
||||
return m_general.font_dir.value;
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetFontsDir(const std::filesystem::path& dir) {
|
||||
m_general.font_dir.value = dir;
|
||||
}
|
||||
|
||||
// ── Game-specific override management ────────────────────────────────
|
||||
void EmulatorSettingsImpl::ClearGameSpecificOverrides() {
|
||||
ClearGroupOverrides(m_general);
|
||||
ClearGroupOverrides(m_debug);
|
||||
ClearGroupOverrides(m_input);
|
||||
ClearGroupOverrides(m_audio);
|
||||
ClearGroupOverrides(m_gpu);
|
||||
ClearGroupOverrides(m_vulkan);
|
||||
LOG_DEBUG(EmuSettings, "All game-specific overrides cleared");
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::ResetGameSpecificValue(const std::string& key) {
|
||||
// Walk every overrideable group until we find the matching key.
|
||||
auto tryGroup = [&key](auto& group) {
|
||||
for (auto& item : group.GetOverrideableFields()) {
|
||||
if (key == item.key) {
|
||||
item.reset_game_specific(&group);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (tryGroup(m_general))
|
||||
return;
|
||||
if (tryGroup(m_debug))
|
||||
return;
|
||||
if (tryGroup(m_input))
|
||||
return;
|
||||
if (tryGroup(m_audio))
|
||||
return;
|
||||
if (tryGroup(m_gpu))
|
||||
return;
|
||||
if (tryGroup(m_vulkan))
|
||||
return;
|
||||
LOG_WARNING(EmuSettings, "ResetGameSpecificValue: key '{}' not found", key);
|
||||
}
|
||||
|
||||
bool EmulatorSettingsImpl::Save(const std::string& serial) {
|
||||
try {
|
||||
if (!serial.empty()) {
|
||||
const auto cfgDir = Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs);
|
||||
std::filesystem::create_directories(cfgDir);
|
||||
const auto path = cfgDir / (serial + ".json");
|
||||
|
||||
json j = json::object();
|
||||
|
||||
json generalObj = json::object();
|
||||
SaveGroupGameSpecific(m_general, generalObj);
|
||||
j["General"] = generalObj;
|
||||
|
||||
json debugObj = json::object();
|
||||
SaveGroupGameSpecific(m_debug, debugObj);
|
||||
j["Debug"] = debugObj;
|
||||
|
||||
json inputObj = json::object();
|
||||
SaveGroupGameSpecific(m_input, inputObj);
|
||||
j["Input"] = inputObj;
|
||||
|
||||
json audioObj = json::object();
|
||||
SaveGroupGameSpecific(m_audio, audioObj);
|
||||
j["Audio"] = audioObj;
|
||||
|
||||
json gpuObj = json::object();
|
||||
SaveGroupGameSpecific(m_gpu, gpuObj);
|
||||
j["GPU"] = gpuObj;
|
||||
|
||||
json vulkanObj = json::object();
|
||||
SaveGroupGameSpecific(m_vulkan, vulkanObj);
|
||||
j["Vulkan"] = vulkanObj;
|
||||
|
||||
std::ofstream out(path);
|
||||
if (!out) {
|
||||
LOG_ERROR(EmuSettings, "Failed to open game config for writing: {}", path.string());
|
||||
return false;
|
||||
}
|
||||
out << std::setw(2) << j;
|
||||
return !out.fail();
|
||||
|
||||
} else {
|
||||
// ── Global config.json ─────────────────────────────────────
|
||||
const auto path =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.json";
|
||||
|
||||
SetConfigVersion(Common::g_scm_rev);
|
||||
|
||||
json j;
|
||||
j["General"] = m_general;
|
||||
j["Debug"] = m_debug;
|
||||
j["Input"] = m_input;
|
||||
j["Audio"] = m_audio;
|
||||
j["GPU"] = m_gpu;
|
||||
j["Vulkan"] = m_vulkan;
|
||||
|
||||
// Read the existing file so we can preserve keys unknown to this build
|
||||
json existing = json::object();
|
||||
if (std::ifstream existingIn{path}; existingIn.good()) {
|
||||
try {
|
||||
existingIn >> existing;
|
||||
} catch (...) {
|
||||
existing = json::object();
|
||||
}
|
||||
}
|
||||
|
||||
// Merge: update each section's known keys, but leave unknown keys intact
|
||||
for (auto& [section, val] : j.items()) {
|
||||
if (existing.contains(section) && existing[section].is_object() && val.is_object())
|
||||
existing[section].update(val); // overwrites known keys, keeps unknown ones
|
||||
else
|
||||
existing[section] = val;
|
||||
}
|
||||
|
||||
std::ofstream out(path);
|
||||
if (!out) {
|
||||
LOG_ERROR(EmuSettings, "Failed to open config for writing: {}", path.string());
|
||||
return false;
|
||||
}
|
||||
out << std::setw(2) << existing;
|
||||
return !out.fail();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(EmuSettings, "Error saving settings: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Load ──────────────────────────────────────────────────────────────
|
||||
|
||||
bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
try {
|
||||
if (serial.empty()) {
|
||||
// ── Global config ──────────────────────────────────────────
|
||||
const auto userDir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
const auto configPath = userDir / "config.json";
|
||||
LOG_DEBUG(EmuSettings, "Loading global config from: {}", configPath.string());
|
||||
|
||||
if (std::ifstream in{configPath}; in.good()) {
|
||||
json gj;
|
||||
in >> gj;
|
||||
|
||||
auto mergeGroup = [&gj](auto& group, const char* section) {
|
||||
if (!gj.contains(section))
|
||||
return;
|
||||
json current = group;
|
||||
current.update(gj.at(section));
|
||||
group = current.get<std::remove_reference_t<decltype(group)>>();
|
||||
};
|
||||
|
||||
mergeGroup(m_general, "General");
|
||||
mergeGroup(m_debug, "Debug");
|
||||
mergeGroup(m_input, "Input");
|
||||
mergeGroup(m_audio, "Audio");
|
||||
mergeGroup(m_gpu, "GPU");
|
||||
mergeGroup(m_vulkan, "Vulkan");
|
||||
|
||||
LOG_DEBUG(EmuSettings, "Global config loaded successfully");
|
||||
} else {
|
||||
if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
|
||||
"config.toml")) {
|
||||
SDL_MessageBoxButtonData btns[2]{
|
||||
{0, 0, "No"},
|
||||
{0, 1, "Yes"},
|
||||
};
|
||||
SDL_MessageBoxData msg_box{
|
||||
0,
|
||||
nullptr,
|
||||
"Config Migration",
|
||||
"The shadPS4 config backend has been updated, and you only have "
|
||||
"the old version of the config. Do you wish to update it "
|
||||
"automatically, or continue with the default config?",
|
||||
2,
|
||||
btns,
|
||||
nullptr,
|
||||
};
|
||||
int result = 1;
|
||||
SDL_ShowMessageBox(&msg_box, &result);
|
||||
if (result == 1) {
|
||||
if (TransferSettings()) {
|
||||
Save();
|
||||
return true;
|
||||
} else {
|
||||
SDL_ShowSimpleMessageBox(0, "Config Migration",
|
||||
"Error transferring settings, exiting.",
|
||||
nullptr);
|
||||
std::quick_exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(EmuSettings, "Global config not found - using defaults");
|
||||
SetDefaultValues();
|
||||
Save();
|
||||
}
|
||||
if (GetConfigVersion() != Common::g_scm_rev) {
|
||||
Save();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// ── Per-game override file ─────────────────────────────────
|
||||
// Never reloads global settings. Only applies
|
||||
// game_specific_value overrides on top of the already-loaded
|
||||
// base configuration.
|
||||
const auto gamePath =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serial + ".json");
|
||||
LOG_DEBUG(EmuSettings, "Applying game config: {}", gamePath.string());
|
||||
|
||||
if (!std::filesystem::exists(gamePath)) {
|
||||
LOG_DEBUG(EmuSettings, "No game-specific config found for {}", serial);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream in(gamePath);
|
||||
if (!in) {
|
||||
LOG_ERROR(EmuSettings, "Failed to open game config: {}", gamePath.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
json gj;
|
||||
in >> gj;
|
||||
|
||||
std::vector<std::string> changed;
|
||||
|
||||
// ApplyGroupOverrides now correctly stores values as
|
||||
// game_specific_value (see make_override in the header).
|
||||
// ConfigMode::Default will then resolve them at getter call
|
||||
// time without ever touching the base values.
|
||||
if (gj.contains("General"))
|
||||
ApplyGroupOverrides(m_general, gj.at("General"), changed);
|
||||
if (gj.contains("Debug"))
|
||||
ApplyGroupOverrides(m_debug, gj.at("Debug"), changed);
|
||||
if (gj.contains("Input"))
|
||||
ApplyGroupOverrides(m_input, gj.at("Input"), changed);
|
||||
if (gj.contains("Audio"))
|
||||
ApplyGroupOverrides(m_audio, gj.at("Audio"), changed);
|
||||
if (gj.contains("GPU"))
|
||||
ApplyGroupOverrides(m_gpu, gj.at("GPU"), changed);
|
||||
if (gj.contains("Vulkan"))
|
||||
ApplyGroupOverrides(m_vulkan, gj.at("Vulkan"), changed);
|
||||
|
||||
PrintChangedSummary(changed);
|
||||
EmulatorState::GetInstance()->SetGameSpecifigConfigUsed(true);
|
||||
return true;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(EmuSettings, "Error loading settings: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::SetDefaultValues() {
|
||||
m_general = GeneralSettings{};
|
||||
m_debug = DebugSettings{};
|
||||
m_input = InputSettings{};
|
||||
m_audio = AudioSettings{};
|
||||
m_gpu = GPUSettings{};
|
||||
m_vulkan = VulkanSettings{};
|
||||
}
|
||||
|
||||
bool EmulatorSettingsImpl::TransferSettings() {
|
||||
toml::value og_data;
|
||||
json new_data = json::object();
|
||||
try {
|
||||
auto path = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml";
|
||||
std::ifstream ifs;
|
||||
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
ifs.open(path, std::ios_base::binary);
|
||||
og_data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
} catch (std::exception& ex) {
|
||||
fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what());
|
||||
return false;
|
||||
}
|
||||
auto setFromToml = [&]<typename T>(Setting<T>& n, toml::value const& t, std::string k) {
|
||||
n = toml::get_optional<T>(t, k).value_or(n.default_value);
|
||||
};
|
||||
if (og_data.contains("General")) {
|
||||
const toml::value& general = og_data.at("General");
|
||||
auto& s = m_general;
|
||||
|
||||
setFromToml(s.volume_slider, general, "volumeSlider");
|
||||
setFromToml(s.neo_mode, general, "isPS4Pro");
|
||||
setFromToml(s.dev_kit_mode, general, "isDevKit");
|
||||
setFromToml(s.psn_signed_in, general, "isPSNSignedIn");
|
||||
setFromToml(s.trophy_popup_disabled, general, "isTrophyPopupDisabled");
|
||||
setFromToml(s.trophy_notification_duration, general, "trophyNotificationDuration");
|
||||
setFromToml(s.discord_rpc_enabled, general, "enableDiscordRPC");
|
||||
setFromToml(s.log_filter, general, "logFilter");
|
||||
setFromToml(s.log_type, general, "logType");
|
||||
setFromToml(s.identical_log_grouped, general, "isIdenticalLogGrouped");
|
||||
setFromToml(s.show_splash, general, "showSplash");
|
||||
setFromToml(s.trophy_notification_side, general, "sideTrophy");
|
||||
setFromToml(s.connected_to_network, general, "isConnectedToNetwork");
|
||||
setFromToml(s.sys_modules_dir, general, "sysModulesPath");
|
||||
setFromToml(s.font_dir, general, "fontsPath");
|
||||
// setFromToml(, general, "userName");
|
||||
// setFromToml(s.defaultControllerID, general, "defaultControllerID");
|
||||
}
|
||||
|
||||
if (og_data.contains("Input")) {
|
||||
const toml::value& input = og_data.at("Input");
|
||||
auto& s = m_input;
|
||||
|
||||
setFromToml(s.cursor_state, input, "cursorState");
|
||||
setFromToml(s.cursor_hide_timeout, input, "cursorHideTimeout");
|
||||
setFromToml(s.use_special_pad, input, "useSpecialPad");
|
||||
setFromToml(s.special_pad_class, input, "specialPadClass");
|
||||
setFromToml(s.motion_controls_enabled, input, "isMotionControlsEnabled");
|
||||
setFromToml(s.use_unified_input_config, input, "useUnifiedInputConfig");
|
||||
setFromToml(s.background_controller_input, input, "backgroundControllerInput");
|
||||
setFromToml(s.usb_device_backend, input, "usbDeviceBackend");
|
||||
}
|
||||
|
||||
if (og_data.contains("Audio")) {
|
||||
const toml::value& audio = og_data.at("Audio");
|
||||
auto& s = m_audio;
|
||||
|
||||
setFromToml(s.sdl_mic_device, audio, "micDevice");
|
||||
setFromToml(s.sdl_main_output_device, audio, "mainOutputDevice");
|
||||
setFromToml(s.sdl_padSpk_output_device, audio, "padSpkOutputDevice");
|
||||
}
|
||||
|
||||
if (og_data.contains("GPU")) {
|
||||
const toml::value& gpu = og_data.at("GPU");
|
||||
auto& s = m_gpu;
|
||||
|
||||
setFromToml(s.window_width, gpu, "screenWidth");
|
||||
setFromToml(s.window_height, gpu, "screenHeight");
|
||||
setFromToml(s.internal_screen_width, gpu, "internalScreenWidth");
|
||||
setFromToml(s.internal_screen_height, gpu, "internalScreenHeight");
|
||||
setFromToml(s.null_gpu, gpu, "nullGpu");
|
||||
setFromToml(s.copy_gpu_buffers, gpu, "copyGPUBuffers");
|
||||
setFromToml(s.readbacks_mode, gpu, "readbacksMode");
|
||||
setFromToml(s.readback_linear_images_enabled, gpu, "readbackLinearImages");
|
||||
setFromToml(s.direct_memory_access_enabled, gpu, "directMemoryAccess");
|
||||
setFromToml(s.dump_shaders, gpu, "dumpShaders");
|
||||
setFromToml(s.patch_shaders, gpu, "patchShaders");
|
||||
setFromToml(s.vblank_frequency, gpu, "vblankFrequency");
|
||||
setFromToml(s.full_screen, gpu, "Fullscreen");
|
||||
setFromToml(s.full_screen_mode, gpu, "FullscreenMode");
|
||||
setFromToml(s.present_mode, gpu, "presentMode");
|
||||
setFromToml(s.hdr_allowed, gpu, "allowHDR");
|
||||
setFromToml(s.fsr_enabled, gpu, "fsrEnabled");
|
||||
setFromToml(s.rcas_enabled, gpu, "rcasEnabled");
|
||||
setFromToml(s.rcas_attenuation, gpu, "rcasAttenuation");
|
||||
}
|
||||
|
||||
if (og_data.contains("Vulkan")) {
|
||||
const toml::value& vk = og_data.at("Vulkan");
|
||||
auto& s = m_vulkan;
|
||||
|
||||
setFromToml(s.gpu_id, vk, "gpuId");
|
||||
setFromToml(s.vkvalidation_enabled, vk, "validation");
|
||||
setFromToml(s.vkvalidation_core_enabled, vk, "validation_core");
|
||||
setFromToml(s.vkvalidation_sync_enabled, vk, "validation_sync");
|
||||
setFromToml(s.vkvalidation_gpu_enabled, vk, "validation_gpu");
|
||||
setFromToml(s.vkcrash_diagnostic_enabled, vk, "crashDiagnostic");
|
||||
setFromToml(s.vkhost_markers, vk, "hostMarkers");
|
||||
setFromToml(s.vkguest_markers, vk, "guestMarkers");
|
||||
setFromToml(s.renderdoc_enabled, vk, "rdocEnable");
|
||||
setFromToml(s.pipeline_cache_enabled, vk, "pipelineCacheEnable");
|
||||
setFromToml(s.pipeline_cache_archived, vk, "pipelineCacheArchive");
|
||||
}
|
||||
|
||||
if (og_data.contains("Debug")) {
|
||||
const toml::value& debug = og_data.at("Debug");
|
||||
auto& s = m_debug;
|
||||
|
||||
setFromToml(s.debug_dump, debug, "DebugDump");
|
||||
setFromToml(s.separate_logging_enabled, debug, "isSeparateLogFilesEnabled");
|
||||
setFromToml(s.shader_collect, debug, "CollectShader");
|
||||
setFromToml(s.log_enabled, debug, "logEnabled");
|
||||
setFromToml(m_general.show_fps_counter, debug, "showFpsCounter");
|
||||
}
|
||||
|
||||
if (og_data.contains("Settings")) {
|
||||
const toml::value& settings = og_data.at("Settings");
|
||||
auto& s = m_general;
|
||||
setFromToml(s.console_language, settings, "consoleLanguage");
|
||||
}
|
||||
|
||||
if (og_data.contains("GUI")) {
|
||||
const toml::value& gui = og_data.at("GUI");
|
||||
auto& s = m_general;
|
||||
|
||||
// Transfer install directories
|
||||
try {
|
||||
const auto install_dir_array =
|
||||
toml::find_or<std::vector<std::string>>(gui, "installDirs", {});
|
||||
std::vector<bool> install_dirs_enabled;
|
||||
|
||||
try {
|
||||
install_dirs_enabled = toml::find<std::vector<bool>>(gui, "installDirsEnabled");
|
||||
} catch (...) {
|
||||
// If it does not exist, assume that all are enabled.
|
||||
install_dirs_enabled.resize(install_dir_array.size(), true);
|
||||
}
|
||||
|
||||
if (install_dirs_enabled.size() < install_dir_array.size()) {
|
||||
install_dirs_enabled.resize(install_dir_array.size(), true);
|
||||
}
|
||||
|
||||
std::vector<GameInstallDir> settings_install_dirs;
|
||||
for (size_t i = 0; i < install_dir_array.size(); i++) {
|
||||
settings_install_dirs.push_back(
|
||||
{std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]});
|
||||
}
|
||||
s.install_dirs.value = settings_install_dirs;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARNING(EmuSettings, "Failed to transfer install directories: {}", e.what());
|
||||
}
|
||||
|
||||
// Transfer addon install directory
|
||||
try {
|
||||
std::string addon_install_dir_str;
|
||||
if (gui.contains("addonInstallDir")) {
|
||||
const auto& addon_value = gui.at("addonInstallDir");
|
||||
if (addon_value.is_string()) {
|
||||
addon_install_dir_str = toml::get<std::string>(addon_value);
|
||||
if (!addon_install_dir_str.empty()) {
|
||||
s.addon_install_dir.value = std::filesystem::path{addon_install_dir_str};
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARNING(EmuSettings, "Failed to transfer addon install directory: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> EmulatorSettingsImpl::GetAllOverrideableKeys() const {
|
||||
std::vector<std::string> keys;
|
||||
auto addGroup = [&keys](const auto& fields) {
|
||||
for (const auto& item : fields)
|
||||
keys.push_back(item.key);
|
||||
};
|
||||
addGroup(m_general.GetOverrideableFields());
|
||||
addGroup(m_debug.GetOverrideableFields());
|
||||
addGroup(m_input.GetOverrideableFields());
|
||||
addGroup(m_audio.GetOverrideableFields());
|
||||
addGroup(m_gpu.GetOverrideableFields());
|
||||
addGroup(m_vulkan.GetOverrideableFields());
|
||||
return keys;
|
||||
}
|
||||
636
src/core/emulator_settings.h
Normal file
636
src/core/emulator_settings.h
Normal file
@ -0,0 +1,636 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#define EmulatorSettings (*EmulatorSettingsImpl::GetInstance())
|
||||
|
||||
enum HideCursorState : int {
|
||||
Never,
|
||||
Idle,
|
||||
Always,
|
||||
};
|
||||
|
||||
enum UsbBackendType : int {
|
||||
Real,
|
||||
SkylandersPortal,
|
||||
InfinityBase,
|
||||
DimensionsToypad,
|
||||
};
|
||||
|
||||
enum GpuReadbacksMode : int {
|
||||
Disabled,
|
||||
Relaxed,
|
||||
Precise,
|
||||
};
|
||||
|
||||
enum class ConfigMode {
|
||||
Default,
|
||||
Global,
|
||||
Clean,
|
||||
};
|
||||
|
||||
enum AudioBackend : int {
|
||||
SDL,
|
||||
OpenAL,
|
||||
// Add more backends as needed
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Setting {
|
||||
T default_value{};
|
||||
T value{};
|
||||
std::optional<T> game_specific_value{};
|
||||
|
||||
Setting() = default;
|
||||
// Single-argument ctor: initialises both default_value and value so
|
||||
// that CleanMode can always recover the intended factory default.
|
||||
/*implicit*/ Setting(T init) : default_value(std::move(init)), value(default_value) {}
|
||||
|
||||
/// Return the active value under the given mode.
|
||||
T get(ConfigMode mode = ConfigMode::Default) const {
|
||||
switch (mode) {
|
||||
case ConfigMode::Default:
|
||||
return game_specific_value.value_or(value);
|
||||
case ConfigMode::Global:
|
||||
return value;
|
||||
case ConfigMode::Clean:
|
||||
return default_value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Write v to the base layer.
|
||||
/// Game-specific overrides are applied exclusively via Load(serial)
|
||||
void set(const T& v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
/// Discard the game-specific override; subsequent get(Default) will
|
||||
/// fall back to the base value.
|
||||
void reset_game_specific() {
|
||||
game_specific_value = std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void to_json(nlohmann::json& j, const Setting<T>& s) {
|
||||
j = s.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void from_json(const nlohmann::json& j, Setting<T>& s) {
|
||||
s.value = j.get<T>();
|
||||
}
|
||||
|
||||
struct OverrideItem {
|
||||
const char* key;
|
||||
std::function<void(void* group_ptr, const nlohmann::json& entry,
|
||||
std::vector<std::string>& changed)>
|
||||
apply;
|
||||
/// Return the value that should be written to the per-game config file.
|
||||
/// Falls back to base value if no game-specific override is set.
|
||||
std::function<nlohmann::json(const void* group_ptr)> get_for_save;
|
||||
|
||||
/// Clear game_specific_value for this field.
|
||||
std::function<void(void* group_ptr)> reset_game_specific;
|
||||
};
|
||||
|
||||
template <typename Struct, typename T>
|
||||
inline OverrideItem make_override(const char* key, Setting<T> Struct::* member) {
|
||||
return OverrideItem{
|
||||
key,
|
||||
[member, key](void* base, const nlohmann::json& entry, std::vector<std::string>& changed) {
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Processing key: {}", key);
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Entry JSON: {}", entry.dump());
|
||||
Struct* obj = reinterpret_cast<Struct*>(base);
|
||||
Setting<T>& dst = obj->*member;
|
||||
try {
|
||||
T newValue = entry.get<T>();
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Parsed value: {}", newValue);
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Current value: {}", dst.value);
|
||||
if (dst.value != newValue) {
|
||||
std::ostringstream oss;
|
||||
oss << key << " ( " << dst.value << " → " << newValue << " )";
|
||||
changed.push_back(oss.str());
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Recorded change: {}", oss.str());
|
||||
}
|
||||
dst.game_specific_value = newValue;
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Successfully updated {}", key);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(EmuSettings, "[make_override] ERROR parsing {}: {}", key, e.what());
|
||||
LOG_ERROR(EmuSettings, "[make_override] Entry was: {}", entry.dump());
|
||||
LOG_ERROR(EmuSettings, "[make_override] Type name: {}", entry.type_name());
|
||||
}
|
||||
},
|
||||
|
||||
// --- get_for_save -------------------------------------------
|
||||
// Returns game_specific_value when present, otherwise base value.
|
||||
// This means a freshly-opened game-specific dialog still shows
|
||||
// useful (current-global) values rather than empty entries.
|
||||
[member](const void* base) -> nlohmann::json {
|
||||
const Struct* obj = reinterpret_cast<const Struct*>(base);
|
||||
const Setting<T>& src = obj->*member;
|
||||
return nlohmann::json(src.game_specific_value.value_or(src.value));
|
||||
},
|
||||
|
||||
// --- reset_game_specific ------------------------------------
|
||||
[member](void* base) {
|
||||
Struct* obj = reinterpret_cast<Struct*>(base);
|
||||
(obj->*member).reset_game_specific();
|
||||
}};
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Support types
|
||||
// -------------------------------
|
||||
struct GameInstallDir {
|
||||
std::filesystem::path path;
|
||||
bool enabled;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GameInstallDir, path, enabled)
|
||||
|
||||
// -------------------------------
|
||||
// General settings
|
||||
// -------------------------------
|
||||
struct GeneralSettings {
|
||||
Setting<std::vector<GameInstallDir>> install_dirs;
|
||||
Setting<std::filesystem::path> addon_install_dir;
|
||||
Setting<std::filesystem::path> home_dir;
|
||||
Setting<std::filesystem::path> sys_modules_dir;
|
||||
Setting<std::filesystem::path> font_dir;
|
||||
|
||||
Setting<int> volume_slider{100};
|
||||
Setting<bool> neo_mode{false};
|
||||
Setting<bool> dev_kit_mode{false};
|
||||
Setting<int> extra_dmem_in_mbytes{0};
|
||||
Setting<bool> psn_signed_in{false};
|
||||
Setting<bool> trophy_popup_disabled{false};
|
||||
Setting<double> trophy_notification_duration{6.0};
|
||||
Setting<std::string> trophy_notification_side{"right"};
|
||||
Setting<std::string> log_filter{""};
|
||||
Setting<std::string> log_type{"sync"};
|
||||
Setting<bool> show_splash{false};
|
||||
Setting<bool> identical_log_grouped{true};
|
||||
Setting<bool> connected_to_network{false};
|
||||
Setting<bool> discord_rpc_enabled{false};
|
||||
Setting<bool> show_fps_counter{false};
|
||||
Setting<int> console_language{1};
|
||||
|
||||
// return a vector of override descriptors (runtime, but tiny)
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<GeneralSettings>("volume_slider", &GeneralSettings::volume_slider),
|
||||
make_override<GeneralSettings>("neo_mode", &GeneralSettings::neo_mode),
|
||||
make_override<GeneralSettings>("dev_kit_mode", &GeneralSettings::dev_kit_mode),
|
||||
make_override<GeneralSettings>("extra_dmem_in_mbytes",
|
||||
&GeneralSettings::extra_dmem_in_mbytes),
|
||||
make_override<GeneralSettings>("psn_signed_in", &GeneralSettings::psn_signed_in),
|
||||
make_override<GeneralSettings>("trophy_popup_disabled",
|
||||
&GeneralSettings::trophy_popup_disabled),
|
||||
make_override<GeneralSettings>("trophy_notification_duration",
|
||||
&GeneralSettings::trophy_notification_duration),
|
||||
make_override<GeneralSettings>("log_filter", &GeneralSettings::log_filter),
|
||||
make_override<GeneralSettings>("log_type", &GeneralSettings::log_type),
|
||||
make_override<GeneralSettings>("identical_log_grouped",
|
||||
&GeneralSettings::identical_log_grouped),
|
||||
make_override<GeneralSettings>("show_splash", &GeneralSettings::show_splash),
|
||||
make_override<GeneralSettings>("trophy_notification_side",
|
||||
&GeneralSettings::trophy_notification_side),
|
||||
make_override<GeneralSettings>("connected_to_network",
|
||||
&GeneralSettings::connected_to_network)};
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, install_dirs, addon_install_dir, home_dir,
|
||||
sys_modules_dir, font_dir, volume_slider, neo_mode, dev_kit_mode,
|
||||
extra_dmem_in_mbytes, psn_signed_in, trophy_popup_disabled,
|
||||
trophy_notification_duration, log_filter, log_type, show_splash,
|
||||
identical_log_grouped, trophy_notification_side,
|
||||
connected_to_network, discord_rpc_enabled, show_fps_counter,
|
||||
console_language)
|
||||
|
||||
// -------------------------------
|
||||
// Debug settings
|
||||
// -------------------------------
|
||||
struct DebugSettings {
|
||||
Setting<bool> separate_logging_enabled{false}; // specific
|
||||
Setting<bool> debug_dump{false}; // specific
|
||||
Setting<bool> shader_collect{false}; // specific
|
||||
Setting<bool> log_enabled{true}; // specific
|
||||
Setting<std::string> config_version{""}; // specific
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<DebugSettings>("debug_dump", &DebugSettings::debug_dump),
|
||||
make_override<DebugSettings>("shader_collect", &DebugSettings::shader_collect),
|
||||
make_override<DebugSettings>("separate_logging_enabled",
|
||||
&DebugSettings::separate_logging_enabled),
|
||||
make_override<DebugSettings>("log_enabled", &DebugSettings::log_enabled)};
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings, separate_logging_enabled, debug_dump,
|
||||
shader_collect, log_enabled, config_version)
|
||||
|
||||
// -------------------------------
|
||||
// Input settings
|
||||
// -------------------------------
|
||||
|
||||
struct InputSettings {
|
||||
Setting<int> cursor_state{HideCursorState::Idle}; // specific
|
||||
Setting<int> cursor_hide_timeout{5}; // specific
|
||||
Setting<int> usb_device_backend{UsbBackendType::Real}; // specific
|
||||
Setting<bool> use_special_pad{false};
|
||||
Setting<int> special_pad_class{1};
|
||||
Setting<bool> motion_controls_enabled{true}; // specific
|
||||
Setting<bool> use_unified_input_config{true};
|
||||
Setting<std::string> default_controller_id{""};
|
||||
Setting<bool> background_controller_input{false}; // specific
|
||||
Setting<s32> camera_id{-1};
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<InputSettings>("cursor_state", &InputSettings::cursor_state),
|
||||
make_override<InputSettings>("cursor_hide_timeout",
|
||||
&InputSettings::cursor_hide_timeout),
|
||||
make_override<InputSettings>("usb_device_backend", &InputSettings::usb_device_backend),
|
||||
make_override<InputSettings>("motion_controls_enabled",
|
||||
&InputSettings::motion_controls_enabled),
|
||||
make_override<InputSettings>("background_controller_input",
|
||||
&InputSettings::background_controller_input),
|
||||
make_override<InputSettings>("camera_id", &InputSettings::camera_id)};
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings, cursor_state, cursor_hide_timeout,
|
||||
usb_device_backend, use_special_pad, special_pad_class,
|
||||
motion_controls_enabled, use_unified_input_config,
|
||||
default_controller_id, background_controller_input, camera_id)
|
||||
// -------------------------------
|
||||
// Audio settings
|
||||
// -------------------------------
|
||||
struct AudioSettings {
|
||||
Setting<u32> audio_backend{AudioBackend::SDL};
|
||||
Setting<std::string> sdl_mic_device{"Default Device"};
|
||||
Setting<std::string> sdl_main_output_device{"Default Device"};
|
||||
Setting<std::string> sdl_padSpk_output_device{"Default Device"};
|
||||
Setting<std::string> openal_mic_device{"Default Device"};
|
||||
Setting<std::string> openal_main_output_device{"Default Device"};
|
||||
Setting<std::string> openal_padSpk_output_device{"Default Device"};
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<AudioSettings>("audio_backend", &AudioSettings::audio_backend),
|
||||
make_override<AudioSettings>("sdl_mic_device", &AudioSettings::sdl_mic_device),
|
||||
make_override<AudioSettings>("sdl_main_output_device",
|
||||
&AudioSettings::sdl_main_output_device),
|
||||
make_override<AudioSettings>("sdl_padSpk_output_device",
|
||||
&AudioSettings::sdl_padSpk_output_device),
|
||||
make_override<AudioSettings>("openal_mic_device", &AudioSettings::openal_mic_device),
|
||||
make_override<AudioSettings>("openal_main_output_device",
|
||||
&AudioSettings::openal_main_output_device),
|
||||
make_override<AudioSettings>("openal_padSpk_output_device",
|
||||
&AudioSettings::openal_padSpk_output_device)};
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AudioSettings, audio_backend, sdl_mic_device,
|
||||
sdl_main_output_device, sdl_padSpk_output_device,
|
||||
openal_mic_device, openal_main_output_device,
|
||||
openal_padSpk_output_device)
|
||||
|
||||
// -------------------------------
|
||||
// GPU settings
|
||||
// -------------------------------
|
||||
struct GPUSettings {
|
||||
Setting<u32> window_width{1280};
|
||||
Setting<u32> window_height{720};
|
||||
Setting<u32> internal_screen_width{1280};
|
||||
Setting<u32> internal_screen_height{720};
|
||||
Setting<bool> null_gpu{false};
|
||||
Setting<bool> copy_gpu_buffers{false};
|
||||
Setting<u32> readbacks_mode{GpuReadbacksMode::Disabled};
|
||||
Setting<bool> readback_linear_images_enabled{false};
|
||||
Setting<bool> direct_memory_access_enabled{false};
|
||||
Setting<bool> dump_shaders{false};
|
||||
Setting<bool> patch_shaders{false};
|
||||
Setting<u32> vblank_frequency{60};
|
||||
Setting<bool> full_screen{false};
|
||||
Setting<std::string> full_screen_mode{"Windowed"};
|
||||
Setting<std::string> present_mode{"Mailbox"};
|
||||
Setting<bool> hdr_allowed{false};
|
||||
Setting<bool> fsr_enabled{false};
|
||||
Setting<bool> rcas_enabled{true};
|
||||
Setting<int> rcas_attenuation{250};
|
||||
// TODO add overrides
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<GPUSettings>("null_gpu", &GPUSettings::null_gpu),
|
||||
make_override<GPUSettings>("copy_gpu_buffers", &GPUSettings::copy_gpu_buffers),
|
||||
make_override<GPUSettings>("full_screen", &GPUSettings::full_screen),
|
||||
make_override<GPUSettings>("full_screen_mode", &GPUSettings::full_screen_mode),
|
||||
make_override<GPUSettings>("present_mode", &GPUSettings::present_mode),
|
||||
make_override<GPUSettings>("window_height", &GPUSettings::window_height),
|
||||
make_override<GPUSettings>("window_width", &GPUSettings::window_width),
|
||||
make_override<GPUSettings>("hdr_allowed", &GPUSettings::hdr_allowed),
|
||||
make_override<GPUSettings>("fsr_enabled", &GPUSettings::fsr_enabled),
|
||||
make_override<GPUSettings>("rcas_enabled", &GPUSettings::rcas_enabled),
|
||||
make_override<GPUSettings>("rcas_attenuation", &GPUSettings::rcas_attenuation),
|
||||
make_override<GPUSettings>("dump_shaders", &GPUSettings::dump_shaders),
|
||||
make_override<GPUSettings>("patch_shaders", &GPUSettings::patch_shaders),
|
||||
make_override<GPUSettings>("readbacks_mode", &GPUSettings::readbacks_mode),
|
||||
make_override<GPUSettings>("readback_linear_images_enabled",
|
||||
&GPUSettings::readback_linear_images_enabled),
|
||||
make_override<GPUSettings>("direct_memory_access_enabled",
|
||||
&GPUSettings::direct_memory_access_enabled),
|
||||
make_override<GPUSettings>("vblank_frequency", &GPUSettings::vblank_frequency),
|
||||
};
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GPUSettings, window_width, window_height, internal_screen_width,
|
||||
internal_screen_height, null_gpu, copy_gpu_buffers,
|
||||
readbacks_mode, readback_linear_images_enabled,
|
||||
direct_memory_access_enabled, dump_shaders, patch_shaders,
|
||||
vblank_frequency, full_screen, full_screen_mode, present_mode,
|
||||
hdr_allowed, fsr_enabled, rcas_enabled, rcas_attenuation)
|
||||
// -------------------------------
|
||||
// Vulkan settings
|
||||
// -------------------------------
|
||||
struct VulkanSettings {
|
||||
Setting<s32> gpu_id{-1};
|
||||
Setting<bool> renderdoc_enabled{false};
|
||||
Setting<bool> vkvalidation_enabled{false};
|
||||
Setting<bool> vkvalidation_core_enabled{true};
|
||||
Setting<bool> vkvalidation_sync_enabled{false};
|
||||
Setting<bool> vkvalidation_gpu_enabled{false};
|
||||
Setting<bool> vkcrash_diagnostic_enabled{false};
|
||||
Setting<bool> vkhost_markers{false};
|
||||
Setting<bool> vkguest_markers{false};
|
||||
Setting<bool> pipeline_cache_enabled{false};
|
||||
Setting<bool> pipeline_cache_archived{false};
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<VulkanSettings>("gpu_id", &VulkanSettings::gpu_id),
|
||||
make_override<VulkanSettings>("renderdoc_enabled", &VulkanSettings::renderdoc_enabled),
|
||||
make_override<VulkanSettings>("vkvalidation_enabled",
|
||||
&VulkanSettings::vkvalidation_enabled),
|
||||
make_override<VulkanSettings>("vkvalidation_core_enabled",
|
||||
&VulkanSettings::vkvalidation_core_enabled),
|
||||
make_override<VulkanSettings>("vkvalidation_sync_enabled",
|
||||
&VulkanSettings::vkvalidation_sync_enabled),
|
||||
make_override<VulkanSettings>("vkvalidation_gpu_enabled",
|
||||
&VulkanSettings::vkvalidation_gpu_enabled),
|
||||
make_override<VulkanSettings>("vkcrash_diagnostic_enabled",
|
||||
&VulkanSettings::vkcrash_diagnostic_enabled),
|
||||
make_override<VulkanSettings>("vkhost_markers", &VulkanSettings::vkhost_markers),
|
||||
make_override<VulkanSettings>("vkguest_markers", &VulkanSettings::vkguest_markers),
|
||||
make_override<VulkanSettings>("pipeline_cache_enabled",
|
||||
&VulkanSettings::pipeline_cache_enabled),
|
||||
make_override<VulkanSettings>("pipeline_cache_archived",
|
||||
&VulkanSettings::pipeline_cache_archived),
|
||||
};
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(VulkanSettings, gpu_id, renderdoc_enabled, vkvalidation_enabled,
|
||||
vkvalidation_core_enabled, vkvalidation_sync_enabled,
|
||||
vkvalidation_gpu_enabled, vkcrash_diagnostic_enabled,
|
||||
vkhost_markers, vkguest_markers, pipeline_cache_enabled,
|
||||
pipeline_cache_archived)
|
||||
|
||||
// -------------------------------
|
||||
// Main manager
|
||||
// -------------------------------
|
||||
class EmulatorSettingsImpl {
|
||||
public:
|
||||
EmulatorSettingsImpl();
|
||||
~EmulatorSettingsImpl();
|
||||
|
||||
static std::shared_ptr<EmulatorSettingsImpl> GetInstance();
|
||||
static void SetInstance(std::shared_ptr<EmulatorSettingsImpl> instance);
|
||||
|
||||
bool Save(const std::string& serial = "");
|
||||
bool Load(const std::string& serial = "");
|
||||
void SetDefaultValues();
|
||||
bool TransferSettings();
|
||||
|
||||
// Config mode
|
||||
ConfigMode GetConfigMode() const {
|
||||
return m_configMode;
|
||||
}
|
||||
void SetConfigMode(ConfigMode mode) {
|
||||
m_configMode = mode;
|
||||
}
|
||||
|
||||
//
|
||||
// Game-specific override management
|
||||
/// Clears all per-game overrides. Call this when a game exits so
|
||||
/// the emulator reverts to global settings.
|
||||
void ClearGameSpecificOverrides();
|
||||
|
||||
/// Reset a single field's game-specific override by its JSON ke
|
||||
void ResetGameSpecificValue(const std::string& key);
|
||||
|
||||
// general accessors
|
||||
bool AddGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
|
||||
std::vector<std::filesystem::path> GetGameInstallDirs() const;
|
||||
void SetAllGameInstallDirs(const std::vector<GameInstallDir>& dirs);
|
||||
void RemoveGameInstallDir(const std::filesystem::path& dir);
|
||||
void SetGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled);
|
||||
void SetGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config);
|
||||
const std::vector<bool> GetGameInstallDirsEnabled();
|
||||
const std::vector<GameInstallDir>& GetAllGameInstallDirs() const;
|
||||
|
||||
std::filesystem::path GetHomeDir();
|
||||
void SetHomeDir(const std::filesystem::path& dir);
|
||||
std::filesystem::path GetSysModulesDir();
|
||||
void SetSysModulesDir(const std::filesystem::path& dir);
|
||||
std::filesystem::path GetFontsDir();
|
||||
void SetFontsDir(const std::filesystem::path& dir);
|
||||
|
||||
private:
|
||||
GeneralSettings m_general{};
|
||||
DebugSettings m_debug{};
|
||||
InputSettings m_input{};
|
||||
AudioSettings m_audio{};
|
||||
GPUSettings m_gpu{};
|
||||
VulkanSettings m_vulkan{};
|
||||
ConfigMode m_configMode{ConfigMode::Default};
|
||||
|
||||
static std::shared_ptr<EmulatorSettingsImpl> s_instance;
|
||||
static std::mutex s_mutex;
|
||||
|
||||
/// Apply overrideable fields from groupJson into group.game_specific_value.
|
||||
template <typename Group>
|
||||
void ApplyGroupOverrides(Group& group, const nlohmann::json& groupJson,
|
||||
std::vector<std::string>& changed) {
|
||||
for (auto& item : group.GetOverrideableFields()) {
|
||||
if (!groupJson.contains(item.key))
|
||||
continue;
|
||||
item.apply(&group, groupJson.at(item.key), changed);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all overrideable fields from group into out (for game-specific save).
|
||||
template <typename Group>
|
||||
static void SaveGroupGameSpecific(const Group& group, nlohmann::json& out) {
|
||||
for (auto& item : group.GetOverrideableFields())
|
||||
out[item.key] = item.get_for_save(&group);
|
||||
}
|
||||
|
||||
// Discard every game-specific override in group.
|
||||
template <typename Group>
|
||||
static void ClearGroupOverrides(Group& group) {
|
||||
for (auto& item : group.GetOverrideableFields())
|
||||
item.reset_game_specific(&group);
|
||||
}
|
||||
|
||||
static void PrintChangedSummary(const std::vector<std::string>& changed);
|
||||
|
||||
public:
|
||||
// Add these getters to access overrideable fields
|
||||
std::vector<OverrideItem> GetGeneralOverrideableFields() const {
|
||||
return m_general.GetOverrideableFields();
|
||||
}
|
||||
std::vector<OverrideItem> GetDebugOverrideableFields() const {
|
||||
return m_debug.GetOverrideableFields();
|
||||
}
|
||||
std::vector<OverrideItem> GetInputOverrideableFields() const {
|
||||
return m_input.GetOverrideableFields();
|
||||
}
|
||||
std::vector<OverrideItem> GetAudioOverrideableFields() const {
|
||||
return m_audio.GetOverrideableFields();
|
||||
}
|
||||
std::vector<OverrideItem> GetGPUOverrideableFields() const {
|
||||
return m_gpu.GetOverrideableFields();
|
||||
}
|
||||
std::vector<OverrideItem> GetVulkanOverrideableFields() const {
|
||||
return m_vulkan.GetOverrideableFields();
|
||||
}
|
||||
std::vector<std::string> GetAllOverrideableKeys() const;
|
||||
|
||||
#define SETTING_FORWARD(group, Name, field) \
|
||||
auto Get##Name() const { \
|
||||
return (group).field.get(m_configMode); \
|
||||
} \
|
||||
void Set##Name(const decltype((group).field.value)& v) { \
|
||||
(group).field.value = v; \
|
||||
}
|
||||
#define SETTING_FORWARD_BOOL(group, Name, field) \
|
||||
bool Is##Name() const { \
|
||||
return (group).field.get(m_configMode); \
|
||||
} \
|
||||
void Set##Name(bool v) { \
|
||||
(group).field.value = v; \
|
||||
}
|
||||
#define SETTING_FORWARD_BOOL_READONLY(group, Name, field) \
|
||||
bool Is##Name() const { \
|
||||
return (group).field.get(m_configMode); \
|
||||
}
|
||||
|
||||
// General settings
|
||||
SETTING_FORWARD(m_general, VolumeSlider, volume_slider)
|
||||
SETTING_FORWARD_BOOL(m_general, Neo, neo_mode)
|
||||
SETTING_FORWARD_BOOL(m_general, DevKit, dev_kit_mode)
|
||||
SETTING_FORWARD(m_general, ExtraDmemInMBytes, extra_dmem_in_mbytes)
|
||||
SETTING_FORWARD_BOOL(m_general, PSNSignedIn, psn_signed_in)
|
||||
SETTING_FORWARD_BOOL(m_general, TrophyPopupDisabled, trophy_popup_disabled)
|
||||
SETTING_FORWARD(m_general, TrophyNotificationDuration, trophy_notification_duration)
|
||||
SETTING_FORWARD(m_general, TrophyNotificationSide, trophy_notification_side)
|
||||
SETTING_FORWARD_BOOL(m_general, ShowSplash, show_splash)
|
||||
SETTING_FORWARD_BOOL(m_general, IdenticalLogGrouped, identical_log_grouped)
|
||||
SETTING_FORWARD(m_general, AddonInstallDir, addon_install_dir)
|
||||
SETTING_FORWARD(m_general, LogFilter, log_filter)
|
||||
SETTING_FORWARD(m_general, LogType, log_type)
|
||||
SETTING_FORWARD_BOOL(m_general, ConnectedToNetwork, connected_to_network)
|
||||
SETTING_FORWARD_BOOL(m_general, DiscordRPCEnabled, discord_rpc_enabled)
|
||||
SETTING_FORWARD_BOOL(m_general, ShowFpsCounter, show_fps_counter)
|
||||
SETTING_FORWARD(m_general, ConsoleLanguage, console_language)
|
||||
|
||||
// Audio settings
|
||||
SETTING_FORWARD(m_audio, AudioBackend, audio_backend)
|
||||
SETTING_FORWARD(m_audio, SDLMicDevice, sdl_mic_device)
|
||||
SETTING_FORWARD(m_audio, SDLMainOutputDevice, sdl_main_output_device)
|
||||
SETTING_FORWARD(m_audio, SDLPadSpkOutputDevice, sdl_padSpk_output_device)
|
||||
SETTING_FORWARD(m_audio, OpenALMicDevice, openal_mic_device)
|
||||
SETTING_FORWARD(m_audio, OpenALMainOutputDevice, openal_main_output_device)
|
||||
SETTING_FORWARD(m_audio, OpenALPadSpkOutputDevice, openal_padSpk_output_device)
|
||||
|
||||
// Debug settings
|
||||
SETTING_FORWARD_BOOL(m_debug, SeparateLoggingEnabled, separate_logging_enabled)
|
||||
SETTING_FORWARD_BOOL(m_debug, DebugDump, debug_dump)
|
||||
SETTING_FORWARD_BOOL(m_debug, ShaderCollect, shader_collect)
|
||||
SETTING_FORWARD_BOOL(m_debug, LogEnabled, log_enabled)
|
||||
SETTING_FORWARD(m_debug, ConfigVersion, config_version)
|
||||
|
||||
// GPU Settings
|
||||
SETTING_FORWARD_BOOL(m_gpu, NullGPU, null_gpu)
|
||||
SETTING_FORWARD_BOOL(m_gpu, DumpShaders, dump_shaders)
|
||||
SETTING_FORWARD_BOOL(m_gpu, CopyGpuBuffers, copy_gpu_buffers)
|
||||
SETTING_FORWARD_BOOL(m_gpu, FullScreen, full_screen)
|
||||
SETTING_FORWARD(m_gpu, FullScreenMode, full_screen_mode)
|
||||
SETTING_FORWARD(m_gpu, PresentMode, present_mode)
|
||||
SETTING_FORWARD(m_gpu, WindowHeight, window_height)
|
||||
SETTING_FORWARD(m_gpu, WindowWidth, window_width)
|
||||
SETTING_FORWARD(m_gpu, InternalScreenHeight, internal_screen_height)
|
||||
SETTING_FORWARD(m_gpu, InternalScreenWidth, internal_screen_width)
|
||||
SETTING_FORWARD_BOOL(m_gpu, HdrAllowed, hdr_allowed)
|
||||
SETTING_FORWARD_BOOL(m_gpu, FsrEnabled, fsr_enabled)
|
||||
SETTING_FORWARD_BOOL(m_gpu, RcasEnabled, rcas_enabled)
|
||||
SETTING_FORWARD(m_gpu, RcasAttenuation, rcas_attenuation)
|
||||
SETTING_FORWARD(m_gpu, ReadbacksMode, readbacks_mode)
|
||||
SETTING_FORWARD_BOOL(m_gpu, ReadbackLinearImagesEnabled, readback_linear_images_enabled)
|
||||
SETTING_FORWARD_BOOL(m_gpu, DirectMemoryAccessEnabled, direct_memory_access_enabled)
|
||||
SETTING_FORWARD_BOOL_READONLY(m_gpu, PatchShaders, patch_shaders)
|
||||
|
||||
u32 GetVblankFrequency() {
|
||||
if (m_gpu.vblank_frequency.value < 60) {
|
||||
m_gpu.vblank_frequency.value = 60;
|
||||
}
|
||||
return m_gpu.vblank_frequency.value;
|
||||
}
|
||||
void SetVblankFrequency(const u32& v) {
|
||||
if (v < 60) {
|
||||
m_gpu.vblank_frequency.value = 60;
|
||||
} else {
|
||||
m_gpu.vblank_frequency.value = v;
|
||||
}
|
||||
}
|
||||
|
||||
// Input Settings
|
||||
SETTING_FORWARD(m_input, CursorState, cursor_state)
|
||||
SETTING_FORWARD(m_input, CursorHideTimeout, cursor_hide_timeout)
|
||||
SETTING_FORWARD(m_input, UsbDeviceBackend, usb_device_backend)
|
||||
SETTING_FORWARD_BOOL(m_input, MotionControlsEnabled, motion_controls_enabled)
|
||||
SETTING_FORWARD_BOOL(m_input, BackgroundControllerInput, background_controller_input)
|
||||
SETTING_FORWARD(m_input, DefaultControllerId, default_controller_id)
|
||||
SETTING_FORWARD_BOOL(m_input, UsingSpecialPad, use_special_pad)
|
||||
SETTING_FORWARD(m_input, SpecialPadClass, special_pad_class)
|
||||
SETTING_FORWARD_BOOL(m_input, UseUnifiedInputConfig, use_unified_input_config)
|
||||
SETTING_FORWARD(m_input, CameraId, camera_id)
|
||||
|
||||
// Vulkan settings
|
||||
SETTING_FORWARD(m_vulkan, GpuId, gpu_id)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, RenderdocEnabled, renderdoc_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkValidationEnabled, vkvalidation_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkValidationCoreEnabled, vkvalidation_core_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkValidationSyncEnabled, vkvalidation_sync_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkValidationGpuEnabled, vkvalidation_gpu_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkCrashDiagnosticEnabled, vkcrash_diagnostic_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkHostMarkersEnabled, vkhost_markers)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, VkGuestMarkersEnabled, vkguest_markers)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, PipelineCacheEnabled, pipeline_cache_enabled)
|
||||
SETTING_FORWARD_BOOL(m_vulkan, PipelineCacheArchived, pipeline_cache_archived)
|
||||
|
||||
#undef SETTING_FORWARD
|
||||
#undef SETTING_FORWARD_BOOL
|
||||
#undef SETTING_FORWARD_BOOL_READONLY
|
||||
};
|
||||
@ -8,12 +8,12 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/types.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/debugger.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/emulator_state.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "input/input_handler.h"
|
||||
@ -153,7 +153,7 @@ void IPC::InputLoop() {
|
||||
} else if (cmd == "ADJUST_VOLUME") {
|
||||
int value = static_cast<int>(next_u64());
|
||||
bool is_game_specific = next_u64() != 0;
|
||||
Config::setVolumeSlider(value, is_game_specific);
|
||||
EmulatorSettings.SetVolumeSlider(value);
|
||||
Libraries::AudioOut::AdjustVol();
|
||||
} else if (cmd == "SET_FSR") {
|
||||
bool use_fsr = next_u64() != 0;
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
|
||||
#include "app_content.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/app_content/app_content_error.h"
|
||||
@ -57,7 +57,7 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label,
|
||||
OrbisAppContentMountPoint* mount_point) {
|
||||
LOG_INFO(Lib_AppContent, "called");
|
||||
|
||||
const auto& addon_path = Config::getAddonInstallDir() / title_id;
|
||||
const auto& addon_path = EmulatorSettings.GetAddonInstallDir() / title_id;
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
||||
// Determine which loaded additional content this entitlement label is for.
|
||||
@ -282,7 +282,7 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar
|
||||
LOG_ERROR(Lib_AppContent, "(DUMMY) called");
|
||||
auto* param_sfo = Common::Singleton<PSF>::Instance();
|
||||
|
||||
const auto addons_dir = Config::getAddonInstallDir();
|
||||
const auto addons_dir = EmulatorSettings.GetAddonInstallDir();
|
||||
if (const auto value = param_sfo->GetString("TITLE_ID"); value.has_value()) {
|
||||
title_id = *value;
|
||||
} else {
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <common/config.h>
|
||||
#include <common/logging/log.h>
|
||||
#include <core/emulator_settings.h>
|
||||
#include "audioin.h"
|
||||
#include "audioin_backend.h"
|
||||
|
||||
@ -21,7 +21,7 @@ public:
|
||||
fmt.channels = static_cast<Uint8>(port.channels_num);
|
||||
fmt.freq = static_cast<int>(port.freq);
|
||||
|
||||
std::string micDevStr = Config::getMicDevice();
|
||||
std::string micDevStr = EmulatorSettings.GetSDLMicDevice();
|
||||
uint32_t devId = 0;
|
||||
if (micDevStr == "None") {
|
||||
nullDevice = true;
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
#include <SDL3/SDL_audio.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
@ -110,7 +110,7 @@ public:
|
||||
max_channel_gain = std::max(max_channel_gain, channel_gain);
|
||||
}
|
||||
|
||||
const float slider_gain = Config::getVolumeSlider() * 0.01f; // Faster than /100.0f
|
||||
const float slider_gain = EmulatorSettings.GetVolumeSlider() * 0.01f; // Faster than /100.0f
|
||||
const float total_gain = max_channel_gain * slider_gain;
|
||||
|
||||
const float current = current_gain.load(std::memory_order_acquire);
|
||||
@ -156,7 +156,7 @@ private:
|
||||
}
|
||||
|
||||
// Initialize current gain
|
||||
current_gain.store(Config::getVolumeSlider() * 0.01f, std::memory_order_relaxed);
|
||||
current_gain.store(EmulatorSettings.GetVolumeSlider() * 0.01f, std::memory_order_relaxed);
|
||||
|
||||
if (!SelectConverter()) {
|
||||
FreeAlignedBuffer();
|
||||
@ -201,7 +201,7 @@ private:
|
||||
|
||||
last_volume_check_time = current_time;
|
||||
|
||||
const float config_volume = Config::getVolumeSlider() * 0.01f;
|
||||
const float config_volume = EmulatorSettings.GetVolumeSlider() * 0.01f;
|
||||
const float stored_gain = current_gain.load(std::memory_order_acquire);
|
||||
|
||||
// Only update if the difference is significant
|
||||
@ -368,11 +368,11 @@ private:
|
||||
switch (type) {
|
||||
case OrbisAudioOutPort::Main:
|
||||
case OrbisAudioOutPort::Bgm:
|
||||
return Config::getMainOutputDevice();
|
||||
return EmulatorSettings.GetSDLMainOutputDevice();
|
||||
case OrbisAudioOutPort::PadSpk:
|
||||
return Config::getPadSpkOutputDevice();
|
||||
return EmulatorSettings.GetSDLPadSpkOutputDevice();
|
||||
default:
|
||||
return Config::getMainOutputDevice();
|
||||
return EmulatorSettings.GetSDLMainOutputDevice();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "gnm_error.h"
|
||||
#include "gnmdriver.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/slot_vector.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/gnmdriver/gnm_error.h"
|
||||
#include "core/libraries/gnmdriver/gnmdriver_init.h"
|
||||
#include "core/libraries/kernel/orbis_error.h"
|
||||
@ -2874,7 +2874,7 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
sdk_version = 0;
|
||||
}
|
||||
|
||||
if (Config::copyGPUCmdBuffers()) {
|
||||
if (EmulatorSettings.IsCopyGpuBuffers()) {
|
||||
liverpool->ReserveCopyBufferSpace();
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/orbis_error.h"
|
||||
#include "core/libraries/kernel/process.h"
|
||||
@ -17,19 +17,19 @@ s32 PS4_SYSV_ABI sceKernelIsInSandbox() {
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelIsNeoMode() {
|
||||
return Config::isNeoModeConsole() &&
|
||||
return EmulatorSettings.IsNeo() &&
|
||||
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelHasNeoMode() {
|
||||
return Config::isNeoModeConsole();
|
||||
return EmulatorSettings.IsNeo();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetMainSocId() {
|
||||
// These hardcoded values are based on hardware observations.
|
||||
// Different models of PS4/PS4 Pro likely return slightly different values.
|
||||
LOG_DEBUG(Lib_Kernel, "called");
|
||||
if (Config::isNeoModeConsole()) {
|
||||
if (EmulatorSettings.IsNeo()) {
|
||||
return 0x740f30;
|
||||
}
|
||||
return 0x710f10;
|
||||
|
||||
@ -1447,7 +1447,7 @@ int PS4_SYSV_ABI sceNetResolverStartNtoa(OrbisNetId resolverid, const char* host
|
||||
return ORBIS_NET_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!Config::getIsConnectedToNetwork()) {
|
||||
if (!EmulatorSettings.IsConnectedToNetwork()) {
|
||||
*sceNetErrnoLoc() = ORBIS_NET_RESOLVER_ENODNS;
|
||||
file->resolver->resolution_error = ORBIS_NET_ERROR_RESOLVER_ENODNS;
|
||||
return ORBIS_NET_ERROR_RESOLVER_ENODNS;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/network/net_ctl_codes.h"
|
||||
#include "core/libraries/network/net_ctl_obj.h"
|
||||
#include "core/tls.h"
|
||||
@ -46,8 +46,9 @@ s32 NetCtlInternal::RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit fu
|
||||
|
||||
void NetCtlInternal::CheckCallback() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
const auto event = Config::getIsConnectedToNetwork() ? ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
const auto event = EmulatorSettings.IsConnectedToNetwork()
|
||||
? ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
for (const auto [func, arg] : callbacks) {
|
||||
if (func != nullptr) {
|
||||
func(event, arg);
|
||||
@ -57,8 +58,9 @@ void NetCtlInternal::CheckCallback() {
|
||||
|
||||
void NetCtlInternal::CheckNpToolkitCallback() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
const auto event = Config::getIsConnectedToNetwork() ? ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
const auto event = EmulatorSettings.IsConnectedToNetwork()
|
||||
? ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
for (const auto [func, arg] : nptool_callbacks) {
|
||||
if (func != nullptr) {
|
||||
func(event, arg);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef WIN32
|
||||
@ -13,8 +13,8 @@
|
||||
#endif
|
||||
|
||||
#include <common/singleton.h>
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/network/net_ctl_codes.h"
|
||||
@ -162,7 +162,7 @@ int PS4_SYSV_ABI sceNetCtlGetIfStat() {
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) {
|
||||
LOG_DEBUG(Lib_NetCtl, "code = {}", code);
|
||||
if (!Config::getIsConnectedToNetwork()) {
|
||||
if (!EmulatorSettings.IsConnectedToNetwork()) {
|
||||
return ORBIS_NET_CTL_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
@ -180,8 +180,8 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) {
|
||||
info->mtu = 1500; // default value
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_LINK:
|
||||
info->link = Config::getIsConnectedToNetwork() ? ORBIS_NET_CTL_LINK_CONNECTED
|
||||
: ORBIS_NET_CTL_LINK_DISCONNECTED;
|
||||
info->link = EmulatorSettings.IsConnectedToNetwork() ? ORBIS_NET_CTL_LINK_CONNECTED
|
||||
: ORBIS_NET_CTL_LINK_DISCONNECTED;
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_IP_ADDRESS: {
|
||||
strcpy(info->ip_address,
|
||||
@ -318,7 +318,7 @@ int PS4_SYSV_ABI sceNetCtlGetScanInfoForSsidScanIpcInt() {
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetState(int* state) {
|
||||
const auto connected = Config::getIsConnectedToNetwork();
|
||||
const auto connected = EmulatorSettings.IsConnectedToNetwork();
|
||||
LOG_DEBUG(Lib_NetCtl, "connected = {}", connected);
|
||||
const auto current_state =
|
||||
connected ? ORBIS_NET_CTL_STATE_IPOBTAINED : ORBIS_NET_CTL_STATE_DISCONNECTED;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_auth.h"
|
||||
@ -363,7 +363,7 @@ s32 PS4_SYSV_ABI sceNpAuthDeleteRequest(s32 req_id) {
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
g_signed_in = Config::getPSNSignedIn();
|
||||
g_signed_in = EmulatorSettings.IsPSNSignedIn();
|
||||
|
||||
LIB_FUNCTION("6bwFkosYRQg", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthCreateRequest);
|
||||
LIB_FUNCTION("N+mr7GjTvr8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthCreateAsyncRequest);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
@ -376,7 +376,7 @@ int PS4_SYSV_ABI sceNpMatching2ContextStart(OrbisNpMatching2ContextId ctxId, u64
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_events_mutex};
|
||||
if (Config::getIsConnectedToNetwork() && Config::getPSNSignedIn()) {
|
||||
if (EmulatorSettings.IsConnectedToNetwork() && EmulatorSettings.IsPSNSignedIn()) {
|
||||
g_ctx_events.emplace_back(ctxId, ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED,
|
||||
ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0);
|
||||
} else {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_web_api2.h"
|
||||
@ -115,10 +115,10 @@ s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize2(const OrbisNpWebApi2IntInitialize2Ar
|
||||
if (args == nullptr || args->struct_size != sizeof(OrbisNpWebApi2IntInitialize2Args)) {
|
||||
return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
LOG_ERROR(
|
||||
Lib_NpWebApi2,
|
||||
"(STUBBED) called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = '{}', group = {:#x}",
|
||||
args->lib_http_ctx_id, args->pool_size, args->name, args->push_config_group);
|
||||
LOG_ERROR(Lib_NpWebApi2,
|
||||
"(STUBBED) called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = '{}', "
|
||||
"group = {:#x}",
|
||||
args->lib_http_ctx_id, args->pool_size, args->name, args->push_config_group);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2SendMultipartRequest() {
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpWebApi2SendRequest() {
|
||||
if (!Config::getPSNSignedIn()) {
|
||||
if (!EmulatorSettings.IsPSNSignedIn()) {
|
||||
LOG_INFO(Lib_NpWebApi2, "called, returning PSN signed out.");
|
||||
return ORBIS_NP_WEBAPI2_ERROR_NOT_SIGNED_IN;
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/network/http.h"
|
||||
@ -606,7 +606,7 @@ s32 sendRequest(s64 requestId, s32 partIndex, const void* pData, u64 dataSize, s
|
||||
unlockContext(context);
|
||||
|
||||
// Stubbing sceNpManagerIntGetSigninState call with a config check.
|
||||
if (!Config::getPSNSignedIn()) {
|
||||
if (!EmulatorSettings.IsPSNSignedIn()) {
|
||||
releaseRequest(request);
|
||||
releaseUserContext(user_context);
|
||||
releaseContext(context);
|
||||
@ -1025,7 +1025,7 @@ s32 createServicePushEventFilterInternal(
|
||||
auto& handle = context->handles[handleId];
|
||||
handle->userCount++;
|
||||
|
||||
if (pNpServiceName != nullptr && !Config::getPSNSignedIn()) {
|
||||
if (pNpServiceName != nullptr && !EmulatorSettings.IsPSNSignedIn()) {
|
||||
// Seems sceNpManagerIntGetUserList fails?
|
||||
LOG_DEBUG(Lib_NpWebApi, "Cannot create service push event while PSN is disabled");
|
||||
handle->userCount--;
|
||||
@ -1202,7 +1202,7 @@ s32 createExtendedPushEventFilterInternal(
|
||||
auto& handle = context->handles[handleId];
|
||||
handle->userCount++;
|
||||
|
||||
if (pNpServiceName != nullptr && !Config::getPSNSignedIn()) {
|
||||
if (pNpServiceName != nullptr && !EmulatorSettings.IsPSNSignedIn()) {
|
||||
// Seems sceNpManagerIntGetUserList fails?
|
||||
LOG_DEBUG(Lib_NpWebApi, "Cannot create extended push event while PSN is disabled");
|
||||
handle->userCount--;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
@ -13,9 +13,9 @@
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/np/trophy_ui.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
||||
@ -36,9 +36,9 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
||||
const std::string_view& rarity)
|
||||
: trophy_name(trophyName), trophy_type(rarity) {
|
||||
|
||||
side = Config::sideTrophy();
|
||||
side = EmulatorSettings.GetTrophyNotificationSide();
|
||||
|
||||
trophy_timer = Config::getTrophyNotificationDuration();
|
||||
trophy_timer = EmulatorSettings.GetTrophyNotificationDuration();
|
||||
|
||||
if (std::filesystem::exists(trophyIconPath)) {
|
||||
trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath);
|
||||
@ -98,7 +98,7 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
||||
return;
|
||||
}
|
||||
|
||||
MIX_SetMasterGain(mixer, static_cast<float>(Config::getVolumeSlider() / 100.f));
|
||||
MIX_SetMasterGain(mixer, static_cast<float>(EmulatorSettings.GetVolumeSlider() / 100.f));
|
||||
auto musicPathMp3 = CustomTrophy_Dir / "trophy.mp3";
|
||||
auto musicPathWav = CustomTrophy_Dir / "trophy.wav";
|
||||
|
||||
@ -284,7 +284,7 @@ void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::st
|
||||
const std::string_view& rarity) {
|
||||
std::lock_guard<std::mutex> lock(queueMtx);
|
||||
|
||||
if (Config::getisTrophyPopupDisabled()) {
|
||||
if (EmulatorSettings.IsTrophyPopupDisabled()) {
|
||||
return;
|
||||
} else if (current_trophy_ui.has_value()) {
|
||||
current_trophy_ui.reset();
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
@ -18,7 +18,7 @@ std::queue<OrbisSystemServiceEvent> g_event_queue;
|
||||
std::mutex g_event_queue_mutex;
|
||||
|
||||
bool IsSplashVisible() {
|
||||
return Config::showSplash() && g_splash_status;
|
||||
return EmulatorSettings.IsShowSplash() && g_splash_status;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAppMessagingClearEventFlag() {
|
||||
@ -1918,7 +1918,7 @@ s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(OrbisSystemServiceParamId param_id,
|
||||
}
|
||||
switch (param_id) {
|
||||
case OrbisSystemServiceParamId::Lang:
|
||||
*value = Config::GetLanguage();
|
||||
*value = EmulatorSettings.GetConsoleLanguage();
|
||||
break;
|
||||
case OrbisSystemServiceParamId::DateFormat:
|
||||
*value = u32(OrbisSystemParamDateFormat::FmtDDMMYYYY);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
@ -9,7 +9,7 @@
|
||||
#include <fmt/format.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
namespace Libraries::Usbd {
|
||||
|
||||
@ -457,14 +457,14 @@ int PS4_SYSV_ABI Func_D56B43060720B1E0() {
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
switch (Config::getUsbDeviceBackend()) {
|
||||
case Config::SkylandersPortal:
|
||||
switch (EmulatorSettings.GetUsbDeviceBackend()) {
|
||||
case UsbBackendType::SkylandersPortal:
|
||||
usb_backend = std::make_shared<SkylandersPortalBackend>();
|
||||
break;
|
||||
case Config::InfinityBase:
|
||||
case UsbBackendType::InfinityBase:
|
||||
usb_backend = std::make_shared<InfinityBaseBackend>();
|
||||
break;
|
||||
case Config::DimensionsToypad:
|
||||
case UsbBackendType::DimensionsToypad:
|
||||
usb_backend = std::make_shared<DimensionsToypadBackend>();
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
#include "core/libraries/videoout/videoout_error.h"
|
||||
@ -268,7 +268,8 @@ void VideoOutDriver::SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_
|
||||
}
|
||||
|
||||
void VideoOutDriver::PresentThread(std::stop_token token) {
|
||||
const std::chrono::nanoseconds vblank_period(1000000000 / Config::vblankFreq());
|
||||
const std::chrono::nanoseconds vblank_period(1000000000 /
|
||||
EmulatorSettings.GetVblankFrequency());
|
||||
|
||||
Common::SetCurrentThreadName("shadPS4:PresentThread");
|
||||
Common::SetCurrentThreadRealtime(vblank_period);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
@ -455,8 +455,8 @@ s32 PS4_SYSV_ABI sceVideoOutSetWindowModeMargins(s32 handle, s32 top, s32 bottom
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
driver = std::make_unique<VideoOutDriver>(Config::getInternalScreenWidth(),
|
||||
Config::getInternalScreenHeight());
|
||||
driver = std::make_unique<VideoOutDriver>(EmulatorSettings.GetInternalScreenWidth(),
|
||||
EmulatorSettings.GetInternalScreenHeight());
|
||||
|
||||
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", sceVideoOutGetFlipStatus);
|
||||
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", sceVideoOutSubmitFlip);
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
@ -13,6 +12,7 @@
|
||||
#include "core/aerolib/aerolib.h"
|
||||
#include "core/aerolib/stubs.h"
|
||||
#include "core/devtools/widget/module_list.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
@ -61,7 +61,7 @@ Linker::Linker() : memory{Memory::Instance()} {}
|
||||
Linker::~Linker() = default;
|
||||
|
||||
void Linker::Execute(const std::vector<std::string>& args) {
|
||||
if (Config::debugDump()) {
|
||||
if (EmulatorSettings.IsDebugDump()) {
|
||||
DebugDump();
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/libraries/kernel/orbis_error.h"
|
||||
@ -37,11 +37,11 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
|
||||
bool use_extended_mem2) {
|
||||
const bool is_neo = ::Libraries::Kernel::sceKernelIsNeoMode();
|
||||
auto total_size = is_neo ? ORBIS_KERNEL_TOTAL_MEM_PRO : ORBIS_KERNEL_TOTAL_MEM;
|
||||
if (Config::isDevKitConsole()) {
|
||||
if (EmulatorSettings.IsDevKit()) {
|
||||
total_size = is_neo ? ORBIS_KERNEL_TOTAL_MEM_DEV_PRO : ORBIS_KERNEL_TOTAL_MEM_DEV;
|
||||
}
|
||||
s32 extra_dmem = Config::getExtraDmemInMbytes();
|
||||
if (Config::getExtraDmemInMbytes() != 0) {
|
||||
s32 extra_dmem = EmulatorSettings.GetExtraDmemInMBytes();
|
||||
if (extra_dmem != 0) {
|
||||
LOG_WARNING(Kernel_Vmm,
|
||||
"extraDmemInMbytes is {} MB! Old Direct Size: {:#x} -> New Direct Size: {:#x}",
|
||||
extra_dmem, total_size, total_size + extra_dmem * 1_MB);
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/config.h"
|
||||
#include "common/types.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/loader/elf.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
|
||||
@ -166,7 +166,7 @@ public:
|
||||
}
|
||||
|
||||
bool IsSystemLib() {
|
||||
auto system_path = Config::getSysModulesPath();
|
||||
auto system_path = EmulatorSettings.GetSysModulesDir();
|
||||
if (file.string().starts_with(system_path.string().c_str())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
177
src/core/user_manager.cpp
Normal file
177
src/core/user_manager.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <common/path_util.h>
|
||||
#include "emulator_settings.h"
|
||||
#include "libraries/system/userservice.h"
|
||||
#include "user_manager.h"
|
||||
#include "user_settings.h"
|
||||
|
||||
bool UserManager::AddUser(const User& user) {
|
||||
for (const auto& u : m_users.user) {
|
||||
if (u.user_id == user.user_id)
|
||||
return false; // already exists
|
||||
}
|
||||
|
||||
m_users.user.push_back(user);
|
||||
|
||||
// Create user home directory and subfolders
|
||||
const auto user_dir = EmulatorSettings.GetHomeDir() / std::to_string(user.user_id);
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(user_dir)) {
|
||||
std::filesystem::create_directory(user_dir, ec);
|
||||
std::filesystem::create_directory(user_dir / "savedata", ec);
|
||||
std::filesystem::create_directory(user_dir / "trophy", ec);
|
||||
std::filesystem::create_directory(user_dir / "inputs", ec);
|
||||
}
|
||||
|
||||
Save();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserManager::RemoveUser(s32 user_id) {
|
||||
auto it = std::remove_if(m_users.user.begin(), m_users.user.end(),
|
||||
[user_id](const User& u) { return u.user_id == user_id; });
|
||||
if (it == m_users.user.end())
|
||||
return false; // not found
|
||||
|
||||
const auto user_dir = EmulatorSettings.GetHomeDir() / std::to_string(user_id);
|
||||
|
||||
if (std::filesystem::exists(user_dir)) {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(user_dir, ec);
|
||||
}
|
||||
|
||||
m_users.user.erase(it, m_users.user.end());
|
||||
Save();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserManager::RenameUser(s32 user_id, const std::string& new_name) {
|
||||
// Find user in the internal list
|
||||
for (auto& user : m_users.user) {
|
||||
if (user.user_id == user_id) {
|
||||
if (user.user_name == new_name)
|
||||
return true; // no change
|
||||
|
||||
user.user_name = new_name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Save();
|
||||
return false;
|
||||
}
|
||||
|
||||
User* UserManager::GetUserByID(s32 user_id) {
|
||||
for (auto& u : m_users.user) {
|
||||
if (u.user_id == user_id)
|
||||
return &u;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
User* UserManager::GetUserByPlayerIndex(s32 index) {
|
||||
for (auto& u : m_users.user) {
|
||||
if (u.player_index == index)
|
||||
return &u;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<User>& UserManager::GetAllUsers() const {
|
||||
return m_users.user;
|
||||
}
|
||||
|
||||
Users UserManager::CreateDefaultUsers() {
|
||||
Users default_users;
|
||||
default_users.user = {
|
||||
{
|
||||
.user_id = 1000,
|
||||
.user_name = "shadPS4",
|
||||
.user_color = 1,
|
||||
.player_index = 1,
|
||||
},
|
||||
{
|
||||
.user_id = 1001,
|
||||
.user_name = "shadPS4-2",
|
||||
.user_color = 2,
|
||||
.player_index = 2,
|
||||
},
|
||||
{
|
||||
.user_id = 1002,
|
||||
.user_name = "shadPS4-3",
|
||||
.user_color = 3,
|
||||
.player_index = 3,
|
||||
},
|
||||
{
|
||||
.user_id = 1003,
|
||||
.user_name = "shadPS4-4",
|
||||
.user_color = 4,
|
||||
.player_index = 4,
|
||||
},
|
||||
};
|
||||
|
||||
for (auto& u : default_users.user) {
|
||||
const auto user_dir = EmulatorSettings.GetHomeDir() / std::to_string(u.user_id);
|
||||
|
||||
if (!std::filesystem::exists(user_dir)) {
|
||||
std::filesystem::create_directory(user_dir);
|
||||
std::filesystem::create_directory(user_dir / "savedata");
|
||||
std::filesystem::create_directory(user_dir / "trophy");
|
||||
std::filesystem::create_directory(user_dir / "inputs");
|
||||
}
|
||||
}
|
||||
|
||||
return default_users;
|
||||
}
|
||||
|
||||
bool UserManager::SetDefaultUser(u32 user_id) {
|
||||
auto it = std::find_if(m_users.user.begin(), m_users.user.end(),
|
||||
[user_id](const User& u) { return u.user_id == user_id; });
|
||||
if (it == m_users.user.end())
|
||||
return false;
|
||||
|
||||
SetControllerPort(user_id, 1); // Set default user to port 1
|
||||
return Save();
|
||||
}
|
||||
|
||||
User UserManager::GetDefaultUser() {
|
||||
return *GetUserByPlayerIndex(1);
|
||||
}
|
||||
|
||||
void UserManager::SetControllerPort(u32 user_id, int port) {
|
||||
for (auto& u : m_users.user) {
|
||||
if (u.user_id != user_id && u.player_index == port)
|
||||
u.player_index = -1;
|
||||
if (u.user_id == user_id)
|
||||
u.player_index = port;
|
||||
}
|
||||
Save();
|
||||
}
|
||||
// Returns a list of users that have valid home directories
|
||||
std::vector<User> UserManager::GetValidUsers() const {
|
||||
std::vector<User> result;
|
||||
result.reserve(m_users.user.size());
|
||||
|
||||
const auto home_dir = EmulatorSettings.GetHomeDir();
|
||||
|
||||
for (const auto& user : m_users.user) {
|
||||
const auto user_dir = home_dir / std::to_string(user.user_id);
|
||||
if (std::filesystem::exists(user_dir)) {
|
||||
result.push_back(user);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LoggedInUsers UserManager::GetLoggedInUsers() const {
|
||||
return logged_in_users;
|
||||
}
|
||||
|
||||
bool UserManager::Save() const {
|
||||
return UserSettings.Save();
|
||||
}
|
||||
58
src/core/user_manager.h
Normal file
58
src/core/user_manager.h
Normal file
@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "common/types.h"
|
||||
|
||||
struct User {
|
||||
s32 user_id = -1;
|
||||
std::string user_name = "";
|
||||
u32 user_color;
|
||||
int player_index = 0; // 1-4
|
||||
|
||||
bool logged_in = false;
|
||||
};
|
||||
|
||||
struct Users {
|
||||
std::vector<User> user{};
|
||||
std::string commit_hash{};
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(User, user_id, user_color, user_name, player_index)
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Users, user, commit_hash)
|
||||
|
||||
using LoggedInUsers = std::array<User*, 4>;
|
||||
|
||||
class UserManager {
|
||||
public:
|
||||
UserManager() = default;
|
||||
|
||||
bool AddUser(const User& user);
|
||||
bool RemoveUser(s32 user_id);
|
||||
bool RenameUser(s32 user_id, const std::string& new_name);
|
||||
User* GetUserByID(s32 user_id);
|
||||
User* GetUserByPlayerIndex(s32 index);
|
||||
const std::vector<User>& GetAllUsers() const;
|
||||
Users CreateDefaultUsers();
|
||||
bool SetDefaultUser(u32 user_id);
|
||||
User GetDefaultUser();
|
||||
void SetControllerPort(u32 user_id, int port);
|
||||
std::vector<User> GetValidUsers() const;
|
||||
LoggedInUsers GetLoggedInUsers() const;
|
||||
|
||||
Users& GetUsers() {
|
||||
return m_users;
|
||||
}
|
||||
const Users& GetUsers() const {
|
||||
return m_users;
|
||||
}
|
||||
|
||||
bool Save() const;
|
||||
|
||||
private:
|
||||
Users m_users;
|
||||
LoggedInUsers logged_in_users{};
|
||||
};
|
||||
110
src/core/user_settings.cpp
Normal file
110
src/core/user_settings.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <common/path_util.h>
|
||||
#include <common/scm_rev.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "user_settings.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// Singleton storage
|
||||
std::shared_ptr<UserSettingsImpl> UserSettingsImpl::s_instance = nullptr;
|
||||
std::mutex UserSettingsImpl::s_mutex;
|
||||
|
||||
// Singleton
|
||||
UserSettingsImpl::UserSettingsImpl() = default;
|
||||
|
||||
UserSettingsImpl::~UserSettingsImpl() {
|
||||
Save();
|
||||
}
|
||||
|
||||
std::shared_ptr<UserSettingsImpl> UserSettingsImpl::GetInstance() {
|
||||
std::lock_guard lock(s_mutex);
|
||||
if (!s_instance)
|
||||
s_instance = std::make_shared<UserSettingsImpl>();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void UserSettingsImpl::SetInstance(std::shared_ptr<UserSettingsImpl> instance) {
|
||||
std::lock_guard lock(s_mutex);
|
||||
s_instance = std::move(instance);
|
||||
}
|
||||
|
||||
bool UserSettingsImpl::Save() const {
|
||||
const auto path = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "users.json";
|
||||
try {
|
||||
json j;
|
||||
j["Users"] = m_userManager.GetUsers();
|
||||
j["Users"]["commit_hash"] = std::string(Common::g_scm_rev);
|
||||
|
||||
std::ofstream out(path);
|
||||
if (!out) {
|
||||
LOG_ERROR(EmuSettings, "Failed to open user settings for writing: {}", path.string());
|
||||
return false;
|
||||
}
|
||||
out << std::setw(2) << j;
|
||||
return !out.fail();
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(EmuSettings, "Error saving user settings: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UserSettingsImpl::Load() {
|
||||
const auto path = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "users.json";
|
||||
try {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
LOG_DEBUG(EmuSettings, "User settings file not found: {}", path.string());
|
||||
// Create default user if no file exists
|
||||
if (m_userManager.GetUsers().user.empty()) {
|
||||
m_userManager.GetUsers() = m_userManager.CreateDefaultUsers();
|
||||
}
|
||||
Save(); // Save default users
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream in(path);
|
||||
if (!in) {
|
||||
LOG_ERROR(EmuSettings, "Failed to open user settings: {}", path.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
json j;
|
||||
in >> j;
|
||||
|
||||
// Create a default Users object
|
||||
auto default_users = m_userManager.CreateDefaultUsers();
|
||||
|
||||
// Convert default_users to json for merging
|
||||
json default_json;
|
||||
default_json["Users"] = default_users;
|
||||
|
||||
// Merge the loaded json with defaults (preserves existing data, adds missing fields)
|
||||
if (j.contains("Users")) {
|
||||
json current = default_json["Users"];
|
||||
current.update(j["Users"]);
|
||||
m_userManager.GetUsers() = current.get<Users>();
|
||||
} else {
|
||||
m_userManager.GetUsers() = default_users;
|
||||
}
|
||||
|
||||
if (m_userManager.GetUsers().commit_hash != Common::g_scm_rev) {
|
||||
Save();
|
||||
}
|
||||
|
||||
LOG_DEBUG(EmuSettings, "User settings loaded successfully");
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(EmuSettings, "Error loading user settings: {}", e.what());
|
||||
// Fall back to defaults
|
||||
if (m_userManager.GetUsers().user.empty()) {
|
||||
m_userManager.GetUsers() = m_userManager.CreateDefaultUsers();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
46
src/core/user_settings.h
Normal file
46
src/core/user_settings.h
Normal file
@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/types.h"
|
||||
#include "core/user_manager.h"
|
||||
|
||||
#define UserSettings (*UserSettingsImpl::GetInstance())
|
||||
|
||||
#define UserManagement UserSettings.GetUserManager()
|
||||
|
||||
// -------------------------------
|
||||
// User settings
|
||||
// -------------------------------
|
||||
|
||||
class UserSettingsImpl {
|
||||
public:
|
||||
UserSettingsImpl();
|
||||
~UserSettingsImpl();
|
||||
|
||||
UserManager& GetUserManager() {
|
||||
return m_userManager;
|
||||
}
|
||||
|
||||
bool Save() const;
|
||||
bool Load();
|
||||
|
||||
static std::shared_ptr<UserSettingsImpl> GetInstance();
|
||||
static void SetInstance(std::shared_ptr<UserSettingsImpl> instance);
|
||||
|
||||
private:
|
||||
UserManager m_userManager;
|
||||
|
||||
static std::shared_ptr<UserSettingsImpl> s_instance;
|
||||
static std::mutex s_mutex;
|
||||
};
|
||||
@ -10,11 +10,11 @@
|
||||
#include <fmt/xchar.h>
|
||||
#include <hwinfo/hwinfo.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/ipc/ipc.h"
|
||||
#ifdef ENABLE_DISCORD_RPC
|
||||
#include "common/discord_rpc_handler.h"
|
||||
@ -197,18 +197,16 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
|
||||
game_info.game_folder = game_folder;
|
||||
|
||||
Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"),
|
||||
true);
|
||||
|
||||
EmulatorSettings.Load(id);
|
||||
if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
|
||||
(id + ".toml"))) {
|
||||
(id + ".json"))) {
|
||||
EmulatorState::GetInstance()->SetGameSpecifigConfigUsed(true);
|
||||
} else {
|
||||
EmulatorState::GetInstance()->SetGameSpecifigConfigUsed(false);
|
||||
}
|
||||
|
||||
// Initialize logging as soon as possible
|
||||
if (!id.empty() && Config::getSeparateLogFilesEnabled()) {
|
||||
if (!id.empty() && EmulatorSettings.IsSeparateLoggingEnabled()) {
|
||||
Common::Log::Initialize(id + ".log");
|
||||
} else {
|
||||
Common::Log::Initialize();
|
||||
@ -227,31 +225,35 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url);
|
||||
|
||||
const bool has_game_config = std::filesystem::exists(
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"));
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".json"));
|
||||
LOG_INFO(Config, "Game-specific config exists: {}", has_game_config);
|
||||
|
||||
LOG_INFO(Config, "General LogType: {}", Config::getLogType());
|
||||
LOG_INFO(Config, "General isIdenticalLogGrouped: {}", Config::groupIdenticalLogs());
|
||||
LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole());
|
||||
LOG_INFO(Config, "General isDevKit: {}", Config::isDevKitConsole());
|
||||
LOG_INFO(Config, "General isConnectedToNetwork: {}", Config::getIsConnectedToNetwork());
|
||||
LOG_INFO(Config, "General isPsnSignedIn: {}", Config::getPSNSignedIn());
|
||||
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
|
||||
LOG_INFO(Config, "GPU readbacksMode: {}", Config::getReadbacksMode());
|
||||
LOG_INFO(Config, "GPU readbackLinearImages: {}", Config::readbackLinearImages());
|
||||
LOG_INFO(Config, "GPU directMemoryAccess: {}", Config::directMemoryAccess());
|
||||
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
|
||||
LOG_INFO(Config, "GPU vblankFrequency: {}", Config::vblankFreq());
|
||||
LOG_INFO(Config, "GPU shouldCopyGPUBuffers: {}", Config::copyGPUCmdBuffers());
|
||||
LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId());
|
||||
LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled());
|
||||
LOG_INFO(Config, "Vulkan vkValidationCore: {}", Config::vkValidationCoreEnabled());
|
||||
LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled());
|
||||
LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled());
|
||||
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled());
|
||||
LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled());
|
||||
LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled());
|
||||
LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled());
|
||||
LOG_INFO(Config, "General LogType: {}", EmulatorSettings.GetLogType());
|
||||
LOG_INFO(Config, "General isIdenticalLogGrouped: {}", EmulatorSettings.IsIdenticalLogGrouped());
|
||||
LOG_INFO(Config, "General isNeo: {}", EmulatorSettings.IsNeo());
|
||||
LOG_INFO(Config, "General isDevKit: {}", EmulatorSettings.IsDevKit());
|
||||
LOG_INFO(Config, "General isConnectedToNetwork: {}", EmulatorSettings.IsConnectedToNetwork());
|
||||
LOG_INFO(Config, "General isPsnSignedIn: {}", EmulatorSettings.IsPSNSignedIn());
|
||||
LOG_INFO(Config, "GPU isNullGpu: {}", EmulatorSettings.IsNullGPU());
|
||||
LOG_INFO(Config, "GPU readbacksMode: {}", EmulatorSettings.GetReadbacksMode());
|
||||
LOG_INFO(Config, "GPU readbackLinearImages: {}",
|
||||
EmulatorSettings.IsReadbackLinearImagesEnabled());
|
||||
LOG_INFO(Config, "GPU directMemoryAccess: {}", EmulatorSettings.IsDirectMemoryAccessEnabled());
|
||||
LOG_INFO(Config, "GPU shouldDumpShaders: {}", EmulatorSettings.IsDumpShaders());
|
||||
LOG_INFO(Config, "GPU vblankFrequency: {}", EmulatorSettings.GetVblankFrequency());
|
||||
LOG_INFO(Config, "GPU shouldCopyGPUBuffers: {}", EmulatorSettings.IsCopyGpuBuffers());
|
||||
LOG_INFO(Config, "Vulkan gpuId: {}", EmulatorSettings.GetGpuId());
|
||||
LOG_INFO(Config, "Vulkan vkValidation: {}", EmulatorSettings.IsVkValidationEnabled());
|
||||
LOG_INFO(Config, "Vulkan vkValidationCore: {}", EmulatorSettings.IsVkValidationCoreEnabled());
|
||||
LOG_INFO(Config, "Vulkan vkValidationSync: {}", EmulatorSettings.IsVkValidationSyncEnabled());
|
||||
LOG_INFO(Config, "Vulkan vkValidationGpu: {}", EmulatorSettings.IsVkValidationGpuEnabled());
|
||||
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", EmulatorSettings.IsVkCrashDiagnosticEnabled());
|
||||
LOG_INFO(Config, "Vulkan hostMarkers: {}", EmulatorSettings.IsVkHostMarkersEnabled());
|
||||
LOG_INFO(Config, "Vulkan guestMarkers: {}", EmulatorSettings.IsVkGuestMarkersEnabled());
|
||||
LOG_INFO(Config, "Vulkan rdocEnable: {}", EmulatorSettings.IsRenderdocEnabled());
|
||||
LOG_INFO(Config, "Vulkan PipelineCacheEnabled: {}", EmulatorSettings.IsPipelineCacheEnabled());
|
||||
LOG_INFO(Config, "Vulkan PipelineCacheArchived: {}",
|
||||
EmulatorSettings.IsPipelineCacheArchived());
|
||||
|
||||
hwinfo::Memory ram;
|
||||
hwinfo::OS os;
|
||||
@ -328,8 +330,9 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
||||
}
|
||||
}
|
||||
window = std::make_unique<Frontend::WindowSDL>(
|
||||
Config::getWindowWidth(), Config::getWindowHeight(), controller, window_title);
|
||||
window = std::make_unique<Frontend::WindowSDL>(EmulatorSettings.GetWindowWidth(),
|
||||
EmulatorSettings.GetWindowHeight(), controller,
|
||||
window_title);
|
||||
|
||||
g_window = window.get();
|
||||
|
||||
@ -360,7 +363,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
VideoCore::SetOutputDir(mount_captures_dir, id);
|
||||
|
||||
// Mount system fonts
|
||||
const auto& fonts_dir = Config::getFontsPath();
|
||||
const auto& fonts_dir = EmulatorSettings.GetFontsDir();
|
||||
if (!std::filesystem::exists(fonts_dir)) {
|
||||
std::filesystem::create_directory(fonts_dir);
|
||||
}
|
||||
@ -399,7 +402,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
|
||||
#ifdef ENABLE_DISCORD_RPC
|
||||
// Discord RPC
|
||||
if (Config::getEnableDiscordRPC()) {
|
||||
if (EmulatorSettings.IsDiscordRPCEnabled()) {
|
||||
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
|
||||
if (rpc->getRPCEnabled() == false) {
|
||||
rpc->init();
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "imgui_core.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
@ -219,7 +219,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||
.pLabelName = "ImGui Render",
|
||||
});
|
||||
@ -244,7 +244,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
|
||||
cmdbuf.beginRendering(render_info);
|
||||
Vulkan::RenderDrawData(*draw_data, cmdbuf);
|
||||
cmdbuf.endRendering();
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.endDebugUtilsLabelEXT();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on imgui_impl_sdl3.cpp from Dear ImGui repository
|
||||
|
||||
#include <imgui.h>
|
||||
#include "common/config.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/memory.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "input/controller.h"
|
||||
@ -396,7 +396,7 @@ bool ProcessEvent(const SDL_Event* event) {
|
||||
if (mouse_pos.x != bd->prev_mouse_pos.x || mouse_pos.y != bd->prev_mouse_pos.y) {
|
||||
bd->prev_mouse_pos.x = mouse_pos.x;
|
||||
bd->prev_mouse_pos.y = mouse_pos.y;
|
||||
if (Config::getCursorState() == Config::HideCursorState::Idle) {
|
||||
if (EmulatorSettings.GetCursorState() == HideCursorState::Idle) {
|
||||
bd->lastCursorMoveTime = bd->time;
|
||||
}
|
||||
}
|
||||
@ -656,16 +656,16 @@ static void UpdateMouseCursor() {
|
||||
return;
|
||||
SdlData* bd = GetBackendData();
|
||||
|
||||
s16 cursorState = Config::getCursorState();
|
||||
s16 cursorState = EmulatorSettings.GetCursorState();
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None ||
|
||||
cursorState == Config::HideCursorState::Always) {
|
||||
cursorState == HideCursorState::Always) {
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
SDL_HideCursor();
|
||||
|
||||
} else if (cursorState == Config::HideCursorState::Idle &&
|
||||
} else if (cursorState == HideCursorState::Idle &&
|
||||
bd->time - bd->lastCursorMoveTime >=
|
||||
Config::getCursorHideTimeout() * SDL_GetPerformanceFrequency()) {
|
||||
EmulatorSettings.GetCursorHideTimeout() * SDL_GetPerformanceFrequency()) {
|
||||
|
||||
bool wasCursorVisible = SDL_CursorVisible();
|
||||
SDL_HideCursor();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <deque>
|
||||
@ -6,11 +6,11 @@
|
||||
|
||||
#include <imgui.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/stb.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "texture_manager.h"
|
||||
|
||||
@ -152,7 +152,7 @@ void WorkerLoop() {
|
||||
g_job_list.pop_front();
|
||||
g_job_list_mtx.unlock();
|
||||
|
||||
if (Config::getVkCrashDiagnosticEnabled()) {
|
||||
if (EmulatorSettings.IsVkCrashDiagnosticEnabled()) {
|
||||
// FIXME: Crash diagnostic hangs when building the command buffer here
|
||||
continue;
|
||||
}
|
||||
|
||||
29
src/main.cpp
29
src/main.cpp
@ -9,6 +9,7 @@
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <SDL3/SDL_messagebox.h>
|
||||
|
||||
#include <core/emulator_settings.h>
|
||||
#include <core/emulator_state.h>
|
||||
#include "common/config.h"
|
||||
#include "common/key_manager.h"
|
||||
@ -19,10 +20,10 @@
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/ipc/ipc.h"
|
||||
#include "emulator.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <core/user_settings.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef _WIN32
|
||||
@ -33,6 +34,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
auto emu_state = std::make_shared<EmulatorState>();
|
||||
EmulatorState::SetInstance(emu_state);
|
||||
UserSettings.Load();
|
||||
|
||||
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
Config::load(user_dir / "config.toml");
|
||||
@ -51,6 +53,11 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load configurations
|
||||
std::shared_ptr<EmulatorSettingsImpl> emu_settings = std::make_shared<EmulatorSettingsImpl>();
|
||||
EmulatorSettingsImpl::SetInstance(emu_settings);
|
||||
emu_settings->Load();
|
||||
|
||||
CLI::App app{"shadPS4 Emulator CLI"};
|
||||
|
||||
// ---- CLI state ----
|
||||
@ -120,15 +127,15 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// ---- Utility commands ----
|
||||
if (addGameFolder) {
|
||||
Config::addGameInstallDir(*addGameFolder);
|
||||
Config::save(user_dir / "config.toml");
|
||||
EmulatorSettings.AddGameInstallDir(*addGameFolder);
|
||||
EmulatorSettings.Save();
|
||||
std::cout << "Game folder successfully saved.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setAddonFolder) {
|
||||
Config::setAddonInstallDir(*setAddonFolder);
|
||||
Config::save(user_dir / "config.toml");
|
||||
EmulatorSettings.SetAddonInstallDir(*setAddonFolder);
|
||||
EmulatorSettings.Save();
|
||||
std::cout << "Addon folder successfully saved.\n";
|
||||
return 0;
|
||||
}
|
||||
@ -160,9 +167,9 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
if (fullscreenStr) {
|
||||
if (*fullscreenStr == "true") {
|
||||
Config::setIsFullscreen(true);
|
||||
EmulatorSettings.SetFullScreen(true);
|
||||
} else if (*fullscreenStr == "false") {
|
||||
Config::setIsFullscreen(false);
|
||||
EmulatorSettings.SetFullScreen(false);
|
||||
} else {
|
||||
std::cerr << "Error: Invalid argument for --fullscreen (use true|false)\n";
|
||||
return 1;
|
||||
@ -170,13 +177,13 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
if (showFps)
|
||||
Config::setShowFpsCounter(true);
|
||||
EmulatorSettings.SetShowFpsCounter(true);
|
||||
|
||||
if (configClean)
|
||||
Config::setConfigMode(Config::ConfigMode::Clean);
|
||||
EmulatorSettings.SetConfigMode(ConfigMode::Clean);
|
||||
|
||||
if (configGlobal)
|
||||
Config::setConfigMode(Config::ConfigMode::Global);
|
||||
EmulatorSettings.SetConfigMode(ConfigMode::Global);
|
||||
|
||||
if (logAppend)
|
||||
Common::Log::SetAppend();
|
||||
@ -186,7 +193,7 @@ int main(int argc, char* argv[]) {
|
||||
if (!std::filesystem::exists(ebootPath)) {
|
||||
bool found = false;
|
||||
constexpr int maxDepth = 5;
|
||||
for (const auto& installDir : Config::getGameInstallDirs()) {
|
||||
for (const auto& installDir : EmulatorSettings.GetGameInstallDirs()) {
|
||||
if (auto foundPath = Common::FS::FindGameByID(installDir, *gamePath, maxDepth)) {
|
||||
ebootPath = *foundPath;
|
||||
found = true;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_bounds.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||
@ -58,7 +58,7 @@ Id EmitGetUserData(EmitContext& ctx, IR::ScalarReg reg) {
|
||||
|
||||
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset) {
|
||||
const u32 flatbuf_off_dw = inst->Flags<u32>();
|
||||
if (!Config::directMemoryAccess()) {
|
||||
if (!EmulatorSettings.IsDirectMemoryAccessEnabled()) {
|
||||
return ctx.EmitFlatbufferLoad(ctx.ConstU32(flatbuf_off_dw));
|
||||
}
|
||||
// We can only provide a fallback for immediate offsets.
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "shader_recompiler/frontend/decode.h"
|
||||
#include "shader_recompiler/frontend/fetch_shader.h"
|
||||
#include "shader_recompiler/frontend/translate/translate.h"
|
||||
@ -569,7 +569,7 @@ void Translator::EmitFetch(const GcnInst& inst) {
|
||||
const auto fetch_data = ParseFetchShader(info);
|
||||
ASSERT(fetch_data.has_value());
|
||||
|
||||
if (Config::dumpShaders()) {
|
||||
if (EmulatorSettings.IsDumpShaders()) {
|
||||
using namespace Common::FS;
|
||||
const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps";
|
||||
if (!std::filesystem::exists(dump_dir)) {
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_map>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
#include "common/config.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/signal_context.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/signals.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/ir/breadth_first_search.h"
|
||||
@ -229,7 +228,7 @@ static void GenerateSrtProgram(Info& info, PassInfo& pass_info) {
|
||||
info.srt_info.walker_func_size =
|
||||
c.getCurr() - reinterpret_cast<const u8*>(info.srt_info.walker_func);
|
||||
|
||||
if (Config::dumpShaders()) {
|
||||
if (EmulatorSettings.IsDumpShaders()) {
|
||||
DumpSrtProgram(info, reinterpret_cast<const u8*>(info.srt_info.walker_func),
|
||||
info.srt_info.walker_func_size);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "shader_recompiler/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
@ -176,7 +176,7 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) {
|
||||
// info.readconst_types |= Info::ReadConstType::Immediate;
|
||||
}
|
||||
|
||||
if (!Config::directMemoryAccess()) {
|
||||
if (!EmulatorSettings.IsDirectMemoryAccessEnabled()) {
|
||||
info.uses_dma = false;
|
||||
info.readconst_types = Info::ReadConstType::None;
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
@ -6,9 +7,9 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "shader_recompiler/ir/basic_block.h"
|
||||
#include "shader_recompiler/ir/program.h"
|
||||
#include "shader_recompiler/ir/value.h"
|
||||
@ -18,7 +19,7 @@ namespace Shader::IR {
|
||||
void DumpProgram(const Program& program, const Info& info, const std::string& type) {
|
||||
using namespace Common::FS;
|
||||
|
||||
if (!Config::dumpShaders()) {
|
||||
if (!EmulatorSettings.IsDumpShaders()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
#include "core/memory.h"
|
||||
@ -229,8 +229,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
ce_task = ProcessCeUpdate(ccb);
|
||||
RESUME_GFX(ce_task);
|
||||
}
|
||||
const bool host_markers_enabled = rasterizer && Config::getVkHostMarkersEnabled();
|
||||
const bool guest_markers_enabled = rasterizer && Config::getVkGuestMarkersEnabled();
|
||||
const bool host_markers_enabled = rasterizer && EmulatorSettings.IsVkHostMarkersEnabled();
|
||||
const bool guest_markers_enabled = rasterizer && EmulatorSettings.IsVkGuestMarkersEnabled();
|
||||
|
||||
const auto base_addr = reinterpret_cast<uintptr_t>(dcb.data());
|
||||
while (!dcb.empty()) {
|
||||
@ -899,7 +899,7 @@ template <bool is_indirect>
|
||||
Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
FIBER_ENTER(acb_task_name[vqid]);
|
||||
auto& queue = asc_queues[{vqid}];
|
||||
const bool host_markers_enabled = rasterizer && Config::getVkHostMarkersEnabled();
|
||||
const bool host_markers_enabled = rasterizer && EmulatorSettings.IsVkHostMarkersEnabled();
|
||||
|
||||
struct IndirectPatch {
|
||||
const PM4Header* header;
|
||||
@ -1202,7 +1202,7 @@ Liverpool::CmdBuffer Liverpool::CopyCmdBuffers(std::span<const u32> dcb, std::sp
|
||||
void Liverpool::SubmitGfx(std::span<const u32> dcb, std::span<const u32> ccb) {
|
||||
auto& queue = mapped_queues[GfxQueueId];
|
||||
|
||||
if (Config::copyGPUCmdBuffers()) {
|
||||
if (EmulatorSettings.IsCopyGpuBuffers()) {
|
||||
std::tie(dcb, ccb) = CopyCmdBuffers(dcb, ccb);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/types.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "video_core/buffer_cache/region_manager.h"
|
||||
|
||||
namespace VideoCore {
|
||||
@ -73,7 +74,7 @@ public:
|
||||
// modified. If we need to flush the flush function is going to perform CPU
|
||||
// state change.
|
||||
std::scoped_lock lk{manager->lock};
|
||||
if (Config::getReadbacksMode() != Config::GpuReadbacksMode::Disabled &&
|
||||
if (EmulatorSettings.GetReadbacksMode() != GpuReadbacksMode::Disabled &&
|
||||
manager->template IsRegionModified<Type::GPU>(offset, size)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include "common/adaptive_mutex.h"
|
||||
@ -95,7 +95,7 @@ public:
|
||||
}
|
||||
if constexpr (type == Type::CPU) {
|
||||
UpdateProtection<!enable, false>();
|
||||
} else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::Precise) {
|
||||
} else if (EmulatorSettings.GetReadbacksMode() == GpuReadbacksMode::Precise) {
|
||||
UpdateProtection<enable, true>();
|
||||
}
|
||||
}
|
||||
@ -126,7 +126,7 @@ public:
|
||||
bits.UnsetRange(start_page, end_page);
|
||||
if constexpr (type == Type::CPU) {
|
||||
UpdateProtection<true, false>();
|
||||
} else if (Config::getReadbacksMode() != Config::GpuReadbacksMode::Disabled) {
|
||||
} else if (EmulatorSettings.GetReadbacksMode() != GpuReadbacksMode::Disabled) {
|
||||
UpdateProtection<false, true>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
#include "video_core/cache_storage.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
@ -95,7 +95,7 @@ void DataBase::Open() {
|
||||
const auto& game_info = Common::ElfInfo::Instance();
|
||||
|
||||
using namespace Common::FS;
|
||||
if (Config::isPipelineCacheArchived()) {
|
||||
if (EmulatorSettings.IsPipelineCacheArchived()) {
|
||||
mz_zip_zero_struct(&zip_ar);
|
||||
|
||||
cache_path = GetUserPath(PathType::CacheDir) /
|
||||
@ -128,7 +128,7 @@ void DataBase::Close() {
|
||||
io_worker.request_stop();
|
||||
io_worker.join();
|
||||
|
||||
if (Config::isPipelineCacheArchived()) {
|
||||
if (EmulatorSettings.IsPipelineCacheArchived()) {
|
||||
mz_zip_writer_finalize_archive(&zip_ar);
|
||||
mz_zip_writer_end(&zip_ar);
|
||||
}
|
||||
@ -142,7 +142,7 @@ bool WriteVector(const BlobType type, std::filesystem::path&& path_, std::vector
|
||||
auto request = std::packaged_task<void()>{[=]() {
|
||||
auto path{path_};
|
||||
path.replace_extension(GetBlobFileExtension(type));
|
||||
if (Config::isPipelineCacheArchived()) {
|
||||
if (EmulatorSettings.IsPipelineCacheArchived()) {
|
||||
ASSERT_MSG(!ar_is_read_only,
|
||||
"The archive is read-only. Did you forget to call `FinishPreload`?");
|
||||
if (!mz_zip_writer_add_mem(&zip_ar, path.string().c_str(), v.data(),
|
||||
@ -169,7 +169,7 @@ template <typename T>
|
||||
void LoadVector(BlobType type, std::filesystem::path& path, std::vector<T>& v) {
|
||||
using namespace Common::FS;
|
||||
path.replace_extension(GetBlobFileExtension(type));
|
||||
if (Config::isPipelineCacheArchived()) {
|
||||
if (EmulatorSettings.IsPipelineCacheArchived()) {
|
||||
int index{-1};
|
||||
index = mz_zip_reader_locate_file(&zip_ar, path.string().c_str(), nullptr, 0);
|
||||
if (index < 0) {
|
||||
@ -192,7 +192,8 @@ bool DataBase::Save(BlobType type, const std::string& name, std::vector<u8>&& da
|
||||
return false;
|
||||
}
|
||||
|
||||
auto path = Config::isPipelineCacheArchived() ? std::filesystem::path{name} : cache_path / name;
|
||||
auto path = EmulatorSettings.IsPipelineCacheArchived() ? std::filesystem::path{name}
|
||||
: cache_path / name;
|
||||
return WriteVector(type, std::move(path), std::move(data));
|
||||
}
|
||||
|
||||
@ -201,7 +202,8 @@ bool DataBase::Save(BlobType type, const std::string& name, std::vector<u32>&& d
|
||||
return false;
|
||||
}
|
||||
|
||||
auto path = Config::isPipelineCacheArchived() ? std::filesystem::path{name} : cache_path / name;
|
||||
auto path = EmulatorSettings.IsPipelineCacheArchived() ? std::filesystem::path{name}
|
||||
: cache_path / name;
|
||||
return WriteVector(type, std::move(path), std::move(data));
|
||||
}
|
||||
|
||||
@ -210,7 +212,8 @@ void DataBase::Load(BlobType type, const std::string& name, std::vector<u8>& dat
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = Config::isPipelineCacheArchived() ? std::filesystem::path{name} : cache_path / name;
|
||||
auto path = EmulatorSettings.IsPipelineCacheArchived() ? std::filesystem::path{name}
|
||||
: cache_path / name;
|
||||
return LoadVector(type, path, data);
|
||||
}
|
||||
|
||||
@ -219,13 +222,14 @@ void DataBase::Load(BlobType type, const std::string& name, std::vector<u32>& da
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = Config::isPipelineCacheArchived() ? std::filesystem::path{name} : cache_path / name;
|
||||
auto path = EmulatorSettings.IsPipelineCacheArchived() ? std::filesystem::path{name}
|
||||
: cache_path / name;
|
||||
return LoadVector(type, path, data);
|
||||
}
|
||||
|
||||
void DataBase::ForEachBlob(BlobType type, const std::function<void(std::vector<u8>&& data)>& func) {
|
||||
const auto& ext = GetBlobFileExtension(type);
|
||||
if (Config::isPipelineCacheArchived()) {
|
||||
if (EmulatorSettings.IsPipelineCacheArchived()) {
|
||||
const auto num_files = mz_zip_reader_get_num_files(&zip_ar);
|
||||
for (int index = 0; index < num_files; ++index) {
|
||||
std::array<char, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE> file_name{};
|
||||
@ -255,7 +259,7 @@ void DataBase::ForEachBlob(BlobType type, const std::function<void(std::vector<u
|
||||
}
|
||||
|
||||
void DataBase::FinishPreload() {
|
||||
if (Config::isPipelineCacheArchived()) {
|
||||
if (EmulatorSettings.IsPipelineCacheArchived()) {
|
||||
mz_zip_writer_init_from_reader(&zip_ar, cache_path.string().c_str());
|
||||
ar_is_read_only = false;
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "video_core/renderdoc.h"
|
||||
|
||||
#include <renderdoc_app.h>
|
||||
@ -31,7 +31,7 @@ void LoadRenderDoc() {
|
||||
|
||||
// Check if we are running by RDoc GUI
|
||||
HMODULE mod = GetModuleHandleA("renderdoc.dll");
|
||||
if (!mod && Config::isRdocEnabled()) {
|
||||
if (!mod && EmulatorSettings.IsRenderdocEnabled()) {
|
||||
// If enabled in config, try to load RDoc runtime in offline mode
|
||||
HKEY h_reg_key;
|
||||
LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
@ -67,7 +67,7 @@ void LoadRenderDoc() {
|
||||
#endif
|
||||
// Check if we are running by RDoc GUI
|
||||
void* mod = dlopen(RENDERDOC_LIB, RTLD_NOW | RTLD_NOLOAD);
|
||||
if (!mod && Config::isRdocEnabled()) {
|
||||
if (!mod && EmulatorSettings.IsRenderdocEnabled()) {
|
||||
// If enabled in config, try to load RDoc runtime in offline mode
|
||||
if ((mod = dlopen(RENDERDOC_LIB, RTLD_NOW))) {
|
||||
const auto RENDERDOC_GetAPI =
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "video_core/host_shaders/fsr_comp.h"
|
||||
#include "video_core/renderer_vulkan/host_passes/fsr_pass.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
@ -164,7 +164,7 @@ vk::ImageView FsrPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input,
|
||||
CreateImages(img);
|
||||
}
|
||||
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||
.pLabelName = "Host/FSR",
|
||||
});
|
||||
@ -387,7 +387,7 @@ vk::ImageView FsrPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input,
|
||||
.pImageMemoryBarriers = return_barrier.data(),
|
||||
});
|
||||
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.endDebugUtilsLabelEXT();
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "video_core/renderer_vulkan/host_passes/pp_pass.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "video_core/host_shaders/fs_tri_vert.h"
|
||||
#include "video_core/host_shaders/post_process_frag.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
@ -188,7 +188,7 @@ void PostProcessingPass::Create(vk::Device device, const vk::Format surface_form
|
||||
|
||||
void PostProcessingPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input,
|
||||
vk::Extent2D input_size, Frame& frame, Settings settings) {
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||
.pLabelName = "Host/Post processing",
|
||||
});
|
||||
@ -279,7 +279,7 @@ void PostProcessingPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input,
|
||||
.pImageMemoryBarriers = &post_barrier,
|
||||
});
|
||||
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.endDebugUtilsLabelEXT();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/hash.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/recompiler.h"
|
||||
@ -300,7 +300,7 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
|
||||
RegisterPipelineData(graphics_key, pipeline_hash, sdata);
|
||||
++num_new_pipelines;
|
||||
|
||||
if (Config::collectShadersForDebug()) {
|
||||
if (EmulatorSettings.IsShaderCollect()) {
|
||||
for (auto stage = 0; stage < MaxShaderStages; ++stage) {
|
||||
if (infos[stage]) {
|
||||
auto& m = modules[stage];
|
||||
@ -329,7 +329,7 @@ const ComputePipeline* PipelineCache::GetComputePipeline() {
|
||||
RegisterPipelineData(compute_key, sdata);
|
||||
++num_new_pipelines;
|
||||
|
||||
if (Config::collectShadersForDebug()) {
|
||||
if (EmulatorSettings.IsShaderCollect()) {
|
||||
auto& m = modules[0];
|
||||
module_related_pipelines[m].emplace_back(compute_key);
|
||||
}
|
||||
@ -554,7 +554,7 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, Shader::Runtim
|
||||
vk::ShaderModule module;
|
||||
|
||||
auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv");
|
||||
const bool is_patched = patch && Config::patchShaders();
|
||||
const bool is_patched = patch && EmulatorSettings.IsPatchShaders();
|
||||
if (is_patched) {
|
||||
LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash);
|
||||
module = CompileSPV(*patch, instance.GetDevice());
|
||||
@ -566,7 +566,7 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, Shader::Runtim
|
||||
|
||||
const auto name = GetShaderName(info.stage, info.pgm_hash, perm_idx);
|
||||
Vulkan::SetObjectName(instance.GetDevice(), module, name);
|
||||
if (Config::collectShadersForDebug()) {
|
||||
if (EmulatorSettings.IsShaderCollect()) {
|
||||
DebugState.CollectShader(name, info.l_stage, module, spv, code,
|
||||
patch ? *patch : std::span<const u32>{}, is_patched);
|
||||
}
|
||||
@ -659,7 +659,7 @@ std::string PipelineCache::GetShaderName(Shader::Stage stage, u64 hash,
|
||||
|
||||
void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage,
|
||||
size_t perm_idx, std::string_view ext) {
|
||||
if (!Config::dumpShaders()) {
|
||||
if (!EmulatorSettings.IsDumpShaders()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/serdes.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "shader_recompiler/frontend/fetch_shader.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "video_core/cache_storage.h"
|
||||
@ -295,7 +295,7 @@ bool PipelineCache::LoadPipelineStage(Serialization::Archive& ar, size_t stage)
|
||||
}
|
||||
|
||||
void PipelineCache::WarmUp() {
|
||||
if (!Config::isPipelineCacheEnabled()) {
|
||||
if (!EmulatorSettings.IsPipelineCacheEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -17,9 +17,9 @@
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
|
||||
@ -87,7 +87,7 @@ vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::WindowSDL& e
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else if (window_info.type == Frontend::WindowSystemType::Wayland) {
|
||||
if (Config::isRdocEnabled()) {
|
||||
if (EmulatorSettings.IsRenderdocEnabled()) {
|
||||
LOG_ERROR(Render_Vulkan,
|
||||
"RenderDoc is not compatible with Wayland, use an X11 window instead.");
|
||||
}
|
||||
@ -200,7 +200,7 @@ std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window
|
||||
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
if (Config::allowHDR()) {
|
||||
if (EmulatorSettings.IsHdrAllowed()) {
|
||||
extensions.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
@ -306,9 +306,9 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e
|
||||
LOG_INFO(Render_Vulkan, "Enabled instance layers: {}", layers_string);
|
||||
|
||||
// Validation settings
|
||||
vk::Bool32 enable_core = Config::vkValidationCoreEnabled() ? vk::True : vk::False;
|
||||
vk::Bool32 enable_sync = Config::vkValidationSyncEnabled() ? vk::True : vk::False;
|
||||
vk::Bool32 enable_gpuav = Config::vkValidationGpuEnabled() ? vk::True : vk::False;
|
||||
vk::Bool32 enable_core = EmulatorSettings.IsVkValidationCoreEnabled() ? vk::True : vk::False;
|
||||
vk::Bool32 enable_sync = EmulatorSettings.IsVkValidationSyncEnabled() ? vk::True : vk::False;
|
||||
vk::Bool32 enable_gpuav = EmulatorSettings.IsVkValidationGpuEnabled() ? vk::True : vk::False;
|
||||
|
||||
// Crash diagnostics settings
|
||||
static const auto crash_diagnostic_path =
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
#include "imgui/renderer/imgui_impl_vulkan.h"
|
||||
@ -104,8 +104,8 @@ static vk::Rect2D FitImage(s32 frame_width, s32 frame_height, s32 swapchain_widt
|
||||
|
||||
Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_)
|
||||
: window{window_}, liverpool{liverpool_},
|
||||
instance{window, Config::getGpuId(), Config::vkValidationEnabled(),
|
||||
Config::getVkCrashDiagnosticEnabled()},
|
||||
instance{window, EmulatorSettings.GetGpuId(), EmulatorSettings.IsVkValidationEnabled(),
|
||||
EmulatorSettings.IsVkCrashDiagnosticEnabled()},
|
||||
draw_scheduler{instance}, present_scheduler{instance}, flip_scheduler{instance},
|
||||
swapchain{instance, window},
|
||||
rasterizer{std::make_unique<Rasterizer>(instance, draw_scheduler, liverpool)},
|
||||
@ -124,9 +124,10 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_
|
||||
free_queue.push(&frame);
|
||||
}
|
||||
|
||||
fsr_settings.enable = Config::getFsrEnabled();
|
||||
fsr_settings.use_rcas = Config::getRcasEnabled();
|
||||
fsr_settings.rcas_attenuation = static_cast<float>(Config::getRcasAttenuation() / 1000.f);
|
||||
fsr_settings.enable = EmulatorSettings.IsFsrEnabled();
|
||||
fsr_settings.use_rcas = EmulatorSettings.IsRcasEnabled();
|
||||
fsr_settings.rcas_attenuation =
|
||||
static_cast<float>(EmulatorSettings.GetRcasAttenuation() / 1000.f);
|
||||
|
||||
fsr_pass.Create(device, instance.GetAllocator(), num_images);
|
||||
pp_pass.Create(device, swapchain.GetSurfaceFormat().format);
|
||||
@ -465,7 +466,7 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) {
|
||||
auto& scheduler = present_scheduler;
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||
.pLabelName = "Present",
|
||||
});
|
||||
@ -577,7 +578,7 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) {
|
||||
ImGui::SetCursorPos(ImGui::GetCursorStartPos() + offset);
|
||||
ImGui::Image(game_texture, size);
|
||||
|
||||
if (Config::nullGpu()) {
|
||||
if (EmulatorSettings.IsNullGPU()) {
|
||||
Core::Devtools::Layer::DrawNullGpuNotice();
|
||||
}
|
||||
}
|
||||
@ -595,8 +596,7 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) {
|
||||
TracyVkCollect(profiler_ctx, cmdbuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (Config::getVkHostMarkersEnabled()) {
|
||||
if (EmulatorSettings.IsVkHostMarkersEnabled()) {
|
||||
cmdbuf.endDebugUtilsLabelEXT();
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/memory.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
@ -38,7 +38,7 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_,
|
||||
texture_cache{instance, scheduler, liverpool_, buffer_cache, page_manager},
|
||||
liverpool{liverpool_}, memory{Core::Memory::Instance()},
|
||||
pipeline_cache{instance, scheduler, liverpool} {
|
||||
if (!Config::nullGpu()) {
|
||||
if (!EmulatorSettings.IsNullGPU()) {
|
||||
liverpool->BindRasterizer(this);
|
||||
}
|
||||
memory->SetRasterizer(this);
|
||||
@ -1313,8 +1313,8 @@ void Rasterizer::UpdateColorBlendingState(const GraphicsPipeline* pipeline) cons
|
||||
}
|
||||
|
||||
void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest) {
|
||||
if ((from_guest && !Config::getVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !Config::getVkHostMarkersEnabled())) {
|
||||
if ((from_guest && !EmulatorSettings.IsVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !EmulatorSettings.IsVkHostMarkersEnabled())) {
|
||||
return;
|
||||
}
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
@ -1324,8 +1324,8 @@ void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest)
|
||||
}
|
||||
|
||||
void Rasterizer::ScopeMarkerEnd(bool from_guest) {
|
||||
if ((from_guest && !Config::getVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !Config::getVkHostMarkersEnabled())) {
|
||||
if ((from_guest && !EmulatorSettings.IsVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !EmulatorSettings.IsVkHostMarkersEnabled())) {
|
||||
return;
|
||||
}
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
@ -1333,8 +1333,8 @@ void Rasterizer::ScopeMarkerEnd(bool from_guest) {
|
||||
}
|
||||
|
||||
void Rasterizer::ScopedMarkerInsert(const std::string_view& str, bool from_guest) {
|
||||
if ((from_guest && !Config::getVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !Config::getVkHostMarkersEnabled())) {
|
||||
if ((from_guest && !EmulatorSettings.IsVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !EmulatorSettings.IsVkHostMarkersEnabled())) {
|
||||
return;
|
||||
}
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
@ -1345,8 +1345,8 @@ void Rasterizer::ScopedMarkerInsert(const std::string_view& str, bool from_guest
|
||||
|
||||
void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 color,
|
||||
bool from_guest) {
|
||||
if ((from_guest && !Config::getVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !Config::getVkHostMarkersEnabled())) {
|
||||
if ((from_guest && !EmulatorSettings.IsVkGuestMarkersEnabled()) ||
|
||||
(!from_guest && !EmulatorSettings.IsVkHostMarkersEnabled())) {
|
||||
return;
|
||||
}
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
@ -164,7 +164,7 @@ void Swapchain::FindPresentFormat() {
|
||||
return format == SURFACE_FORMAT_HDR;
|
||||
}) != formats.end();
|
||||
// Also make sure that user allowed us to use HDR
|
||||
supports_hdr &= Config::allowHDR();
|
||||
supports_hdr &= EmulatorSettings.IsHdrAllowed();
|
||||
|
||||
// If there is a single undefined surface format, the device doesn't care, so we'll just use
|
||||
// RGBA sRGB.
|
||||
@ -199,7 +199,7 @@ void Swapchain::FindPresentMode() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto requested_mode = Config::getPresentMode();
|
||||
const auto requested_mode = EmulatorSettings.GetPresentMode();
|
||||
if (requested_mode == "Mailbox") {
|
||||
present_mode = vk::PresentModeKHR::eMailbox;
|
||||
} else if (requested_mode == "Fifo") {
|
||||
@ -208,7 +208,7 @@ void Swapchain::FindPresentMode() {
|
||||
present_mode = vk::PresentModeKHR::eImmediate;
|
||||
} else {
|
||||
LOG_ERROR(Render_Vulkan, "Unknown present mode {}, defaulting to Mailbox.",
|
||||
Config::getPresentMode());
|
||||
EmulatorSettings.GetPresentMode());
|
||||
present_mode = vk::PresentModeKHR::eMailbox;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user