Compare commits

...

31 Commits

Author SHA1 Message Date
Zach Hilman
4b149768d3 core: Set game config title ID to zero if game has no title ID 2018-10-25 14:06:56 -04:00
Zach Hilman
d6eca26279 qt: Use ReadableByteSize instead of QLocale for size formatting 2018-10-25 09:24:48 -04:00
Zach Hilman
1e4024c7c4 configure_per_general: Fix bug loading dialog for files spanning more than one
Mainly affects NAX and NCA files in registered, as they are split along 4GB boundaries.
2018-10-25 09:24:48 -04:00
Zach Hilman
573b88ab27 qt: Open current per game config when game is running
Instead of opening global config. Also prevents strange glitches from switching current game config mid-game.
2018-10-25 09:24:48 -04:00
Zach Hilman
6c728b4819 game_list: Avoid refreshing game list if settings aren't changed 2018-10-25 09:24:48 -04:00
Zach Hilman
e5a80d1ed4 settings: Fix various issues with setting/getting current config
Sets title ID properly in SetCurrentTitleID and enuses proper reading/saving in qt config
2018-10-25 09:24:48 -04:00
Zach Hilman
4e80486361 game_list: Use proper text colors in dark theme with item delegate 2018-10-25 09:24:48 -04:00
Zach Hilman
22026e617d qt/configure: Obey partially checked status for QCheckBoxes
Allows proper saving of a setting if the box is left partially checked instead of defaulting to true.
2018-10-25 09:24:48 -04:00
Zach Hilman
4dd3a9a22b core: Print current game settings on game boot 2018-10-25 09:24:48 -04:00
Zach Hilman
2bfcaabf5f game_list: Update sizeHint in HTMLDelegate to obey icon size 2018-10-25 09:24:48 -04:00
Zach Hilman
af2d90696e hid: Port better HID to per game config 2018-10-25 09:24:48 -04:00
Zach Hilman
edcb75a037 configure_dialog: Force global mode with tabs 2018-10-25 09:24:48 -04:00
Zach Hilman
59633ac921 configure_input: Port to support both per-game and global modes 2018-10-25 09:24:04 -04:00
Zach Hilman
518ac27996 configure_graphics: Port to support both per-game and global modes 2018-10-25 09:24:04 -04:00
Zach Hilman
0f042773d3 configure_debug: Port to support both per-game and global modes 2018-10-25 09:24:04 -04:00
Zach Hilman
f33be7c01c configure_audio: Port to support both per-game and global modes 2018-10-25 09:24:04 -04:00
Zach Hilman
34e5693be8 qt: Add per game config overview page for dialog 2018-10-25 09:24:04 -04:00
Zach Hilman
4c54b9446a qt: Add per game config dialog with tabs 2018-10-25 09:24:04 -04:00
Zach Hilman
1485616a54 game_list: Add custom HTMLDelegate to support strikethrough in game list 2018-10-25 09:24:04 -04:00
Zach Hilman
11a3f59936 qt: Add Properties option to game list context menu 2018-10-25 09:24:04 -04:00
Zach Hilman
7539d2d9d4 qt: Add help option to open yuzu folder 2018-10-25 09:23:27 -04:00
Zach Hilman
e9166f4664 game_list: Show disabled patches with strikethrough in list 2018-10-25 09:21:52 -04:00
Zach Hilman
7d2fb1cffb yuzu/config: Add support for reading and saving per game config 2018-10-25 09:17:47 -04:00
Zach Hilman
295b22474a yuzu/config: Save settings delta on title ID change
Allows user to mark settings that didn't change to not be overridden by global
2018-10-25 09:17:09 -04:00
Zach Hilman
ce4052e527 yuzu_cmd: Add support for reading per game config 2018-10-25 09:14:33 -04:00
Zach Hilman
23daaccf84 core: Apply current game settings on boot 2018-10-25 09:13:02 -04:00
Zach Hilman
b71a463df6 loader: Add accessor to get game developer name 2018-10-25 09:13:02 -04:00
Zach Hilman
ab9abac488 aoc_u: Add support for disabling DLC 2018-10-25 09:13:02 -04:00
Zach Hilman
1de29dd396 patch_manager: Add support for disabling patches
Uses current per game config to determine enabled
2018-10-25 09:13:02 -04:00
Zach Hilman
5b70c9c493 settings: Port all current uses of per game settings to new class 2018-10-25 09:13:02 -04:00
Zach Hilman
7452a10688 settings: Add PerGameValues class to encapsulate game-specific settings
Provides various operators and methods to facilitate easy management and use of settings.
2018-10-25 09:12:23 -04:00
61 changed files with 2812 additions and 412 deletions

View File

@@ -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>(

View File

@@ -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)};

View File

@@ -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;

View File

@@ -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());

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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.

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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()), {}};

View File

@@ -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()), {}};

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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 &current_game;
}
const PerGameValues* Values::operator->() const {
if (current_title_id == 0)
return &default_game;
return &current_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);

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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/>

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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/>

View File

@@ -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();
}

View File

@@ -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(

View File

@@ -20,6 +20,9 @@ public:
void applyConfiguration();
public slots:
void RequestGameListUpdate();
private:
void setConfiguration();

View File

@@ -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();
}

View File

@@ -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());
}

View File

@@ -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();

View File

@@ -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/>

View File

@@ -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);

View File

@@ -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

View 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>

View 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;
}

View 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;
};

View 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;
}

View 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;
};

View 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>

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -95,7 +95,7 @@ public:
if (row2.isEmpty())
return row1;
return row1 + "\n " + row2;
return row1 + "<br> " + row2;
}
return GameListItem::data(role);

View File

@@ -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)));
}
}

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -110,6 +110,7 @@
<string>&amp;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>

View File

@@ -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;

View File

@@ -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 =

View File

@@ -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();

View File

@@ -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;
}