Compare commits
31 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b149768d3 | ||
|
|
d6eca26279 | ||
|
|
1e4024c7c4 | ||
|
|
573b88ab27 | ||
|
|
6c728b4819 | ||
|
|
e5a80d1ed4 | ||
|
|
4e80486361 | ||
|
|
22026e617d | ||
|
|
4dd3a9a22b | ||
|
|
2bfcaabf5f | ||
|
|
af2d90696e | ||
|
|
edcb75a037 | ||
|
|
59633ac921 | ||
|
|
518ac27996 | ||
|
|
0f042773d3 | ||
|
|
f33be7c01c | ||
|
|
34e5693be8 | ||
|
|
4c54b9446a | ||
|
|
1485616a54 | ||
|
|
11a3f59936 | ||
|
|
7539d2d9d4 | ||
|
|
e9166f4664 | ||
|
|
7d2fb1cffb | ||
|
|
295b22474a | ||
|
|
ce4052e527 | ||
|
|
23daaccf84 | ||
|
|
b71a463df6 | ||
|
|
ab9abac488 | ||
|
|
1de29dd396 | ||
|
|
5b70c9c493 | ||
|
|
7452a10688 |
@@ -30,8 +30,8 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
|
||||
Stream::ReleaseCallback&& release_callback) {
|
||||
if (!sink) {
|
||||
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
|
||||
sink = sink_details.factory(Settings::values.audio_device_id);
|
||||
const SinkDetails& sink_details = GetSinkDetails(Settings::values->sink_id);
|
||||
sink = sink_details.factory(Settings::values->audio_device_id);
|
||||
}
|
||||
|
||||
return std::make_shared<Stream>(
|
||||
|
||||
@@ -163,7 +163,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
if (Settings::values->enable_audio_stretching) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
|
||||
@@ -63,7 +63,7 @@ s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
|
||||
const float volume{std::clamp(Settings::values->volume, 0.0f, 1.0f)};
|
||||
|
||||
if (volume == 1.0f) {
|
||||
return;
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace Core {
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
namespace {
|
||||
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
const std::string& path) {
|
||||
// To account for split 00+01+etc files.
|
||||
@@ -70,6 +69,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Runs a CPU core while the system is powered on
|
||||
void RunCpuCore(Cpu& cpu_state) {
|
||||
while (Core::System::GetInstance().IsPoweredOn()) {
|
||||
@@ -136,6 +136,16 @@ struct System::Impl {
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
// Force update title ID for per game settings in case any services use them
|
||||
u64 title_id = 0;
|
||||
if (app_loader->ReadProgramId(title_id) == Loader::ResultStatus::Success)
|
||||
Settings::values.SetCurrentTitleID(title_id);
|
||||
else
|
||||
Settings::values.SetCurrentTitleID(Settings::DEFAULT_PER_GAME);
|
||||
|
||||
// Write config to log
|
||||
Settings::values->LogSettings();
|
||||
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
@@ -54,6 +55,9 @@ class TelemetrySession;
|
||||
|
||||
struct PerfStatsResults;
|
||||
|
||||
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
const std::string& path);
|
||||
|
||||
class System {
|
||||
public:
|
||||
System(const System&) = delete;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -50,6 +51,10 @@ PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
|
||||
|
||||
PatchManager::~PatchManager() = default;
|
||||
|
||||
u64 PatchManager::GetTitleID() const {
|
||||
return title_id;
|
||||
}
|
||||
|
||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||
|
||||
@@ -61,7 +66,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
|
||||
if (update != nullptr) {
|
||||
|
||||
const auto& disabled = Settings::values[title_id].disabled_patches;
|
||||
const auto update_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
|
||||
if (!update_disabled && update != nullptr) {
|
||||
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
update->GetExeFS() != nullptr) {
|
||||
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
|
||||
@@ -73,11 +83,16 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
return exefs;
|
||||
}
|
||||
|
||||
static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) {
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const {
|
||||
const auto& disabled = Settings::values[title_id].disabled_patches;
|
||||
|
||||
std::vector<VirtualFile> out;
|
||||
out.reserve(patch_dirs.size());
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
|
||||
continue;
|
||||
|
||||
auto exefs_dir = subdir->GetSubdirectory("exefs");
|
||||
if (exefs_dir != nullptr) {
|
||||
for (const auto& file : exefs_dir->GetFiles()) {
|
||||
@@ -178,6 +193,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values[title_id].disabled_patches;
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
@@ -187,6 +203,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
layers.reserve(patch_dirs.size() + 1);
|
||||
layers_ext.reserve(patch_dirs.size() + 1);
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
|
||||
continue;
|
||||
|
||||
auto romfs_dir = subdir->GetSubdirectory("romfs");
|
||||
if (romfs_dir != nullptr)
|
||||
layers.push_back(std::move(romfs_dir));
|
||||
@@ -232,7 +251,12 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed->GetEntryRaw(update_tid, type);
|
||||
if (update != nullptr) {
|
||||
|
||||
const auto& disabled = Settings::values[title_id].disabled_patches;
|
||||
const auto update_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
|
||||
if (!update_disabled && update != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
@@ -240,7 +264,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
|
||||
romfs = new_nca->GetRomFS();
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
} else if (!update_disabled && update_raw != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
|
||||
@@ -30,6 +30,8 @@ public:
|
||||
explicit PatchManager(u64 title_id);
|
||||
~PatchManager();
|
||||
|
||||
u64 GetTitleID() const;
|
||||
|
||||
// Currently tracked ExeFS patches:
|
||||
// - Game Updates
|
||||
VirtualDir PatchExeFS(VirtualDir exefs) const;
|
||||
@@ -63,6 +65,9 @@ public:
|
||||
std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
|
||||
|
||||
private:
|
||||
std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const;
|
||||
|
||||
u64 title_id;
|
||||
};
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values->use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
@@ -439,7 +439,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode};
|
||||
const bool use_docked_mode{Settings::values->use_docked_mode};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
|
||||
@@ -448,7 +448,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode};
|
||||
const bool use_docked_mode{Settings::values->use_docked_mode};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
@@ -72,6 +73,13 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
|
||||
const auto& disabled = Settings::values[current].disabled_patches;
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
rb.Push<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push<u32>(static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
|
||||
@@ -91,6 +99,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values[current].disabled_patches;
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
|
||||
out = {};
|
||||
|
||||
if (out.size() < offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
|
||||
@@ -131,11 +131,11 @@ void Controller_NPad::OnInit() {
|
||||
}
|
||||
|
||||
void Controller_NPad::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
|
||||
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
|
||||
std::transform(Settings::values->buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
|
||||
Settings::values->buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
|
||||
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
std::transform(Settings::values->analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
Settings::values->analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
case NPadControllerType::Handheld:
|
||||
handheld_entry.connection_status.raw = 0;
|
||||
handheld_entry.connection_status.IsConnected.Assign(1);
|
||||
if (!Settings::values.use_docked_mode) {
|
||||
if (!Settings::values->use_docked_mode) {
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
}
|
||||
handheld_entry.pad_states.raw = pad_state.raw;
|
||||
@@ -504,7 +504,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
|
||||
return false;
|
||||
}
|
||||
// Handheld should not be supported in docked mode
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values->use_docked_mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
|
||||
if (IsControllerSupported(priority)) {
|
||||
return priority;
|
||||
}
|
||||
const auto is_docked = Settings::values.use_docked_mode;
|
||||
const auto is_docked = Settings::values->use_docked_mode;
|
||||
if (is_docked && priority == NPadControllerType::Handheld) {
|
||||
priority = NPadControllerType::JoyDual;
|
||||
if (IsControllerSupported(priority)) {
|
||||
|
||||
@@ -60,6 +60,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnLoadInputDevices() {
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values->touch_device);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -668,7 +668,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values->use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
@@ -877,7 +877,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values->use_docked_mode) {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -242,6 +243,19 @@ public:
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the developer of the application
|
||||
* @param developer Reference to store the application developer into
|
||||
* @return ResultStatus result of function
|
||||
*/
|
||||
virtual ResultStatus ReadDeveloper(std::string& developer) {
|
||||
return ResultStatus::ErrorNotImplemented;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile GetFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
protected:
|
||||
FileSys::VirtualFile file;
|
||||
bool is_loaded = false;
|
||||
|
||||
@@ -150,8 +150,8 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
|
||||
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
|
||||
}
|
||||
|
||||
if (!Settings::values.program_args.empty()) {
|
||||
const auto arg_data = Settings::values.program_args;
|
||||
if (!Settings::values->program_args.empty()) {
|
||||
const auto arg_data = Settings::values->program_args;
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
|
||||
@@ -122,8 +122,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
|
||||
codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
|
||||
}
|
||||
|
||||
if (should_pass_arguments && !Settings::values.program_args.empty()) {
|
||||
const auto arg_data = Settings::values.program_args;
|
||||
if (should_pass_arguments && !Settings::values->program_args.empty()) {
|
||||
const auto arg_data = Settings::values->program_args;
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
|
||||
@@ -148,4 +148,11 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
developer = nacp_file->GetDeveloperName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NSP> nsp;
|
||||
|
||||
@@ -120,4 +120,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
developer = nacp_file->GetDeveloperName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadDeveloper(std::string& developer) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::XCI> xci;
|
||||
|
||||
@@ -74,13 +74,13 @@ double PerfStats::GetLastFrameTimeScale() {
|
||||
}
|
||||
|
||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_frame_limit) {
|
||||
if (!Settings::values->use_frame_limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
|
||||
const double sleep_scale = Settings::values.frame_limit / 100.0;
|
||||
const double sleep_scale = Settings::values->frame_limit / 100.0;
|
||||
|
||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||
|
||||
@@ -12,6 +12,111 @@ namespace Settings {
|
||||
|
||||
Values values = {};
|
||||
|
||||
void PerGameValues::LogSettings() {
|
||||
LOG_INFO(Config, "Buttons:");
|
||||
for (std::size_t i = 0; i < NativeButton::NumButtons; ++i)
|
||||
LOG_INFO(Config, " Button[{}]: ", buttons[i]);
|
||||
|
||||
LOG_INFO(Config, "Analogs:");
|
||||
for (std::size_t i = 0; i < NativeAnalog::NumAnalogs; ++i)
|
||||
LOG_INFO(Config, " Analog[{}]: ", analogs[i]);
|
||||
|
||||
LOG_INFO(Config, "Motion Device: {}", motion_device);
|
||||
LOG_INFO(Config, "Touch Device: {}", touch_device);
|
||||
|
||||
LOG_INFO(Config, "Docked Mode: {}", use_docked_mode);
|
||||
|
||||
LOG_INFO(Config, "Resolution Factor: {}", resolution_factor);
|
||||
LOG_INFO(Config, "Frame Limit Enabled: {}", use_frame_limit);
|
||||
LOG_INFO(Config, "Frame Limit: {}", frame_limit);
|
||||
|
||||
LOG_INFO(Config, "Background Red: {}", bg_red);
|
||||
LOG_INFO(Config, "Background Green: {}", bg_green);
|
||||
LOG_INFO(Config, "Background Blue: {}", bg_blue);
|
||||
|
||||
LOG_INFO(Config, "Audio Sink ID: {}", sink_id);
|
||||
LOG_INFO(Config, "Audio Stretching: {}", enable_audio_stretching);
|
||||
LOG_INFO(Config, "Audio Device ID: {}", audio_device_id);
|
||||
LOG_INFO(Config, "Volume: {}", volume);
|
||||
|
||||
LOG_INFO(Config, "Program Arguments: {}", program_args);
|
||||
|
||||
LOG_INFO(Config, "Disabled Patches:");
|
||||
for (const auto& patch : disabled_patches)
|
||||
LOG_INFO(Config, " - {}", patch);
|
||||
}
|
||||
|
||||
Values::Values() : current_game(default_game) {}
|
||||
|
||||
void Values::SetUpdateCurrentGameFunction(std::function<bool(u64, PerGameValues&)> new_function) {
|
||||
update_current_game = std::move(new_function);
|
||||
}
|
||||
|
||||
u64 Values::CurrentTitleID() const {
|
||||
return current_title_id;
|
||||
}
|
||||
|
||||
void Values::SetCurrentTitleID(u64 title_id) {
|
||||
// Ignore Updates
|
||||
title_id &= ~0x800;
|
||||
|
||||
if (current_title_id == title_id)
|
||||
return;
|
||||
|
||||
update_current_game(title_id, current_game);
|
||||
current_title_id = title_id;
|
||||
}
|
||||
|
||||
PerGameValues& Values::operator[](u64 title_id) {
|
||||
// Ignore Updates
|
||||
title_id &= ~0x800;
|
||||
|
||||
if (current_title_id == title_id)
|
||||
return current_game;
|
||||
|
||||
if (!update_current_game(title_id, current_game))
|
||||
return default_game;
|
||||
|
||||
current_title_id = title_id;
|
||||
return current_game;
|
||||
}
|
||||
|
||||
const PerGameValues& Values::operator[](u64 title_id) const {
|
||||
// Ignore Updates
|
||||
title_id &= ~0x800;
|
||||
|
||||
if (current_title_id == title_id)
|
||||
return current_game;
|
||||
|
||||
return default_game;
|
||||
}
|
||||
|
||||
PerGameValues* Values::operator->() {
|
||||
if (current_title_id == 0)
|
||||
return &default_game;
|
||||
|
||||
return ¤t_game;
|
||||
}
|
||||
|
||||
const PerGameValues* Values::operator->() const {
|
||||
if (current_title_id == 0)
|
||||
return &default_game;
|
||||
|
||||
return ¤t_game;
|
||||
}
|
||||
|
||||
PerGameValues& Values::operator*() {
|
||||
if (current_title_id == 0)
|
||||
return default_game;
|
||||
return current_game;
|
||||
}
|
||||
|
||||
const PerGameValues& Values::operator*() const {
|
||||
if (current_title_id == 0)
|
||||
return default_game;
|
||||
return current_game;
|
||||
}
|
||||
|
||||
void Apply() {
|
||||
GDBStub::SetServerPort(values.gdbstub_port);
|
||||
GDBStub::ToggleServer(values.use_gdbstub);
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
constexpr u64 DEFAULT_PER_GAME = 0;
|
||||
|
||||
namespace NativeButton {
|
||||
enum Values {
|
||||
A,
|
||||
@@ -110,43 +115,27 @@ static const std::array<const char*, NumAnalogs> mapping = {{
|
||||
}};
|
||||
} // namespace NativeAnalog
|
||||
|
||||
struct Values {
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
bool enable_nfc;
|
||||
int current_user;
|
||||
int language_index;
|
||||
struct PerGameValues {
|
||||
void LogSettings();
|
||||
|
||||
// Controls
|
||||
std::array<std::string, NativeButton::NumButtons> buttons;
|
||||
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
|
||||
std::string motion_device;
|
||||
std::string touch_device;
|
||||
std::atomic_bool is_device_reload_pending{true};
|
||||
|
||||
// Core
|
||||
bool use_cpu_jit;
|
||||
bool use_multi_core;
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
std::string nand_dir;
|
||||
std::string sdmc_dir;
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
|
||||
// Renderer
|
||||
float resolution_factor;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_accurate_gpu_emulation;
|
||||
|
||||
float bg_red;
|
||||
float bg_green;
|
||||
float bg_blue;
|
||||
|
||||
std::string log_filter;
|
||||
|
||||
bool use_dev_keys;
|
||||
|
||||
// Audio
|
||||
std::string sink_id;
|
||||
bool enable_audio_stretching;
|
||||
@@ -154,15 +143,69 @@ struct Values {
|
||||
float volume;
|
||||
|
||||
// Debugging
|
||||
std::string program_args;
|
||||
|
||||
// Add-Ons
|
||||
std::vector<std::string> disabled_patches;
|
||||
};
|
||||
|
||||
struct Values {
|
||||
Values();
|
||||
|
||||
// Per-Game
|
||||
PerGameValues default_game;
|
||||
|
||||
void SetUpdateCurrentGameFunction(std::function<bool(u64, PerGameValues&)> new_function);
|
||||
u64 CurrentTitleID() const;
|
||||
void SetCurrentTitleID(u64 title_id);
|
||||
|
||||
PerGameValues& operator[](u64 title_id);
|
||||
const PerGameValues& operator[](u64 title_id) const;
|
||||
PerGameValues* operator->();
|
||||
const PerGameValues* operator->() const;
|
||||
PerGameValues& operator*();
|
||||
const PerGameValues& operator*() const;
|
||||
|
||||
// Core
|
||||
bool use_cpu_jit;
|
||||
bool use_multi_core;
|
||||
|
||||
// System
|
||||
std::string username;
|
||||
bool enable_nfc;
|
||||
int current_user;
|
||||
int language_index;
|
||||
|
||||
// Renderer
|
||||
bool use_accurate_gpu_emulation;
|
||||
|
||||
// Input
|
||||
std::atomic_bool is_device_reload_pending{true};
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
std::string nand_dir;
|
||||
std::string sdmc_dir;
|
||||
|
||||
// Debugging
|
||||
std::string log_filter;
|
||||
bool use_dev_keys;
|
||||
bool use_gdbstub;
|
||||
u16 gdbstub_port;
|
||||
std::string program_args;
|
||||
|
||||
// WebService
|
||||
bool enable_telemetry;
|
||||
std::string web_api_url;
|
||||
std::string yuzu_username;
|
||||
std::string yuzu_token;
|
||||
|
||||
private:
|
||||
u64 current_title_id = 0;
|
||||
PerGameValues current_game;
|
||||
|
||||
std::function<bool(u64, PerGameValues&)> update_current_game = [](u64 in, PerGameValues& val) {
|
||||
return false;
|
||||
};
|
||||
} extern values;
|
||||
|
||||
void Apply();
|
||||
|
||||
@@ -152,21 +152,22 @@ TelemetrySession::TelemetrySession() {
|
||||
Telemetry::AppendOSInfo(field_collection);
|
||||
|
||||
// Log user configuration information
|
||||
AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values->sink_id);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching);
|
||||
Settings::values->enable_audio_stretching);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
|
||||
Settings::values.use_multi_core);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor);
|
||||
Settings::values->resolution_factor);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
|
||||
Settings::values.use_frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
|
||||
Settings::values->use_frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit",
|
||||
Settings::values->frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers",
|
||||
Settings::values.use_accurate_gpu_emulation);
|
||||
AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
|
||||
Settings::values.use_docked_mode);
|
||||
Settings::values->use_docked_mode);
|
||||
}
|
||||
|
||||
TelemetrySession::~TelemetrySession() {
|
||||
|
||||
@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
|
||||
void RendererBase::RefreshBaseSettings() {
|
||||
UpdateCurrentFramebufferLayout();
|
||||
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
|
||||
renderer_settings.use_framelimiter = Settings::values->use_frame_limit;
|
||||
renderer_settings.set_background_color = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||
* Initializes the OpenGL state and creates persistent objects.
|
||||
*/
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
glClearColor(Settings::values->bg_red, Settings::values->bg_green, Settings::values->bg_blue,
|
||||
0.0f);
|
||||
|
||||
// Link shaders and get variable locations
|
||||
@@ -371,8 +371,8 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
void RendererOpenGL::DrawScreen() {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values->bg_red, Settings::values->bg_green,
|
||||
Settings::values->bg_blue, 0.0f);
|
||||
}
|
||||
|
||||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
|
||||
@@ -29,6 +29,10 @@ add_executable(yuzu
|
||||
configuration/configure_input.h
|
||||
configuration/configure_system.cpp
|
||||
configuration/configure_system.h
|
||||
configuration/configure_per_game_dialog.cpp
|
||||
configuration/configure_per_game_dialog.h
|
||||
configuration/configure_per_general.cpp
|
||||
configuration/configure_per_general.h
|
||||
configuration/configure_web.cpp
|
||||
configuration/configure_web.h
|
||||
debugger/graphics/graphics_breakpoint_observer.cpp
|
||||
@@ -74,6 +78,8 @@ set(UIS
|
||||
configuration/configure_general.ui
|
||||
configuration/configure_graphics.ui
|
||||
configuration/configure_input.ui
|
||||
configuration/configure_per_game.ui
|
||||
configuration/configure_per_general.ui
|
||||
configuration/configure_system.ui
|
||||
configuration/configure_web.ui
|
||||
hotkeys.ui
|
||||
|
||||
@@ -9,6 +9,42 @@
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
|
||||
PerGameValuesChange CalculateValuesDelta(const Settings::PerGameValues& base,
|
||||
const Settings::PerGameValues& changed) {
|
||||
PerGameValuesChange out{};
|
||||
for (std::size_t i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
if (base.buttons[i] != changed.buttons[i])
|
||||
out.buttons[i] = true;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
if (base.analogs[i] != changed.analogs[i])
|
||||
out.analogs[i] = true;
|
||||
}
|
||||
|
||||
out.motion_device = base.motion_device != changed.motion_device;
|
||||
out.touch_device = base.touch_device != changed.touch_device;
|
||||
|
||||
out.use_docked_mode = base.use_docked_mode != changed.use_docked_mode;
|
||||
|
||||
out.resolution_factor = base.resolution_factor != changed.resolution_factor;
|
||||
out.use_frame_limit = base.use_frame_limit != changed.use_frame_limit;
|
||||
out.frame_limit = base.frame_limit != changed.frame_limit;
|
||||
|
||||
out.bg_red = base.bg_red != changed.bg_red;
|
||||
out.bg_green = base.bg_green != changed.bg_green;
|
||||
out.bg_blue = base.bg_blue != changed.bg_blue;
|
||||
|
||||
out.sink_id = base.sink_id != changed.sink_id;
|
||||
out.enable_audio_stretching = base.enable_audio_stretching != changed.enable_audio_stretching;
|
||||
out.audio_device_id = base.audio_device_id != changed.audio_device_id;
|
||||
out.volume = base.volume != changed.volume;
|
||||
|
||||
out.program_args = base.program_args != changed.program_args;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
|
||||
@@ -47,67 +83,211 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||
},
|
||||
}};
|
||||
|
||||
void Config::ReadValues() {
|
||||
Settings::PerGameValues ApplyValuesDelta(const Settings::PerGameValues& base,
|
||||
const Settings::PerGameValues& changed,
|
||||
const PerGameValuesChange& changes) {
|
||||
Settings::PerGameValues out{};
|
||||
for (std::size_t i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
out.buttons[i] = changes.buttons[i] ? changed.buttons[i] : base.buttons[i];
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
out.analogs[i] = changes.analogs[i] ? changed.analogs[i] : base.analogs[i];
|
||||
}
|
||||
|
||||
out.motion_device = changes.motion_device ? changed.motion_device : base.motion_device;
|
||||
out.touch_device = changes.touch_device ? changed.touch_device : base.touch_device;
|
||||
|
||||
out.use_docked_mode = changes.use_docked_mode ? changed.use_docked_mode : base.use_docked_mode;
|
||||
|
||||
out.resolution_factor =
|
||||
changes.resolution_factor ? changed.resolution_factor : base.resolution_factor;
|
||||
out.use_frame_limit = changes.use_frame_limit ? changed.use_frame_limit : base.use_frame_limit;
|
||||
out.frame_limit = changes.frame_limit ? changed.frame_limit : base.frame_limit;
|
||||
|
||||
out.bg_red = changes.bg_red ? changed.bg_red : base.bg_red;
|
||||
out.bg_green = changes.bg_green ? changed.bg_green : base.bg_green;
|
||||
out.bg_blue = changes.bg_blue ? changed.bg_blue : base.bg_blue;
|
||||
|
||||
out.sink_id = changes.sink_id ? changed.sink_id : base.sink_id;
|
||||
out.enable_audio_stretching = changes.enable_audio_stretching ? changed.enable_audio_stretching
|
||||
: base.enable_audio_stretching;
|
||||
out.audio_device_id = changes.audio_device_id ? changed.audio_device_id : base.audio_device_id;
|
||||
out.volume = changes.volume ? changed.volume : base.volume;
|
||||
|
||||
out.program_args = changes.program_args ? changed.program_args : base.program_args;
|
||||
|
||||
out.disabled_patches = changed.disabled_patches;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void Config::ReadPerGameSettings(Settings::PerGameValues& values) const {
|
||||
qt_config->beginGroup("Controls");
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.buttons[i] =
|
||||
values.buttons[i] =
|
||||
qt_config
|
||||
->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.buttons[i].empty())
|
||||
Settings::values.buttons[i] = default_param;
|
||||
if (values.buttons[i].empty())
|
||||
values.buttons[i] = default_param;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||
default_analogs[i][3], default_analogs[i][4], 0.5f);
|
||||
Settings::values.analogs[i] =
|
||||
values.analogs[i] =
|
||||
qt_config
|
||||
->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.analogs[i].empty())
|
||||
Settings::values.analogs[i] = default_param;
|
||||
if (values.analogs[i].empty())
|
||||
values.analogs[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.motion_device =
|
||||
values.motion_device =
|
||||
qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.touch_device =
|
||||
values.touch_device =
|
||||
qt_config->value("touch_device", "engine:emu_window").toString().toStdString();
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
|
||||
Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
|
||||
qt_config->beginGroup("System");
|
||||
values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
|
||||
Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
|
||||
Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
|
||||
Settings::values.use_accurate_gpu_emulation =
|
||||
qt_config->value("use_accurate_gpu_emulation", false).toBool();
|
||||
values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
|
||||
values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
|
||||
values.frame_limit = qt_config->value("frame_limit", 100).toInt();
|
||||
|
||||
Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
|
||||
Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
|
||||
Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();
|
||||
values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
|
||||
values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
|
||||
values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Audio");
|
||||
Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
|
||||
Settings::values.enable_audio_stretching =
|
||||
qt_config->value("enable_audio_stretching", true).toBool();
|
||||
Settings::values.audio_device_id =
|
||||
qt_config->value("output_device", "auto").toString().toStdString();
|
||||
Settings::values.volume = qt_config->value("volume", 1).toFloat();
|
||||
values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
|
||||
values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool();
|
||||
values.audio_device_id = qt_config->value("output_device", "auto").toString().toStdString();
|
||||
values.volume = qt_config->value("volume", 1).toFloat();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Debugging");
|
||||
values.program_args = qt_config->value("program_args", "").toString().toStdString();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Add Ons");
|
||||
const auto size = qt_config->beginReadArray("Disabled");
|
||||
|
||||
values.disabled_patches = std::vector<std::string>(size);
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
qt_config->setArrayIndex(i);
|
||||
values.disabled_patches[i] = qt_config->value("name", "").toString().toStdString();
|
||||
}
|
||||
qt_config->endArray();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::ReadPerGameSettingsDelta(PerGameValuesChange& values) const {
|
||||
qt_config->beginGroup("Controls");
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
values.buttons[i] = qt_config
|
||||
->value(QString::fromStdString(Settings::NativeButton::mapping[i] +
|
||||
std::string("_changed")),
|
||||
false)
|
||||
.toBool();
|
||||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
values.analogs[i] = qt_config
|
||||
->value(QString::fromStdString(Settings::NativeAnalog::mapping[i] +
|
||||
std::string("_changed")),
|
||||
false)
|
||||
.toBool();
|
||||
}
|
||||
|
||||
values.motion_device = qt_config->value("motion_device_changed", false).toBool();
|
||||
values.touch_device = qt_config->value("touch_device_changed", false).toBool();
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
values.use_docked_mode = qt_config->value("use_docked_mode_changed", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
values.resolution_factor = qt_config->value("resolution_factor_changed", false).toBool();
|
||||
values.use_frame_limit = qt_config->value("use_frame_limit_changed", false).toBool();
|
||||
values.frame_limit = qt_config->value("frame_limit_changed", false).toBool();
|
||||
|
||||
values.bg_red = qt_config->value("bg_red_changed", false).toBool();
|
||||
values.bg_green = qt_config->value("bg_green_changed", false).toBool();
|
||||
values.bg_blue = qt_config->value("bg_blue_changed", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Audio");
|
||||
values.sink_id = qt_config->value("output_engine_changed", false).toBool();
|
||||
values.enable_audio_stretching =
|
||||
qt_config->value("enable_audio_stretching_changed", false).toBool();
|
||||
values.audio_device_id = qt_config->value("output_device_changed", false).toBool();
|
||||
values.volume = qt_config->value("volume_changed", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Debugging");
|
||||
values.program_args = qt_config->value("program_args_changed", false).toBool();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
bool Config::UpdateCurrentGame(u64 title_id, Settings::PerGameValues& values) {
|
||||
if (Settings::values.CurrentTitleID() != 0) {
|
||||
update_values.insert_or_assign(Settings::values.CurrentTitleID(), *Settings::values);
|
||||
}
|
||||
|
||||
if (update_values.find(title_id) != update_values.end()) {
|
||||
values = ApplyValuesDelta(Settings::values.default_game, update_values[title_id],
|
||||
GetPerGameSettingsDelta(title_id));
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto size = qt_config->beginReadArray("Per Game Settings");
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
qt_config->setArrayIndex(i);
|
||||
const auto read_title_id = qt_config->value("title_id", 0).toULongLong();
|
||||
if (read_title_id != title_id)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
|
||||
PerGameValuesChange changes{};
|
||||
ReadPerGameSettings(values);
|
||||
ReadPerGameSettingsDelta(changes);
|
||||
|
||||
values = ApplyValuesDelta(Settings::values.default_game, values, changes);
|
||||
}
|
||||
|
||||
qt_config->endArray();
|
||||
|
||||
if (!found)
|
||||
values = Settings::values.default_game;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
ReadPerGameSettings(Settings::values.default_game);
|
||||
Settings::values.SetUpdateCurrentGameFunction(
|
||||
[this](u64 title_id, Settings::PerGameValues& values) {
|
||||
return UpdateCurrentGame(title_id, values);
|
||||
});
|
||||
|
||||
qt_config->beginGroup("Data Storage");
|
||||
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
|
||||
FileUtil::GetUserPath(
|
||||
@@ -126,8 +306,12 @@ void Config::ReadValues() {
|
||||
.toStdString());
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
|
||||
Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
|
||||
Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
|
||||
|
||||
Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0,
|
||||
@@ -136,6 +320,11 @@ void Config::ReadValues() {
|
||||
Settings::values.language_index = qt_config->value("language_index", 1).toInt();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
Settings::values.use_accurate_gpu_emulation =
|
||||
qt_config->value("use_accurate_gpu_emulation", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Miscellaneous");
|
||||
Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
|
||||
Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool();
|
||||
@@ -144,7 +333,6 @@ void Config::ReadValues() {
|
||||
qt_config->beginGroup("Debugging");
|
||||
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
|
||||
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
|
||||
Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("WebService");
|
||||
@@ -220,44 +408,143 @@ void Config::ReadValues() {
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
void Config::SavePerGameSettings(const Settings::PerGameValues& values) {
|
||||
qt_config->beginGroup("Controls");
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
|
||||
QString::fromStdString(Settings::values.buttons[i]));
|
||||
QString::fromStdString(values.buttons[i]));
|
||||
}
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
|
||||
QString::fromStdString(Settings::values.analogs[i]));
|
||||
QString::fromStdString(values.analogs[i]));
|
||||
}
|
||||
qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
|
||||
qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device));
|
||||
qt_config->setValue("motion_device", QString::fromStdString(values.motion_device));
|
||||
qt_config->setValue("touch_device", QString::fromStdString(values.touch_device));
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit);
|
||||
qt_config->setValue("use_multi_core", Settings::values.use_multi_core);
|
||||
qt_config->beginGroup("System");
|
||||
qt_config->setValue("use_docked_mode", values.use_docked_mode);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
|
||||
qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
|
||||
qt_config->setValue("frame_limit", Settings::values.frame_limit);
|
||||
qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
|
||||
qt_config->setValue("resolution_factor", (double)values.resolution_factor);
|
||||
qt_config->setValue("use_frame_limit", values.use_frame_limit);
|
||||
qt_config->setValue("frame_limit", values.frame_limit);
|
||||
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
qt_config->setValue("bg_red", (double)Settings::values.bg_red);
|
||||
qt_config->setValue("bg_green", (double)Settings::values.bg_green);
|
||||
qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
|
||||
qt_config->setValue("bg_red", (double)values.bg_red);
|
||||
qt_config->setValue("bg_green", (double)values.bg_green);
|
||||
qt_config->setValue("bg_blue", (double)values.bg_blue);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Audio");
|
||||
qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id));
|
||||
qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching);
|
||||
qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id));
|
||||
qt_config->setValue("volume", Settings::values.volume);
|
||||
qt_config->setValue("output_engine", QString::fromStdString(values.sink_id));
|
||||
qt_config->setValue("enable_audio_stretching", values.enable_audio_stretching);
|
||||
qt_config->setValue("output_device", QString::fromStdString(values.audio_device_id));
|
||||
qt_config->setValue("volume", values.volume);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Debugging");
|
||||
qt_config->setValue("program_args", QString::fromStdString(values.program_args));
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Add Ons");
|
||||
qt_config->beginWriteArray("Disabled", values.disabled_patches.size());
|
||||
for (std::size_t i = 0; i < values.disabled_patches.size(); ++i) {
|
||||
qt_config->setArrayIndex(i);
|
||||
qt_config->setValue("name", QString::fromStdString(values.disabled_patches[i]));
|
||||
}
|
||||
qt_config->endArray();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SavePerGameSettingsDelta(const PerGameValuesChange& values) {
|
||||
qt_config->beginGroup("Controls");
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]) + "_changed",
|
||||
values.buttons[i]);
|
||||
}
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]) + "_changed",
|
||||
values.analogs[i]);
|
||||
}
|
||||
qt_config->setValue("motion_device_changed", values.motion_device);
|
||||
qt_config->setValue("touch_device_changed", values.touch_device);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
qt_config->setValue("use_docked_mode_changed", values.use_docked_mode);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
qt_config->setValue("resolution_factor_changed", values.resolution_factor);
|
||||
qt_config->setValue("use_frame_limit_changed", values.use_frame_limit);
|
||||
qt_config->setValue("frame_limit_changed", values.frame_limit);
|
||||
|
||||
qt_config->setValue("bg_red_changed", values.bg_red);
|
||||
qt_config->setValue("bg_green_changed", values.bg_green);
|
||||
qt_config->setValue("bg_blue_changed", values.bg_blue);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Audio");
|
||||
qt_config->setValue("output_engine_changed", values.sink_id);
|
||||
qt_config->setValue("enable_audio_stretching_changed", values.enable_audio_stretching);
|
||||
qt_config->setValue("output_device_changed", values.audio_device_id);
|
||||
qt_config->setValue("volume_changed", values.volume);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Debugging");
|
||||
qt_config->setValue("program_args_changed", values.program_args);
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
SavePerGameSettings(Settings::values.default_game);
|
||||
|
||||
Settings::PerGameValues values;
|
||||
UpdateCurrentGame(Settings::values.CurrentTitleID(), values);
|
||||
|
||||
const auto size = qt_config->beginReadArray("Per Game Settings");
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
qt_config->setArrayIndex(i);
|
||||
const auto read_title_id = qt_config->value("title_id", 0).toULongLong();
|
||||
if (update_values.find(read_title_id) == update_values.end()) {
|
||||
ReadPerGameSettings(values);
|
||||
|
||||
PerGameValuesChange change{};
|
||||
ReadPerGameSettingsDelta(change);
|
||||
|
||||
update_values.emplace(read_title_id, values);
|
||||
update_values_delta.emplace(read_title_id, change);
|
||||
}
|
||||
}
|
||||
|
||||
qt_config->endArray();
|
||||
|
||||
qt_config->beginWriteArray("Per Game Settings", update_values.size());
|
||||
|
||||
int i = 0;
|
||||
for (const auto& kv : update_values) {
|
||||
qt_config->setArrayIndex(i++);
|
||||
qt_config->setValue("title_id", kv.first);
|
||||
SavePerGameSettings(kv.second);
|
||||
|
||||
const auto iter = update_values_delta.find(kv.first);
|
||||
if (iter != update_values_delta.end()) {
|
||||
SavePerGameSettingsDelta(iter->second);
|
||||
} else {
|
||||
LOG_WARNING(
|
||||
Config,
|
||||
"Missing values delta for title_id={:016X}! Falling back to default-derived delta.",
|
||||
kv.first);
|
||||
SavePerGameSettingsDelta(
|
||||
CalculateValuesDelta(Settings::values.default_game, kv.second));
|
||||
}
|
||||
}
|
||||
|
||||
qt_config->endArray();
|
||||
|
||||
qt_config->beginGroup("Data Storage");
|
||||
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
|
||||
qt_config->setValue("nand_directory",
|
||||
@@ -266,14 +553,22 @@ void Config::SaveValues() {
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit);
|
||||
qt_config->setValue("use_multi_core", Settings::values.use_multi_core);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
|
||||
qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
|
||||
qt_config->setValue("current_user", Settings::values.current_user);
|
||||
|
||||
qt_config->setValue("language_index", Settings::values.language_index);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Renderer");
|
||||
qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Miscellaneous");
|
||||
qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
|
||||
qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys);
|
||||
@@ -282,7 +577,6 @@ void Config::SaveValues() {
|
||||
qt_config->beginGroup("Debugging");
|
||||
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
|
||||
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
|
||||
qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("WebService");
|
||||
@@ -347,3 +641,30 @@ void Config::Reload() {
|
||||
void Config::Save() {
|
||||
SaveValues();
|
||||
}
|
||||
|
||||
PerGameValuesChange Config::GetPerGameSettingsDelta(u64 title_id) const {
|
||||
if (update_values_delta.find(title_id) != update_values_delta.end())
|
||||
return update_values_delta.at(title_id);
|
||||
|
||||
const auto size = qt_config->beginReadArray("Per Game Settings");
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
qt_config->setArrayIndex(i);
|
||||
const auto read_title_id = qt_config->value("title_id", 0).toULongLong();
|
||||
if (read_title_id != title_id)
|
||||
continue;
|
||||
|
||||
PerGameValuesChange changes{};
|
||||
ReadPerGameSettingsDelta(changes);
|
||||
qt_config->endArray();
|
||||
return changes;
|
||||
}
|
||||
|
||||
qt_config->endArray();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Config::SetPerGameSettingsDelta(u64 title_id, PerGameValuesChange change) {
|
||||
update_values_delta.insert_or_assign(title_id, change);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,42 @@
|
||||
|
||||
class QSettings;
|
||||
|
||||
struct PerGameValuesChange {
|
||||
// Controls
|
||||
std::array<bool, Settings::NativeButton::NumButtons> buttons;
|
||||
std::array<bool, Settings::NativeAnalog::NumAnalogs> analogs;
|
||||
bool motion_device;
|
||||
bool touch_device;
|
||||
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
|
||||
// Renderer
|
||||
bool resolution_factor;
|
||||
bool use_frame_limit;
|
||||
bool frame_limit;
|
||||
|
||||
bool bg_red;
|
||||
bool bg_green;
|
||||
bool bg_blue;
|
||||
|
||||
// Audio
|
||||
bool sink_id;
|
||||
bool enable_audio_stretching;
|
||||
bool audio_device_id;
|
||||
bool volume;
|
||||
|
||||
// Debugging
|
||||
bool program_args;
|
||||
};
|
||||
|
||||
PerGameValuesChange CalculateValuesDelta(const Settings::PerGameValues& base,
|
||||
const Settings::PerGameValues& changed);
|
||||
|
||||
Settings::PerGameValues ApplyValuesDelta(const Settings::PerGameValues& base,
|
||||
const Settings::PerGameValues& changed,
|
||||
const PerGameValuesChange& changes);
|
||||
|
||||
class Config {
|
||||
public:
|
||||
Config();
|
||||
@@ -19,6 +55,8 @@ public:
|
||||
|
||||
void Reload();
|
||||
void Save();
|
||||
PerGameValuesChange GetPerGameSettingsDelta(u64 title_id) const;
|
||||
void SetPerGameSettingsDelta(u64 title_id, PerGameValuesChange change);
|
||||
|
||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||
@@ -27,6 +65,15 @@ private:
|
||||
void ReadValues();
|
||||
void SaveValues();
|
||||
|
||||
void ReadPerGameSettings(Settings::PerGameValues& values) const;
|
||||
void ReadPerGameSettingsDelta(PerGameValuesChange& values) const;
|
||||
void SavePerGameSettings(const Settings::PerGameValues& values);
|
||||
void SavePerGameSettingsDelta(const PerGameValuesChange& values);
|
||||
|
||||
bool UpdateCurrentGame(u64 title_id, Settings::PerGameValues& values);
|
||||
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
std::string qt_config_loc;
|
||||
std::map<u64, Settings::PerGameValues> update_values;
|
||||
std::map<u64, PerGameValuesChange> update_values_delta;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_audio.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_audio.h"
|
||||
|
||||
ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
@@ -23,6 +24,10 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
|
||||
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
||||
&ConfigureAudio::setVolumeIndicatorText);
|
||||
connect(ui->volume_slider, &QSlider::valueChanged, this, [this]() {
|
||||
if (!ui->volume_checkbox->isHidden())
|
||||
ui->volume_checkbox->setChecked(true);
|
||||
});
|
||||
|
||||
this->setConfiguration();
|
||||
connect(ui->output_sink_combo_box,
|
||||
@@ -35,6 +40,31 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
|
||||
ConfigureAudio::~ConfigureAudio() = default;
|
||||
|
||||
void ConfigureAudio::setPerGame(bool per_game) {
|
||||
ui->toggle_audio_stretching->setTristate(per_game);
|
||||
ui->output_sink_checkbox->setHidden(!per_game);
|
||||
ui->audio_device_checkbox->setHidden(!per_game);
|
||||
ui->volume_checkbox->setHidden(!per_game);
|
||||
}
|
||||
|
||||
void ConfigureAudio::loadValuesChange(const PerGameValuesChange& change) {
|
||||
ui->toggle_audio_stretching->setCheckState(
|
||||
change.enable_audio_stretching
|
||||
? (Settings::values->enable_audio_stretching ? Qt::Checked : Qt::Unchecked)
|
||||
: Qt::PartiallyChecked);
|
||||
ui->output_sink_checkbox->setChecked(change.sink_id);
|
||||
ui->audio_device_checkbox->setChecked(change.audio_device_id);
|
||||
ui->volume_checkbox->setChecked(change.volume);
|
||||
}
|
||||
|
||||
void ConfigureAudio::mergeValuesChange(PerGameValuesChange& change) {
|
||||
change.enable_audio_stretching =
|
||||
ui->toggle_audio_stretching->checkState() != Qt::PartiallyChecked;
|
||||
change.sink_id = ui->output_sink_checkbox->isChecked();
|
||||
change.audio_device_id = ui->audio_device_checkbox->isChecked();
|
||||
change.volume = ui->volume_checkbox->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureAudio::setConfiguration() {
|
||||
setOutputSinkFromSinkID();
|
||||
|
||||
@@ -43,15 +73,15 @@ void ConfigureAudio::setConfiguration() {
|
||||
|
||||
setAudioDeviceFromDeviceID();
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
|
||||
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values->enable_audio_stretching);
|
||||
ui->volume_slider->setValue(Settings::values->volume * ui->volume_slider->maximum());
|
||||
setVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
}
|
||||
|
||||
void ConfigureAudio::setOutputSinkFromSinkID() {
|
||||
int new_sink_index = 0;
|
||||
|
||||
const QString sink_id = QString::fromStdString(Settings::values.sink_id);
|
||||
const QString sink_id = QString::fromStdString(Settings::values->sink_id);
|
||||
for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
|
||||
if (ui->output_sink_combo_box->itemText(index) == sink_id) {
|
||||
new_sink_index = index;
|
||||
@@ -65,7 +95,7 @@ void ConfigureAudio::setOutputSinkFromSinkID() {
|
||||
void ConfigureAudio::setAudioDeviceFromDeviceID() {
|
||||
int new_device_index = -1;
|
||||
|
||||
const QString device_id = QString::fromStdString(Settings::values.audio_device_id);
|
||||
const QString device_id = QString::fromStdString(Settings::values->audio_device_id);
|
||||
for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
|
||||
if (ui->audio_device_combo_box->itemText(index) == device_id) {
|
||||
new_device_index = index;
|
||||
@@ -81,14 +111,17 @@ void ConfigureAudio::setVolumeIndicatorText(int percentage) {
|
||||
}
|
||||
|
||||
void ConfigureAudio::applyConfiguration() {
|
||||
Settings::values.sink_id =
|
||||
Settings::values->sink_id =
|
||||
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
|
||||
Settings::values.audio_device_id =
|
||||
Settings::values->enable_audio_stretching =
|
||||
ui->toggle_audio_stretching->checkState() == Qt::PartiallyChecked
|
||||
? Settings::values.default_game.enable_audio_stretching
|
||||
: ui->toggle_audio_stretching->isChecked();
|
||||
Settings::values->audio_device_id =
|
||||
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.volume =
|
||||
Settings::values->volume =
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
struct PerGameValuesChange;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureAudio;
|
||||
}
|
||||
@@ -18,6 +20,10 @@ public:
|
||||
explicit ConfigureAudio(QWidget* parent = nullptr);
|
||||
~ConfigureAudio();
|
||||
|
||||
void setPerGame(bool per_game);
|
||||
void loadValuesChange(const PerGameValuesChange& change);
|
||||
void mergeValuesChange(PerGameValuesChange& change);
|
||||
|
||||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>188</width>
|
||||
<width>402</width>
|
||||
<height>246</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -29,18 +29,31 @@
|
||||
<item>
|
||||
<widget class="QComboBox" name="output_sink_combo_box"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="output_sink_checkbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_audio_stretching">
|
||||
<property name="toolTip">
|
||||
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable audio stretching</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_audio_stretching">
|
||||
<property name="toolTip">
|
||||
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable audio stretching</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
@@ -53,6 +66,19 @@
|
||||
<item>
|
||||
<widget class="QComboBox" name="audio_device_combo_box"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="audio_device_checkbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@@ -115,6 +141,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="volume_checkbox">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -133,6 +166,16 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Check the box next to output engine, audio device, or volume to override the global default setting with this one for this game only.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_debug.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_debug.h"
|
||||
#include "yuzu/debugger/console.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
@@ -18,14 +19,35 @@
|
||||
ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
|
||||
connect(ui->open_log_button, &QPushButton::pressed, []() {
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
connect(ui->homebrew_args_edit, &QLineEdit::textChanged, [this](const QString& str) {
|
||||
if (!ui->program_args_checkbox->isHidden())
|
||||
ui->program_args_checkbox->setChecked(true);
|
||||
});
|
||||
}
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() = default;
|
||||
|
||||
void ConfigureDebug::setPerGame(bool per_game) {
|
||||
ui->override_label->setHidden(!per_game);
|
||||
ui->program_args_checkbox->setHidden(!per_game);
|
||||
ui->groupBox_2->setHidden(per_game);
|
||||
ui->groupBox->setHidden(per_game);
|
||||
}
|
||||
|
||||
void ConfigureDebug::loadValuesChange(const PerGameValuesChange& change) {
|
||||
ui->program_args_checkbox->setChecked(change.program_args);
|
||||
}
|
||||
|
||||
void ConfigureDebug::mergeValuesChange(PerGameValuesChange& change) {
|
||||
change.program_args = ui->program_args_checkbox->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureDebug::setConfiguration() {
|
||||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
|
||||
@@ -33,7 +55,7 @@ void ConfigureDebug::setConfiguration() {
|
||||
ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values->program_args));
|
||||
}
|
||||
|
||||
void ConfigureDebug::applyConfiguration() {
|
||||
@@ -41,7 +63,7 @@ void ConfigureDebug::applyConfiguration() {
|
||||
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
|
||||
Settings::values->program_args = ui->homebrew_args_edit->text().toStdString();
|
||||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
struct PerGameValuesChange;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureDebug;
|
||||
}
|
||||
@@ -20,9 +22,12 @@ public:
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
void setPerGame(bool per_game);
|
||||
void loadValuesChange(const PerGameValuesChange& change);
|
||||
void mergeValuesChange(PerGameValuesChange& change);
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
||||
|
||||
@@ -124,6 +124,13 @@
|
||||
<item>
|
||||
<widget class="QLineEdit" name="homebrew_args_edit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="program_args_checkbox">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -142,6 +149,16 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="override_label">
|
||||
<property name="text">
|
||||
<string>Check the box next to the homebrew arguments string to override the global default setting with this one for this game only.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -12,6 +12,10 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry
|
||||
: QDialog(parent), ui(new Ui::ConfigureDialog) {
|
||||
ui->setupUi(this);
|
||||
ui->generalTab->PopulateHotkeyList(registry);
|
||||
ui->inputTab->setPerGame(false);
|
||||
ui->graphicsTab->setPerGame(false);
|
||||
ui->audioTab->setPerGame(false);
|
||||
ui->debugTab->setPerGame(false);
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,16 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
|
||||
InitializeRowComboBoxes();
|
||||
|
||||
this->setConfiguration();
|
||||
|
||||
// Force game list reload if any of the relevant settings are changed.
|
||||
connect(ui->show_unknown, &QCheckBox::stateChanged, this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
}
|
||||
|
||||
ConfigureGameList::~ConfigureGameList() = default;
|
||||
@@ -48,6 +58,10 @@ void ConfigureGameList::applyConfiguration() {
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureGameList::RequestGameListUpdate() {
|
||||
UISettings::values.is_game_list_reload_pending.exchange(true);
|
||||
}
|
||||
|
||||
void ConfigureGameList::setConfiguration() {
|
||||
ui->show_unknown->setChecked(UISettings::values.show_unknown);
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
|
||||
@@ -20,6 +20,9 @@ public:
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
public slots:
|
||||
void RequestGameListUpdate();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
|
||||
ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
|
||||
connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
|
||||
[]() { UISettings::values.is_game_list_reload_pending.exchange(true); });
|
||||
}
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
@@ -30,7 +33,7 @@ void ConfigureGeneral::setConfiguration() {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
|
||||
ui->use_docked_mode->setChecked(Settings::values->use_docked_mode);
|
||||
ui->enable_nfc->setChecked(Settings::values.enable_nfc);
|
||||
}
|
||||
|
||||
@@ -45,6 +48,6 @@ void ConfigureGeneral::applyConfiguration() {
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
|
||||
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
|
||||
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
|
||||
Settings::values->use_docked_mode = ui->use_docked_mode->isChecked();
|
||||
Settings::values.enable_nfc = ui->enable_nfc->isChecked();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_graphics.h"
|
||||
|
||||
namespace {
|
||||
@@ -55,7 +56,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
|
||||
ui->frame_limit->setEnabled(Settings::values.use_frame_limit);
|
||||
ui->frame_limit->setEnabled(Settings::values->use_frame_limit);
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit,
|
||||
&QSpinBox::setEnabled);
|
||||
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
|
||||
@@ -72,23 +73,51 @@ ConfigureGraphics::~ConfigureGraphics() = default;
|
||||
|
||||
void ConfigureGraphics::setConfiguration() {
|
||||
ui->resolution_factor_combobox->setCurrentIndex(
|
||||
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
|
||||
ui->frame_limit->setValue(Settings::values.frame_limit);
|
||||
static_cast<int>(FromResolutionFactor(Settings::values->resolution_factor)));
|
||||
ui->toggle_frame_limit->setChecked(Settings::values->use_frame_limit);
|
||||
ui->frame_limit->setValue(Settings::values->frame_limit);
|
||||
ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
|
||||
bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue);
|
||||
bg_color = QColor::fromRgbF(Settings::values->bg_red, Settings::values->bg_green,
|
||||
Settings::values->bg_blue);
|
||||
ui->bg_button->setStyleSheet(
|
||||
QString("QPushButton { background-color: %1 }").arg(bg_color.name()));
|
||||
}
|
||||
|
||||
void ConfigureGraphics::applyConfiguration() {
|
||||
Settings::values.resolution_factor =
|
||||
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
|
||||
Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
|
||||
Settings::values.frame_limit = ui->frame_limit->value();
|
||||
Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
|
||||
Settings::values.bg_red = static_cast<float>(bg_color.redF());
|
||||
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
|
||||
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
|
||||
void ConfigureGraphics::setPerGame(bool per_game) {
|
||||
ui->bg_checkbox->setHidden(!per_game);
|
||||
ui->resolution_factor_checkbox->setHidden(!per_game);
|
||||
ui->use_accurate_gpu_emulation->setHidden(per_game);
|
||||
ui->override_label->setHidden(!per_game);
|
||||
ui->toggle_frame_limit->setTristate(per_game);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::loadValuesChange(const PerGameValuesChange& change) {
|
||||
ui->bg_checkbox->setChecked(change.bg_red || change.bg_green || change.bg_blue);
|
||||
ui->toggle_frame_limit->setCheckState(
|
||||
change.use_frame_limit ? (Settings::values->use_frame_limit ? Qt::Checked : Qt::Unchecked)
|
||||
: Qt::PartiallyChecked);
|
||||
ui->frame_limit->setEnabled(change.use_frame_limit || Settings::values->use_frame_limit);
|
||||
ui->resolution_factor_checkbox->setChecked(change.resolution_factor);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::mergeValuesChange(PerGameValuesChange& change) {
|
||||
change.bg_red = ui->bg_checkbox->isChecked();
|
||||
change.bg_green = ui->bg_checkbox->isChecked();
|
||||
change.bg_blue = ui->bg_checkbox->isChecked();
|
||||
change.use_frame_limit = ui->toggle_frame_limit->checkState() != Qt::PartiallyChecked;
|
||||
change.frame_limit = ui->toggle_frame_limit->checkState() != Qt::PartiallyChecked;
|
||||
change.resolution_factor = ui->resolution_factor_checkbox->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureGraphics::applyConfiguration() {
|
||||
Settings::values->resolution_factor =
|
||||
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
|
||||
Settings::values->use_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked
|
||||
? Settings::values.default_game.use_frame_limit
|
||||
: ui->toggle_frame_limit->isChecked();
|
||||
Settings::values->frame_limit = ui->frame_limit->value();
|
||||
Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
|
||||
Settings::values->bg_red = static_cast<float>(bg_color.redF());
|
||||
Settings::values->bg_green = static_cast<float>(bg_color.greenF());
|
||||
Settings::values->bg_blue = static_cast<float>(bg_color.blueF());
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
struct PerGameValuesChange;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureGraphics;
|
||||
}
|
||||
@@ -20,6 +22,10 @@ public:
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
void setPerGame(bool per_game);
|
||||
void loadValuesChange(const PerGameValuesChange& change);
|
||||
void mergeValuesChange(PerGameValuesChange& change);
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<width>402</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -23,31 +23,31 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_frame_limit">
|
||||
<property name="text">
|
||||
<string>Limit Speed Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="frame_limit">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_frame_limit">
|
||||
<property name="text">
|
||||
<string>Limit Speed Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="frame_limit">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_accurate_gpu_emulation">
|
||||
@@ -61,7 +61,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Internal Resolution:(Currently does nothing.)</string>
|
||||
<string>Internal Resolution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -94,29 +94,55 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="resolution_factor_checkbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="bg_label">
|
||||
<property name="text">
|
||||
<string>Background Color:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bg_button">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="bg_checkbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="bg_label">
|
||||
<property name="text">
|
||||
<string>Background Color:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bg_button">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -135,6 +161,16 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="override_label">
|
||||
<property name="text">
|
||||
<string>Check the box next to internal resolution or background color to override the global default setting with this one for this game only.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include "common/assert.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/main.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
@@ -215,6 +216,24 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
}
|
||||
});
|
||||
|
||||
buttons_delta = {
|
||||
ui->checkboxA, ui->checkboxB, ui->checkboxX,
|
||||
ui->checkboxY, ui->checkboxLStickMod, ui->checkboxRStickMod,
|
||||
ui->checkboxL, ui->checkboxR, ui->checkboxZL,
|
||||
ui->checkboxZR, ui->checkboxPlus, ui->checkboxMinus,
|
||||
ui->checkboxDpadLeft, ui->checkboxDpadUp, ui->checkboxDpadRight,
|
||||
ui->checkboxDpadDown, ui->checkboxLStickLeft, ui->checkboxLStickUp,
|
||||
ui->checkboxLStickRight, ui->checkboxLStickDown, ui->checkboxRStickLeft,
|
||||
ui->checkboxRStickUp, ui->checkboxRStickRight, ui->checkboxRStickDown,
|
||||
ui->checkboxSL, ui->checkboxSR, ui->checkboxHome,
|
||||
ui->checkboxScreenshot,
|
||||
};
|
||||
|
||||
analogs_delta = {
|
||||
ui->checkboxLStickPressed,
|
||||
ui->checkboxRStickPressed,
|
||||
};
|
||||
|
||||
this->loadConfiguration();
|
||||
|
||||
// TODO(wwylele): enable this when we actually emulate it
|
||||
@@ -222,17 +241,52 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
}
|
||||
|
||||
void ConfigureInput::applyConfiguration() {
|
||||
std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(),
|
||||
Settings::PerGameValues temp{};
|
||||
|
||||
std::transform(buttons_param.begin(), buttons_param.end(), temp.buttons.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
|
||||
std::transform(analogs_param.begin(), analogs_param.end(), temp.analogs.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
|
||||
if (std::any_of(buttons_delta.begin(), buttons_delta.end(),
|
||||
[](const QCheckBox* box) { return box->isHidden(); })) {
|
||||
Settings::values->buttons = temp.buttons;
|
||||
Settings::values->analogs = temp.analogs;
|
||||
return;
|
||||
}
|
||||
|
||||
PerGameValuesChange changes{};
|
||||
|
||||
std::transform(buttons_delta.begin(), buttons_delta.end(), changes.buttons.begin(),
|
||||
[](const QCheckBox* box) { return box->isChecked(); });
|
||||
std::transform(analogs_delta.begin(), analogs_delta.end(), changes.analogs.begin(),
|
||||
[](const QCheckBox* box) { return box->isChecked(); });
|
||||
|
||||
temp = ApplyValuesDelta(Settings::values.default_game, temp, changes);
|
||||
Settings::values->buttons = temp.buttons;
|
||||
Settings::values->analogs = temp.analogs;
|
||||
}
|
||||
|
||||
void ConfigureInput::setPerGame(bool show) {
|
||||
for (auto* button : buttons_delta)
|
||||
button->setHidden(!show);
|
||||
for (auto* analog : analogs_delta)
|
||||
analog->setHidden(!show);
|
||||
ui->override_label->setHidden(!show);
|
||||
}
|
||||
|
||||
void ConfigureInput::mergeValuesChange(PerGameValuesChange& changes) {
|
||||
std::transform(buttons_delta.begin(), buttons_delta.end(), changes.buttons.begin(),
|
||||
[](const QCheckBox* box) { return box->isChecked(); });
|
||||
std::transform(analogs_delta.begin(), analogs_delta.end(), changes.analogs.begin(),
|
||||
[](const QCheckBox* box) { return box->isChecked(); });
|
||||
}
|
||||
|
||||
void ConfigureInput::loadConfiguration() {
|
||||
std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(),
|
||||
std::transform(Settings::values->buttons.begin(), Settings::values->buttons.end(),
|
||||
buttons_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
|
||||
std::transform(Settings::values->analogs.begin(), Settings::values->analogs.end(),
|
||||
analogs_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
updateButtonLabels();
|
||||
@@ -285,12 +339,28 @@ void ConfigureInput::updateButtonLabels() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::loadValuesChange(const PerGameValuesChange& change) {
|
||||
for (std::size_t i = 0; i < Settings::NativeButton::NumButtons; ++i)
|
||||
buttons_delta[i]->setChecked(change.buttons[i]);
|
||||
|
||||
for (std::size_t i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i)
|
||||
analogs_delta[i]->setChecked(change.analogs[i]);
|
||||
}
|
||||
|
||||
void ConfigureInput::handleClick(QPushButton* button,
|
||||
std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type) {
|
||||
button->setText(tr("[press key]"));
|
||||
button->setFocus();
|
||||
|
||||
const auto iter = std::find(button_map.begin(), button_map.end(), button);
|
||||
ASSERT(iter != button_map.end());
|
||||
const auto index = std::distance(button_map.begin(), iter);
|
||||
ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
|
||||
const auto checkbox = buttons_delta[index];
|
||||
if (!checkbox->isHidden())
|
||||
checkbox->setChecked(true);
|
||||
|
||||
input_setter = new_input_setter;
|
||||
|
||||
device_pollers = InputCommon::Polling::GetPollers(type);
|
||||
|
||||
@@ -34,6 +34,10 @@ public:
|
||||
/// Save all button configurations to settings file
|
||||
void applyConfiguration();
|
||||
|
||||
void setPerGame(bool per_game);
|
||||
void loadValuesChange(const PerGameValuesChange& change);
|
||||
void mergeValuesChange(PerGameValuesChange& change);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureInput> ui;
|
||||
|
||||
@@ -46,6 +50,9 @@ private:
|
||||
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
|
||||
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
|
||||
|
||||
std::array<QCheckBox*, Settings::NativeButton::NumButtons> buttons_delta;
|
||||
std::array<QCheckBox*, Settings::NativeAnalog::NumAnalogs> analogs_delta;
|
||||
|
||||
static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
|
||||
|
||||
/// Each button input is represented by a QPushButton.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
125
src/yuzu/configuration/configure_per_game.ui
Normal file
125
src/yuzu/configuration/configure_per_game.ui
Normal file
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigurePerGameDialog</class>
|
||||
<widget class="QDialog" name="ConfigurePerGameDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>461</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>yuzu Game Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="ConfigurePerGameGeneral" name="generalTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureInput" name="inputTab">
|
||||
<attribute name="title">
|
||||
<string>Input</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGraphics" name="graphicsTab">
|
||||
<attribute name="title">
|
||||
<string>Graphics</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureAudio" name="audioTab">
|
||||
<attribute name="title">
|
||||
<string>Audio</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureDebug" name="debugTab">
|
||||
<attribute name="title">
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ConfigurePerGameGeneral</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_per_general.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureInput</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_input.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGraphics</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_graphics.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureAudio</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_audio.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureDebug</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_debug.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigurePerGameDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>220</x>
|
||||
<y>380</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>220</x>
|
||||
<y>200</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfigurePerGameDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>220</x>
|
||||
<y>380</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>220</x>
|
||||
<y>200</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
46
src/yuzu/configuration/configure_per_game_dialog.cpp
Normal file
46
src/yuzu/configuration/configure_per_game_dialog.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_per_game.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_per_game_dialog.h"
|
||||
|
||||
ConfigurePerGameDialog::ConfigurePerGameDialog(QWidget* parent, Loader::AppLoader& loader,
|
||||
const PerGameValuesChange& change)
|
||||
: QDialog(parent), ui(new Ui::ConfigurePerGameDialog) {
|
||||
ui->setupUi(this);
|
||||
ui->generalTab->loadGameData(loader);
|
||||
ui->generalTab->loadValuesChange(change);
|
||||
ui->inputTab->setPerGame(true);
|
||||
ui->inputTab->loadValuesChange(change);
|
||||
ui->graphicsTab->setPerGame(true);
|
||||
ui->graphicsTab->loadValuesChange(change);
|
||||
ui->audioTab->setPerGame(true);
|
||||
ui->audioTab->loadValuesChange(change);
|
||||
ui->debugTab->setPerGame(true);
|
||||
ui->debugTab->loadValuesChange(change);
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
ConfigurePerGameDialog::~ConfigurePerGameDialog() = default;
|
||||
|
||||
void ConfigurePerGameDialog::setConfiguration() {}
|
||||
|
||||
PerGameValuesChange ConfigurePerGameDialog::applyConfiguration() {
|
||||
PerGameValuesChange out{};
|
||||
ui->generalTab->applyConfiguration();
|
||||
ui->generalTab->mergeValuesChange(out);
|
||||
ui->inputTab->applyConfiguration();
|
||||
ui->inputTab->mergeValuesChange(out);
|
||||
ui->graphicsTab->applyConfiguration();
|
||||
ui->graphicsTab->mergeValuesChange(out);
|
||||
ui->audioTab->applyConfiguration();
|
||||
ui->audioTab->mergeValuesChange(out);
|
||||
ui->debugTab->applyConfiguration();
|
||||
ui->debugTab->mergeValuesChange(out);
|
||||
Settings::Apply();
|
||||
return out;
|
||||
}
|
||||
32
src/yuzu/configuration/configure_per_game_dialog.h
Normal file
32
src/yuzu/configuration/configure_per_game_dialog.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class ConfigurePerGameDialog;
|
||||
}
|
||||
|
||||
class ConfigurePerGameDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigurePerGameDialog(QWidget* parent, Loader::AppLoader& file,
|
||||
const PerGameValuesChange& change);
|
||||
~ConfigurePerGameDialog();
|
||||
|
||||
PerGameValuesChange applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
std::unique_ptr<Ui::ConfigurePerGameDialog> ui;
|
||||
};
|
||||
174
src/yuzu/configuration/configure_per_general.cpp
Normal file
174
src/yuzu/configuration/configure_per_general.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardItemModel>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QTreeView>
|
||||
#include "common/param_package.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/xts_archive.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "input_common/main.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input.h"
|
||||
#include "yuzu/configuration/configure_per_general.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
#include "yuzu/util/util.h"
|
||||
|
||||
ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()) {
|
||||
ui->setupUi(this);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
tree_view = new QTreeView;
|
||||
item_model = new QStandardItemModel(tree_view);
|
||||
tree_view->setModel(item_model);
|
||||
tree_view->setAlternatingRowColors(true);
|
||||
tree_view->setSelectionMode(QHeaderView::SingleSelection);
|
||||
tree_view->setSelectionBehavior(QHeaderView::SelectRows);
|
||||
tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
|
||||
tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
|
||||
tree_view->setSortingEnabled(true);
|
||||
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
|
||||
tree_view->setUniformRowHeights(true);
|
||||
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
|
||||
item_model->insertColumns(0, 2);
|
||||
item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
|
||||
item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
|
||||
|
||||
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
||||
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
||||
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(tree_view);
|
||||
|
||||
ui->scrollArea->setLayout(layout);
|
||||
|
||||
scene = new QGraphicsScene;
|
||||
ui->icon_view->setScene(scene);
|
||||
|
||||
connect(item_model, &QStandardItemModel::itemChanged, this,
|
||||
[]() { UISettings::values.is_game_list_reload_pending.exchange(true); });
|
||||
}
|
||||
|
||||
ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default;
|
||||
|
||||
void ConfigurePerGameGeneral::applyConfiguration() {
|
||||
std::vector<std::string> disabled_add_ons;
|
||||
|
||||
for (const auto& item : list_items) {
|
||||
const auto disabled = item.front()->checkState() == Qt::Unchecked;
|
||||
if (disabled)
|
||||
disabled_add_ons.push_back(item.front()->text().toStdString());
|
||||
}
|
||||
|
||||
Settings::values->disabled_patches = disabled_add_ons;
|
||||
|
||||
Settings::values->use_docked_mode = ui->use_docked_mode->checkState() == Qt::PartiallyChecked
|
||||
? Settings::values.default_game.use_docked_mode
|
||||
: ui->use_docked_mode->isChecked();
|
||||
}
|
||||
|
||||
void ConfigurePerGameGeneral::loadGameData(Loader::AppLoader& loader) {
|
||||
u64 program_id{};
|
||||
if (loader.ReadProgramId(program_id) == Loader::ResultStatus::Success) {
|
||||
ui->display_title_id->setText(QStringLiteral("%1").arg(program_id, 16, 16, QChar{'0'}));
|
||||
|
||||
FileSys::PatchManager pm{program_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
if (control.first != nullptr) {
|
||||
ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
|
||||
ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
|
||||
ui->display_developer->setText(
|
||||
QString::fromStdString(control.first->GetDeveloperName()));
|
||||
} else {
|
||||
std::string title;
|
||||
if (loader.ReadTitle(title) == Loader::ResultStatus::Success)
|
||||
ui->display_name->setText(QString::fromStdString(title));
|
||||
|
||||
std::string developer;
|
||||
if (loader.ReadDeveloper(developer) == Loader::ResultStatus::Success)
|
||||
ui->display_developer->setText(QString::fromStdString(developer));
|
||||
|
||||
ui->display_version->setText("1.0.0");
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
scene->clear();
|
||||
|
||||
QPixmap map;
|
||||
const auto bytes = control.second->ReadAllBytes();
|
||||
map.loadFromData(bytes.data(), bytes.size());
|
||||
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
} else {
|
||||
std::vector<u8> bytes;
|
||||
if (loader.ReadIcon(bytes) == Loader::ResultStatus::Success) {
|
||||
scene->clear();
|
||||
|
||||
QPixmap map;
|
||||
map.loadFromData(bytes.data(), bytes.size());
|
||||
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
}
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
loader.ReadUpdateRaw(update_raw);
|
||||
|
||||
const auto& disabled = Settings::values[program_id].disabled_patches;
|
||||
|
||||
for (const auto& patch : pm.GetPatchVersionNames(update_raw)) {
|
||||
QStandardItem* first_item = new QStandardItem;
|
||||
first_item->setText(QString::fromStdString(patch.first));
|
||||
first_item->setCheckable(true);
|
||||
|
||||
const auto patch_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), patch.first) != disabled.end();
|
||||
|
||||
first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked);
|
||||
|
||||
list_items.push_back(QList<QStandardItem*>{
|
||||
first_item, new QStandardItem{QString::fromStdString(patch.second)}});
|
||||
item_model->appendRow(list_items.back());
|
||||
}
|
||||
|
||||
tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
|
||||
}
|
||||
|
||||
const auto file = loader.GetFile();
|
||||
|
||||
ui->display_filename->setText(QString::fromStdString(file->GetName()));
|
||||
|
||||
ui->display_format->setText(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType())));
|
||||
|
||||
QString valueText = ReadableByteSize(file->GetSize());
|
||||
ui->display_size->setText(valueText);
|
||||
}
|
||||
|
||||
void ConfigurePerGameGeneral::loadValuesChange(const PerGameValuesChange& change) {
|
||||
ui->use_docked_mode->setCheckState(
|
||||
change.use_docked_mode ? (Settings::values->use_docked_mode ? Qt::Checked : Qt::Unchecked)
|
||||
: Qt::PartiallyChecked);
|
||||
}
|
||||
|
||||
void ConfigurePerGameGeneral::mergeValuesChange(PerGameValuesChange& change) {
|
||||
change.use_docked_mode = ui->use_docked_mode->checkState() != Qt::PartiallyChecked;
|
||||
}
|
||||
52
src/yuzu/configuration/configure_per_general.h
Normal file
52
src/yuzu/configuration/configure_per_general.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QKeyEvent>
|
||||
#include <QList>
|
||||
#include <QWidget>
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "ui_configure_per_general.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
|
||||
class QGraphicsScene;
|
||||
class QStandardItem;
|
||||
class QStandardItemModel;
|
||||
class QTreeView;
|
||||
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class ConfigurePerGameGeneral;
|
||||
}
|
||||
|
||||
class ConfigurePerGameGeneral : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigurePerGameGeneral(QWidget* parent = nullptr);
|
||||
~ConfigurePerGameGeneral() override;
|
||||
|
||||
/// Save all button configurations to settings file
|
||||
void applyConfiguration();
|
||||
|
||||
void loadGameData(Loader::AppLoader& file);
|
||||
|
||||
void loadValuesChange(const PerGameValuesChange& change);
|
||||
void mergeValuesChange(PerGameValuesChange& change);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigurePerGameGeneral> ui;
|
||||
|
||||
QVBoxLayout* layout;
|
||||
QTreeView* tree_view;
|
||||
QStandardItemModel* item_model;
|
||||
QGraphicsScene* scene;
|
||||
|
||||
std::vector<QList<QStandardItem*>> list_items;
|
||||
};
|
||||
258
src/yuzu/configuration/configure_per_general.ui
Normal file
258
src/yuzu/configuration/configure_per_general.ui
Normal file
@@ -0,0 +1,258 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigurePerGameGeneral</class>
|
||||
<widget class="QWidget" name="ConfigurePerGameGeneral">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>540</width>
|
||||
<height>589</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>ConfigurePerGameGeneral</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="HorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GeneralGroupBox">
|
||||
<property name="title">
|
||||
<string>Info</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GeneralHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="6" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="display_filename">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="display_name">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Developer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="display_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="display_version">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="display_format">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="display_developer">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Title ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="display_title_id">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="5">
|
||||
<widget class="QGraphicsView" name="icon_view">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="interactive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="PerformanceGroupBox">
|
||||
<property name="title">
|
||||
<string>Add-Ons</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>490</width>
|
||||
<height>108</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="PerformanceVerticalLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>System</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_docked_mode">
|
||||
<property name="text">
|
||||
<string>Use Docked Mode</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <regex>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
@@ -12,11 +13,15 @@
|
||||
#include <QJsonObject>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTextDocument>
|
||||
#include <QThreadPool>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/game_list.h"
|
||||
@@ -25,6 +30,52 @@
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
|
||||
class HTMLDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& _option,
|
||||
const QModelIndex& index) const override {
|
||||
auto option = _option;
|
||||
initStyleOption(&option, index);
|
||||
|
||||
QStyle* style = option.widget ? option.widget->style() : QApplication::style();
|
||||
|
||||
QTextDocument document;
|
||||
document.setHtml(option.text);
|
||||
|
||||
/// Painting item without text
|
||||
option.text = QString();
|
||||
style->drawControl(QStyle::CE_ItemViewItem, &option, painter);
|
||||
|
||||
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||
|
||||
// Highlighting text if item is selected
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
ctx.palette.setColor(QPalette::Text,
|
||||
option.palette.color(QPalette::Active, QPalette::HighlightedText));
|
||||
} else {
|
||||
ctx.palette.setColor(QPalette::Text,
|
||||
option.palette.color(QPalette::Normal, QPalette::Text));
|
||||
}
|
||||
|
||||
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option);
|
||||
painter->save();
|
||||
painter->translate(textRect.topLeft());
|
||||
painter->setClipRect(textRect.translated(-textRect.topLeft()));
|
||||
document.documentLayout()->draw(painter, ctx);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem& _option, const QModelIndex& index) const override {
|
||||
auto option = _option;
|
||||
initStyleOption(&option, index);
|
||||
|
||||
QTextDocument document;
|
||||
document.setHtml(option.text);
|
||||
document.setTextWidth(option.rect.width());
|
||||
return QSize(document.idealWidth(), UISettings::values.icon_size);
|
||||
}
|
||||
};
|
||||
|
||||
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {}
|
||||
|
||||
// EventFilter in order to process systemkeys while editing the searchfield
|
||||
@@ -214,6 +265,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
|
||||
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
|
||||
tree_view->setUniformRowHeights(true);
|
||||
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
tree_view->setItemDelegate(new HTMLDelegate);
|
||||
|
||||
item_model->insertColumns(0, COLUMN_COUNT);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
@@ -326,6 +378,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
|
||||
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
|
||||
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
|
||||
context_menu.addSeparator();
|
||||
QAction* properties = context_menu.addAction(tr("Properties"));
|
||||
|
||||
open_save_location->setEnabled(program_id != 0);
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
@@ -339,6 +393,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
|
||||
connect(navigate_to_gamedb_entry, &QAction::triggered,
|
||||
[&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
|
||||
connect(properties, &QAction::triggered,
|
||||
[&]() { emit OpenGamePropertiesDialogRequested(path); });
|
||||
|
||||
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
|
||||
class GameListWorker;
|
||||
@@ -68,6 +69,7 @@ signals:
|
||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
|
||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
|
||||
void CopyTIDRequested(u64 program_id);
|
||||
void OpenGamePropertiesDialogRequested(const std::string& file);
|
||||
void NavigateToGamedbEntryRequested(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
if (row2.isEmpty())
|
||||
return row1;
|
||||
|
||||
return row1 + "\n " + row2;
|
||||
return row1 + "<br> " + row2;
|
||||
}
|
||||
|
||||
return GameListItem::data(role);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/game_list.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
@@ -58,6 +60,7 @@ QString FormatGameName(const std::string& physical_name) {
|
||||
|
||||
QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
Loader::AppLoader& loader, bool updatable = true) {
|
||||
const auto& disabled = Settings::values[patch_manager.GetTitleID()].disabled_patches;
|
||||
QString out;
|
||||
FileSys::VirtualFile update_raw;
|
||||
loader.ReadUpdateRaw(update_raw);
|
||||
@@ -67,10 +70,13 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto patch_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), kv.first) != disabled.end();
|
||||
const QString type = QString::fromStdString(kv.first);
|
||||
|
||||
if (kv.second.empty()) {
|
||||
out.append(QStringLiteral("%1\n").arg(type));
|
||||
out.append((patch_disabled ? QStringLiteral("<s>%1</s><br>") : QStringLiteral("%1<br>"))
|
||||
.arg(type));
|
||||
} else {
|
||||
auto ver = kv.second;
|
||||
|
||||
@@ -79,7 +85,9 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
ver = Loader::GetFileTypeString(loader.GetFileType());
|
||||
}
|
||||
|
||||
out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver)));
|
||||
out.append((patch_disabled ? QStringLiteral("<s>%1 (%2)</s><br>")
|
||||
: QStringLiteral("%1 (%2)<br>"))
|
||||
.arg(type, QString::fromStdString(ver)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_dialog.h"
|
||||
#include "yuzu/configuration/configure_per_game_dialog.h"
|
||||
#include "yuzu/configuration/configure_per_general.h"
|
||||
#include "yuzu/debugger/console.h"
|
||||
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
|
||||
#include "yuzu/debugger/graphics/graphics_surface.h"
|
||||
@@ -339,21 +341,21 @@ void GMainWindow::InitializeHotkeys() {
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
Settings::values.use_frame_limit = !Settings::values.use_frame_limit;
|
||||
Settings::values->use_frame_limit = !Settings::values->use_frame_limit;
|
||||
UpdateStatusBar();
|
||||
});
|
||||
constexpr u16 SPEED_LIMIT_STEP = 5;
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Increase Speed Limit", this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) {
|
||||
Settings::values.frame_limit += SPEED_LIMIT_STEP;
|
||||
if (Settings::values->frame_limit < 9999 - SPEED_LIMIT_STEP) {
|
||||
Settings::values->frame_limit += SPEED_LIMIT_STEP;
|
||||
UpdateStatusBar();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Decrease Speed Limit", this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
if (Settings::values.frame_limit > SPEED_LIMIT_STEP) {
|
||||
Settings::values.frame_limit -= SPEED_LIMIT_STEP;
|
||||
if (Settings::values->frame_limit > SPEED_LIMIT_STEP) {
|
||||
Settings::values->frame_limit -= SPEED_LIMIT_STEP;
|
||||
UpdateStatusBar();
|
||||
}
|
||||
});
|
||||
@@ -405,6 +407,8 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
||||
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
|
||||
&GMainWindow::OnGameListNavigateToGamedbEntry);
|
||||
connect(game_list, &GameList::OpenGamePropertiesDialogRequested, this,
|
||||
&GMainWindow::OnGameListOpenProperties);
|
||||
|
||||
connect(this, &GMainWindow::EmulationStarting, render_window,
|
||||
&GRenderWindow::OnEmulationStarting);
|
||||
@@ -453,6 +457,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||
|
||||
// Help
|
||||
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
|
||||
connect(ui.action_Rederive, &QAction::triggered, this,
|
||||
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
|
||||
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
|
||||
@@ -960,6 +965,30 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory));
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListOpenProperties(const std::string& file) {
|
||||
u64 title_id{};
|
||||
auto loader = Loader::GetLoader(Core::GetGameFileFromPath(vfs, file));
|
||||
if (loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||
QMessageBox::information(
|
||||
this, tr("Per Game Configuration"),
|
||||
tr("Per Game Configuration is not supported on games that do not have a title ID. "
|
||||
"Please use a format that includes the title ID, such as NSP or XCI."));
|
||||
return;
|
||||
}
|
||||
|
||||
Settings::values.SetCurrentTitleID(title_id);
|
||||
ConfigurePerGameDialog dialog{this, *loader, config->GetPerGameSettingsDelta(title_id)};
|
||||
auto result = dialog.exec();
|
||||
if (result != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
config->SetPerGameSettingsDelta(title_id, dialog.applyConfiguration());
|
||||
config->Save();
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload)
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuLoadFile() {
|
||||
const QString extensions =
|
||||
QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
|
||||
@@ -1317,19 +1346,43 @@ void GMainWindow::ToggleWindowMode() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
ConfigureDialog configureDialog(this, hotkey_registry);
|
||||
const auto old_title_id = Settings::values.CurrentTitleID();
|
||||
auto old_theme = UISettings::values.theme;
|
||||
const bool old_discord_presence = UISettings::values.enable_discord_presence;
|
||||
auto result = configureDialog.exec();
|
||||
|
||||
int result = QDialog::Rejected;
|
||||
|
||||
if (emu_thread == nullptr) {
|
||||
Settings::values.SetCurrentTitleID(Settings::DEFAULT_PER_GAME);
|
||||
ConfigureDialog configureDialog(this, hotkey_registry);
|
||||
|
||||
result = configureDialog.exec();
|
||||
if (result == QDialog::Accepted)
|
||||
configureDialog.applyConfiguration();
|
||||
} else {
|
||||
ConfigurePerGameDialog configureDialog(this, Core::System::GetInstance().GetAppLoader(),
|
||||
config->GetPerGameSettingsDelta(old_title_id));
|
||||
|
||||
result = configureDialog.exec();
|
||||
if (result == QDialog::Accepted)
|
||||
config->SetPerGameSettingsDelta(old_title_id, configureDialog.applyConfiguration());
|
||||
}
|
||||
|
||||
if (result == QDialog::Accepted) {
|
||||
configureDialog.applyConfiguration();
|
||||
if (UISettings::values.theme != old_theme)
|
||||
UpdateUITheme();
|
||||
if (UISettings::values.enable_discord_presence != old_discord_presence)
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence);
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload) {
|
||||
game_list->PopulateAsync(UISettings::values.gamedir,
|
||||
UISettings::values.gamedir_deepscan);
|
||||
}
|
||||
config->Save();
|
||||
}
|
||||
|
||||
if (emu_thread == nullptr)
|
||||
Settings::values.SetCurrentTitleID(old_title_id);
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadAmiibo() {
|
||||
@@ -1353,6 +1406,11 @@ void GMainWindow::OnLoadAmiibo() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnOpenYuzuFolder() {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
|
||||
}
|
||||
|
||||
void GMainWindow::OnAbout() {
|
||||
AboutDialog aboutDialog(this);
|
||||
aboutDialog.exec();
|
||||
@@ -1375,10 +1433,10 @@ void GMainWindow::UpdateStatusBar() {
|
||||
|
||||
auto results = Core::System::GetInstance().GetAndResetPerfStats();
|
||||
|
||||
if (Settings::values.use_frame_limit) {
|
||||
if (Settings::values->use_frame_limit) {
|
||||
emu_speed_label->setText(tr("Speed: %1% / %2%")
|
||||
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
|
||||
.arg(Settings::values.frame_limit));
|
||||
.arg(Settings::values->frame_limit));
|
||||
} else {
|
||||
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "ui_main.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/hotkeys.h"
|
||||
@@ -157,6 +158,7 @@ private slots:
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
void OnGameListOpenProperties(const std::string& file);
|
||||
void OnMenuLoadFile();
|
||||
void OnMenuLoadFolder();
|
||||
void OnMenuInstallToNAND();
|
||||
@@ -167,6 +169,7 @@ private slots:
|
||||
void OnMenuRecentFile();
|
||||
void OnConfigure();
|
||||
void OnLoadAmiibo();
|
||||
void OnOpenYuzuFolder();
|
||||
void OnAbout();
|
||||
void OnToggleFilterBar();
|
||||
void OnDisplayTitleBars(bool);
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="action_Open_yuzu_Folder"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_About"/>
|
||||
</widget>
|
||||
@@ -277,7 +278,12 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<action name="action_Open_yuzu_Folder">
|
||||
<property name="text">
|
||||
<string>Open yuzu Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
@@ -62,6 +63,7 @@ struct Values {
|
||||
uint32_t icon_size;
|
||||
uint8_t row_1_text_id;
|
||||
uint8_t row_2_text_id;
|
||||
std::atomic_bool is_game_list_reload_pending{false};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <SDL.h>
|
||||
#include <inih/cpp/INIReader.h>
|
||||
#include "common/file_util.h"
|
||||
@@ -65,54 +66,97 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
|
||||
},
|
||||
}};
|
||||
|
||||
void Config::ReadValues() {
|
||||
void Config::ReadValuesForTitleID(Settings::PerGameValues& values, const std::string& name) {
|
||||
// Controls
|
||||
auto group = fmt::format("{}Controls", name);
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.buttons[i] =
|
||||
sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
|
||||
if (Settings::values.buttons[i].empty())
|
||||
Settings::values.buttons[i] = default_param;
|
||||
values.buttons[i] =
|
||||
sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
|
||||
if (values.buttons[i].empty())
|
||||
values.buttons[i] = default_param;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||
default_analogs[i][3], default_analogs[i][4], 0.5f);
|
||||
Settings::values.analogs[i] =
|
||||
sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
|
||||
if (Settings::values.analogs[i].empty())
|
||||
Settings::values.analogs[i] = default_param;
|
||||
values.analogs[i] =
|
||||
sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
|
||||
if (values.analogs[i].empty())
|
||||
values.analogs[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.motion_device = sdl2_config->Get(
|
||||
"Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
|
||||
Settings::values.touch_device =
|
||||
sdl2_config->Get("Controls", "touch_device", "engine:emu_window");
|
||||
values.motion_device = sdl2_config->Get(group, "motion_device",
|
||||
"engine:motion_emu,update_period:100,sensitivity:0.01");
|
||||
values.touch_device = sdl2_config->Get(group, "touch_device", "engine:emu_window");
|
||||
|
||||
// Core
|
||||
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
|
||||
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
|
||||
// System
|
||||
values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
|
||||
|
||||
// Renderer
|
||||
Settings::values.resolution_factor =
|
||||
(float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
|
||||
Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
|
||||
Settings::values.frame_limit =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
group = fmt::format("{}Renderer", name);
|
||||
values.resolution_factor = (float)sdl2_config->GetReal(group, "resolution_factor", 1.0);
|
||||
values.use_frame_limit = sdl2_config->GetBoolean(group, "use_frame_limit", true);
|
||||
values.frame_limit = static_cast<u16>(sdl2_config->GetInteger(group, "frame_limit", 100));
|
||||
Settings::values.use_accurate_gpu_emulation =
|
||||
sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
|
||||
|
||||
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
|
||||
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
|
||||
Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
|
||||
values.bg_red = (float)sdl2_config->GetReal(group, "bg_red", 0.0);
|
||||
values.bg_green = (float)sdl2_config->GetReal(group, "bg_green", 0.0);
|
||||
values.bg_blue = (float)sdl2_config->GetReal(group, "bg_blue", 0.0);
|
||||
|
||||
// Audio
|
||||
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
|
||||
Settings::values.enable_audio_stretching =
|
||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
||||
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
|
||||
Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
|
||||
group = fmt::format("{}Audio", name);
|
||||
values.sink_id = sdl2_config->Get(group, "output_engine", "auto");
|
||||
values.enable_audio_stretching =
|
||||
sdl2_config->GetBoolean(group, "enable_audio_stretching", true);
|
||||
values.audio_device_id = sdl2_config->Get(group, "output_device", "auto");
|
||||
values.volume = sdl2_config->GetReal(group, "volume", 1);
|
||||
|
||||
// Debugging
|
||||
group = fmt::format("{}Debugging", name);
|
||||
values.program_args = sdl2_config->Get(group, "program_args", "");
|
||||
|
||||
// Add Ons
|
||||
group = fmt::format("{}AddOns", name);
|
||||
const auto disabled_str = sdl2_config->Get(group, "disabled_patches", "");
|
||||
|
||||
std::stringstream ss(disabled_str);
|
||||
std::string line;
|
||||
values.disabled_patches.clear();
|
||||
while (std::getline(ss, line, ';'))
|
||||
values.disabled_patches.push_back(line);
|
||||
}
|
||||
|
||||
bool Config::UpdateCurrentGame(u64 title_id, Settings::PerGameValues& values) {
|
||||
if (std::find(titles.begin(), titles.end(), title_id) == titles.end())
|
||||
return false;
|
||||
|
||||
values = Settings::values.default_game;
|
||||
ReadValuesForTitleID(values, fmt::format("{:016X}", title_id));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
ReadValuesForTitleID(Settings::values.default_game, "");
|
||||
|
||||
const auto titles_str = sdl2_config->Get("Per Game", "Titles", "");
|
||||
std::stringstream ss(titles_str);
|
||||
|
||||
std::string token;
|
||||
while (std::getline(ss, token, '|')) {
|
||||
if (token.size() != 16)
|
||||
continue;
|
||||
|
||||
const u64 val = std::stoull(token, nullptr, 16);
|
||||
titles.push_back(val);
|
||||
}
|
||||
|
||||
Settings::values.SetUpdateCurrentGameFunction(
|
||||
[this](u64 title_id, Settings::PerGameValues& values) {
|
||||
return UpdateCurrentGame(title_id, values);
|
||||
});
|
||||
|
||||
// Data Storage
|
||||
Settings::values.use_virtual_sd =
|
||||
@@ -124,14 +168,21 @@ void Config::ReadValues() {
|
||||
sdl2_config->Get("Data Storage", "sdmc_directory",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
|
||||
|
||||
// Core
|
||||
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
|
||||
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
|
||||
|
||||
// System
|
||||
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
|
||||
Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
|
||||
const auto size = sdl2_config->GetInteger("System", "users_size", 0);
|
||||
|
||||
Settings::values.current_user = std::clamp<int>(
|
||||
sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
|
||||
|
||||
// Renderer
|
||||
Settings::values.use_accurate_gpu_emulation =
|
||||
sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
|
||||
|
||||
// Miscellaneous
|
||||
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
|
||||
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
|
||||
@@ -140,7 +191,6 @@ void Config::ReadValues() {
|
||||
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
|
||||
Settings::values.gdbstub_port =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
|
||||
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
|
||||
|
||||
// Web Service
|
||||
Settings::values.enable_telemetry =
|
||||
|
||||
@@ -12,9 +12,13 @@ class INIReader;
|
||||
class Config {
|
||||
std::unique_ptr<INIReader> sdl2_config;
|
||||
std::string sdl2_config_loc;
|
||||
std::vector<u64> titles;
|
||||
|
||||
bool LoadINI(const std::string& default_contents = "", bool retry = true);
|
||||
void ReadValues();
|
||||
void ReadValuesForTitleID(Settings::PerGameValues& values, const std::string& name);
|
||||
|
||||
bool UpdateCurrentGame(u64 title_id, Settings::PerGameValues& values);
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
@@ -135,7 +135,7 @@ int main(int argc, char** argv) {
|
||||
PrintVersion();
|
||||
return 0;
|
||||
case 'p':
|
||||
Settings::values.program_args = argv[optind];
|
||||
Settings::values->program_args = argv[optind];
|
||||
++optind;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user