summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/quaternion.h2
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/core.h5
-rw-r--r--src/core/file_sys/control_metadata.cpp28
-rw-r--r--src/core/file_sys/control_metadata.h30
-rw-r--r--src/core/file_sys/savedata_factory.cpp31
-rw-r--r--src/core/file_sys/savedata_factory.h8
-rw-r--r--src/core/file_sys/vfs.h4
-rw-r--r--src/core/frontend/applets/profile_select.cpp19
-rw-r--r--src/core/frontend/applets/profile_select.h27
-rw-r--r--src/core/frontend/framebuffer_layout.cpp15
-rw-r--r--src/core/frontend/framebuffer_layout.h7
-rw-r--r--src/core/gdbstub/gdbstub.cpp4
-rw-r--r--src/core/hle/kernel/errors.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp12
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/process.h18
-rw-r--r--src/core/hle/kernel/svc.cpp73
-rw-r--r--src/core/hle/kernel/svc_wrap.h10
-rw-r--r--src/core/hle/kernel/thread.cpp24
-rw-r--r--src/core/hle/kernel/thread.h18
-rw-r--r--src/core/hle/kernel/vm_manager.cpp1
-rw-r--r--src/core/hle/service/am/am.cpp58
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp77
-rw-r--r--src/core/hle/service/am/applets/profile_select.h50
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp47
-rw-r--r--src/core/hle/service/filesystem/filesystem.h6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp46
-rw-r--r--src/core/loader/loader.h10
-rw-r--r--src/core/loader/nsp.cpp4
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp5
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/memory.cpp1
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/engines/shader_bytecode.h4
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/macro_interpreter.cpp2
-rw-r--r--src/video_core/morton.cpp1
-rw-r--r--src/video_core/renderer_base.cpp12
-rw-r--r--src/video_core/renderer_base.h27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp14
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp43
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h9
-rw-r--r--src/video_core/surface.cpp7
-rw-r--r--src/video_core/textures/decoders.cpp2
-rw-r--r--src/video_core/video_core.cpp8
-rw-r--r--src/video_core/video_core.h2
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/applets/profile_select.cpp168
-rw-r--r--src/yuzu/applets/profile_select.h73
-rw-r--r--src/yuzu/bootmanager.cpp18
-rw-r--r--src/yuzu/bootmanager.h6
-rw-r--r--src/yuzu/configuration/config.cpp17
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configure.ui52
-rw-r--r--src/yuzu/configuration/configure_input.cpp60
-rw-r--r--src/yuzu/configuration/configure_input.h8
-rw-r--r--src/yuzu/configuration/configure_input.ui48
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp1
-rw-r--r--src/yuzu/configuration/configure_input_simple.cpp137
-rw-r--r--src/yuzu/configuration/configure_input_simple.h40
-rw-r--r--src/yuzu/configuration/configure_input_simple.ui97
-rw-r--r--src/yuzu/configuration/configure_per_general.cpp10
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp1
-rw-r--r--src/yuzu/debugger/wait_tree.cpp5
-rw-r--r--src/yuzu/main.cpp52
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/main.ui88
-rw-r--r--src/yuzu/ui_settings.h7
75 files changed, 1481 insertions, 232 deletions
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index ea39298c1..c528c0b68 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -12,7 +12,7 @@ template <typename T>
12class Quaternion { 12class Quaternion {
13public: 13public:
14 Math::Vec3<T> xyz; 14 Math::Vec3<T> xyz;
15 T w; 15 T w{};
16 16
17 Quaternion<decltype(-T{})> Inverse() const { 17 Quaternion<decltype(-T{})> Inverse() const {
18 return {-xyz, w}; 18 return {-xyz, w};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 882c9ab59..93f5ba3fe 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -83,6 +83,8 @@ add_library(core STATIC
83 file_sys/vfs_vector.h 83 file_sys/vfs_vector.h
84 file_sys/xts_archive.cpp 84 file_sys/xts_archive.cpp
85 file_sys/xts_archive.h 85 file_sys/xts_archive.h
86 frontend/applets/profile_select.cpp
87 frontend/applets/profile_select.h
86 frontend/applets/software_keyboard.cpp 88 frontend/applets/software_keyboard.cpp
87 frontend/applets/software_keyboard.h 89 frontend/applets/software_keyboard.h
88 frontend/emu_window.cpp 90 frontend/emu_window.cpp
@@ -162,6 +164,8 @@ add_library(core STATIC
162 hle/service/am/applet_oe.h 164 hle/service/am/applet_oe.h
163 hle/service/am/applets/applets.cpp 165 hle/service/am/applets/applets.cpp
164 hle/service/am/applets/applets.h 166 hle/service/am/applets/applets.h
167 hle/service/am/applets/profile_select.cpp
168 hle/service/am/applets/profile_select.h
165 hle/service/am/applets/software_keyboard.cpp 169 hle/service/am/applets/software_keyboard.cpp
166 hle/service/am/applets/software_keyboard.h 170 hle/service/am/applets/software_keyboard.h
167 hle/service/am/applets/stub_applet.cpp 171 hle/service/am/applets/stub_applet.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index ce7851538..fd10199ec 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -99,6 +99,8 @@ struct System::Impl {
99 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 99 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
100 100
101 /// Create default implementations of applets if one is not provided. 101 /// Create default implementations of applets if one is not provided.
102 if (profile_selector == nullptr)
103 profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
102 if (software_keyboard == nullptr) 104 if (software_keyboard == nullptr)
103 software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); 105 software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
104 106
@@ -229,6 +231,7 @@ struct System::Impl {
229 bool is_powered_on = false; 231 bool is_powered_on = false;
230 232
231 /// Frontend applets 233 /// Frontend applets
234 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
232 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; 235 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
233 236
234 /// Service manager 237 /// Service manager
@@ -424,6 +427,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
424 return impl->virtual_filesystem; 427 return impl->virtual_filesystem;
425} 428}
426 429
430void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
431 impl->profile_selector = std::move(applet);
432}
433
434const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
435 return *impl->profile_selector;
436}
437
427void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) { 438void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
428 impl->software_keyboard = std::move(applet); 439 impl->software_keyboard = std::move(applet);
429} 440}
diff --git a/src/core/core.h b/src/core/core.h
index 71031dfcf..869921493 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -11,6 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/file_sys/vfs_types.h" 12#include "core/file_sys/vfs_types.h"
13#include "core/hle/kernel/object.h" 13#include "core/hle/kernel/object.h"
14#include "frontend/applets/profile_select.h"
14 15
15namespace Core::Frontend { 16namespace Core::Frontend {
16class EmuWindow; 17class EmuWindow;
@@ -241,6 +242,10 @@ public:
241 242
242 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 243 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
243 244
245 void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
246
247 const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
248
244 void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet); 249 void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
245 250
246 const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; 251 const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index e065e592f..83c184750 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -36,18 +36,20 @@ std::string LanguageEntry::GetDeveloperName() const {
36 developer_name.size()); 36 developer_name.size());
37} 37}
38 38
39NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 39NACP::NACP() = default;
40 file->ReadObject(raw.get()); 40
41NACP::NACP(VirtualFile file) {
42 file->ReadObject(&raw);
41} 43}
42 44
43NACP::~NACP() = default; 45NACP::~NACP() = default;
44 46
45const LanguageEntry& NACP::GetLanguageEntry(Language language) const { 47const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
46 if (language != Language::Default) { 48 if (language != Language::Default) {
47 return raw->language_entries.at(static_cast<u8>(language)); 49 return raw.language_entries.at(static_cast<u8>(language));
48 } 50 }
49 51
50 for (const auto& language_entry : raw->language_entries) { 52 for (const auto& language_entry : raw.language_entries) {
51 if (!language_entry.GetApplicationName().empty()) 53 if (!language_entry.GetApplicationName().empty())
52 return language_entry; 54 return language_entry;
53 } 55 }
@@ -65,21 +67,29 @@ std::string NACP::GetDeveloperName(Language language) const {
65} 67}
66 68
67u64 NACP::GetTitleId() const { 69u64 NACP::GetTitleId() const {
68 return raw->title_id; 70 return raw.title_id;
69} 71}
70 72
71u64 NACP::GetDLCBaseTitleId() const { 73u64 NACP::GetDLCBaseTitleId() const {
72 return raw->dlc_base_title_id; 74 return raw.dlc_base_title_id;
73} 75}
74 76
75std::string NACP::GetVersionString() const { 77std::string NACP::GetVersionString() const {
76 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 78 return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
77 raw->version_string.size()); 79 raw.version_string.size());
80}
81
82u64 NACP::GetDefaultNormalSaveSize() const {
83 return raw.normal_save_data_size;
84}
85
86u64 NACP::GetDefaultJournalSaveSize() const {
87 return raw.journal_sava_data_size;
78} 88}
79 89
80std::vector<u8> NACP::GetRawBytes() const { 90std::vector<u8> NACP::GetRawBytes() const {
81 std::vector<u8> out(sizeof(RawNACP)); 91 std::vector<u8> out(sizeof(RawNACP));
82 std::memcpy(out.data(), raw.get(), sizeof(RawNACP)); 92 std::memcpy(out.data(), &raw, sizeof(RawNACP));
83 return out; 93 return out;
84} 94}
85} // namespace FileSys 95} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index bfaad46b4..7b9cdc910 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -28,17 +28,30 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.
28// The raw file format of a NACP file. 28// The raw file format of a NACP file.
29struct RawNACP { 29struct RawNACP {
30 std::array<LanguageEntry, 16> language_entries; 30 std::array<LanguageEntry, 16> language_entries;
31 INSERT_PADDING_BYTES(0x38); 31 std::array<u8, 0x25> isbn;
32 u8 startup_user_account;
33 INSERT_PADDING_BYTES(2);
34 u32_le application_attribute;
35 u32_le supported_languages;
36 u32_le parental_control;
37 bool screenshot_enabled;
38 u8 video_capture_mode;
39 bool data_loss_confirmation;
40 INSERT_PADDING_BYTES(1);
32 u64_le title_id; 41 u64_le title_id;
33 INSERT_PADDING_BYTES(0x20); 42 std::array<u8, 0x20> rating_age;
34 std::array<char, 0x10> version_string; 43 std::array<char, 0x10> version_string;
35 u64_le dlc_base_title_id; 44 u64_le dlc_base_title_id;
36 u64_le title_id_2; 45 u64_le title_id_2;
37 INSERT_PADDING_BYTES(0x28); 46 u64_le normal_save_data_size;
47 u64_le journal_sava_data_size;
48 INSERT_PADDING_BYTES(0x18);
38 u64_le product_code; 49 u64_le product_code;
39 u64_le title_id_3; 50 std::array<u64_le, 0x8> local_communication;
40 std::array<u64_le, 0x7> title_id_array; 51 u8 logo_type;
41 INSERT_PADDING_BYTES(0x8); 52 u8 logo_handling;
53 bool runtime_add_on_content_install;
54 INSERT_PADDING_BYTES(5);
42 u64_le title_id_update; 55 u64_le title_id_update;
43 std::array<u8, 0x40> bcat_passphrase; 56 std::array<u8, 0x40> bcat_passphrase;
44 INSERT_PADDING_BYTES(0xEC0); 57 INSERT_PADDING_BYTES(0xEC0);
@@ -72,6 +85,7 @@ extern const std::array<const char*, 15> LANGUAGE_NAMES;
72// These store application name, dev name, title id, and other miscellaneous data. 85// These store application name, dev name, title id, and other miscellaneous data.
73class NACP { 86class NACP {
74public: 87public:
88 explicit NACP();
75 explicit NACP(VirtualFile file); 89 explicit NACP(VirtualFile file);
76 ~NACP(); 90 ~NACP();
77 91
@@ -81,10 +95,12 @@ public:
81 u64 GetTitleId() const; 95 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const; 96 u64 GetDLCBaseTitleId() const;
83 std::string GetVersionString() const; 97 std::string GetVersionString() const;
98 u64 GetDefaultNormalSaveSize() const;
99 u64 GetDefaultJournalSaveSize() const;
84 std::vector<u8> GetRawBytes() const; 100 std::vector<u8> GetRawBytes() const;
85 101
86private: 102private:
87 std::unique_ptr<RawNACP> raw; 103 RawNACP raw{};
88}; 104};
89 105
90} // namespace FileSys 106} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index bd50fedc7..1913dc956 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -13,6 +13,8 @@
13 13
14namespace FileSys { 14namespace FileSys {
15 15
16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
17
16std::string SaveDataDescriptor::DebugInfo() const { 18std::string SaveDataDescriptor::DebugInfo() const {
17 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]", 19 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
18 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); 20 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
@@ -128,7 +130,36 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
128 return fmt::format("{}save/cache/{:016X}", out, title_id); 130 return fmt::format("{}save/cache/{:016X}", out, title_id);
129 default: 131 default:
130 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 132 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
133 return fmt::format("{}save/unknown_{:X}/{:016X}", out, static_cast<u8>(type), title_id);
131 } 134 }
132} 135}
133 136
137SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
138 u128 user_id) const {
139 const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
140 const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
141
142 const auto size_file = dir->GetFile(SAVE_DATA_SIZE_FILENAME);
143 if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize))
144 return {0, 0};
145
146 SaveDataSize out;
147 if (size_file->ReadObject(&out) != sizeof(SaveDataSize))
148 return {0, 0};
149 return out;
150}
151
152void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
153 SaveDataSize new_value) {
154 const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
155 const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
156
157 const auto size_file = dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
158 if (size_file == nullptr)
159 return;
160
161 size_file->Resize(sizeof(SaveDataSize));
162 size_file->WriteObject(new_value);
163}
164
134} // namespace FileSys 165} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index bd4919610..3a1caf292 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -46,6 +46,11 @@ struct SaveDataDescriptor {
46}; 46};
47static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); 47static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
48 48
49struct SaveDataSize {
50 u64 normal;
51 u64 journal;
52};
53
49/// File system interface to the SaveData archive 54/// File system interface to the SaveData archive
50class SaveDataFactory { 55class SaveDataFactory {
51public: 56public:
@@ -60,6 +65,9 @@ public:
60 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 65 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
61 u128 user_id, u64 save_id); 66 u128 user_id, u64 save_id);
62 67
68 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
69 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
70
63private: 71private:
64 VirtualDir dir; 72 VirtualDir dir;
65}; 73};
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index e5641b255..954094772 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -150,7 +150,7 @@ public:
150 template <typename T> 150 template <typename T>
151 std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { 151 std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
152 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 152 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
153 return Write(data, number_elements * sizeof(T), offset); 153 return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
154 } 154 }
155 155
156 // Writes size bytes starting at memory location data to offset in file. 156 // Writes size bytes starting at memory location data to offset in file.
@@ -166,7 +166,7 @@ public:
166 template <typename T> 166 template <typename T>
167 std::size_t WriteObject(const T& data, std::size_t offset = 0) { 167 std::size_t WriteObject(const T& data, std::size_t offset = 0) {
168 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 168 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
169 return Write(&data, sizeof(T), offset); 169 return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
170 } 170 }
171 171
172 // Renames the file to name. Returns whether or not the operation was successsful. 172 // Renames the file to name. Returns whether or not the operation was successsful.
diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp
new file mode 100644
index 000000000..fbf5f2a9e
--- /dev/null
+++ b/src/core/frontend/applets/profile_select.cpp
@@ -0,0 +1,19 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/applets/profile_select.h"
6#include "core/settings.h"
7
8namespace Core::Frontend {
9
10ProfileSelectApplet::~ProfileSelectApplet() = default;
11
12void DefaultProfileSelectApplet::SelectProfile(
13 std::function<void(std::optional<Service::Account::UUID>)> callback) const {
14 Service::Account::ProfileManager manager;
15 callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
16 LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
17}
18
19} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h
new file mode 100644
index 000000000..fc8f7ae94
--- /dev/null
+++ b/src/core/frontend/applets/profile_select.h
@@ -0,0 +1,27 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <optional>
9#include "core/hle/service/acc/profile_manager.h"
10
11namespace Core::Frontend {
12
13class ProfileSelectApplet {
14public:
15 virtual ~ProfileSelectApplet();
16
17 virtual void SelectProfile(
18 std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
19};
20
21class DefaultProfileSelectApplet final : public ProfileSelectApplet {
22public:
23 void SelectProfile(
24 std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
25};
26
27} // namespace Core::Frontend
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 8f07aedc9..f8662d193 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/frontend/framebuffer_layout.h" 8#include "core/frontend/framebuffer_layout.h"
9#include "core/settings.h"
9 10
10namespace Layout { 11namespace Layout {
11 12
@@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
42 return res; 43 return res;
43} 44}
44 45
46FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
47 int width, height;
48
49 if (Settings::values.use_docked_mode) {
50 width = ScreenDocked::WidthDocked * res_scale;
51 height = ScreenDocked::HeightDocked * res_scale;
52 } else {
53 width = ScreenUndocked::Width * res_scale;
54 height = ScreenUndocked::Height * res_scale;
55 }
56
57 return DefaultFrameLayout(width, height);
58}
59
45} // namespace Layout 60} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index f3fddfa94..e06647794 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -9,6 +9,7 @@
9namespace Layout { 9namespace Layout {
10 10
11enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; 11enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
12enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
12 13
13/// Describes the layout of the window framebuffer 14/// Describes the layout of the window framebuffer
14struct FramebufferLayout { 15struct FramebufferLayout {
@@ -34,4 +35,10 @@ struct FramebufferLayout {
34 */ 35 */
35FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height); 36FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
36 37
38/**
39 * Convenience method to get frame layout by resolution scale
40 * @param res_scale resolution scale factor
41 */
42FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
43
37} // namespace Layout 44} // namespace Layout
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index e6b5171ee..a1cad4fcb 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -201,11 +201,11 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
201 modules.push_back(std::move(module)); 201 modules.push_back(std::move(module));
202} 202}
203 203
204static Kernel::Thread* FindThreadById(int id) { 204static Kernel::Thread* FindThreadById(s64 id) {
205 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 205 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
206 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList(); 206 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
207 for (auto& thread : threads) { 207 for (auto& thread : threads) {
208 if (thread->GetThreadID() == static_cast<u32>(id)) { 208 if (thread->GetThreadID() == static_cast<u64>(id)) {
209 current_core = core; 209 current_core = core;
210 return thread.get(); 210 return thread.get();
211 } 211 }
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 8b58d701d..d8240ec6d 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -27,7 +27,7 @@ constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; 27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; 28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; 29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
30constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122}; 30constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; 31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; 32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; 33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e441c5bc6..1c2290651 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -112,7 +112,7 @@ struct KernelCore::Impl {
112 112
113 void Shutdown() { 113 void Shutdown() {
114 next_object_id = 0; 114 next_object_id = 0;
115 next_process_id = 10; 115 next_process_id = Process::ProcessIDMin;
116 next_thread_id = 1; 116 next_thread_id = 1;
117 117
118 process_list.clear(); 118 process_list.clear();
@@ -153,10 +153,8 @@ struct KernelCore::Impl {
153 } 153 }
154 154
155 std::atomic<u32> next_object_id{0}; 155 std::atomic<u32> next_object_id{0};
156 // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are 156 std::atomic<u64> next_process_id{Process::ProcessIDMin};
157 // reserved for low-level services 157 std::atomic<u64> next_thread_id{1};
158 std::atomic<u32> next_process_id{10};
159 std::atomic<u32> next_thread_id{1};
160 158
161 // Lists all processes that exist in the current session. 159 // Lists all processes that exist in the current session.
162 std::vector<SharedPtr<Process>> process_list; 160 std::vector<SharedPtr<Process>> process_list;
@@ -242,11 +240,11 @@ u32 KernelCore::CreateNewObjectID() {
242 return impl->next_object_id++; 240 return impl->next_object_id++;
243} 241}
244 242
245u32 KernelCore::CreateNewThreadID() { 243u64 KernelCore::CreateNewThreadID() {
246 return impl->next_thread_id++; 244 return impl->next_thread_id++;
247} 245}
248 246
249u32 KernelCore::CreateNewProcessID() { 247u64 KernelCore::CreateNewProcessID() {
250 return impl->next_process_id++; 248 return impl->next_process_id++;
251} 249}
252 250
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index ea00c89f5..58c9d108b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -88,10 +88,10 @@ private:
88 u32 CreateNewObjectID(); 88 u32 CreateNewObjectID();
89 89
90 /// Creates a new process ID, incrementing the internal process ID counter; 90 /// Creates a new process ID, incrementing the internal process ID counter;
91 u32 CreateNewProcessID(); 91 u64 CreateNewProcessID();
92 92
93 /// Creates a new thread ID, incrementing the internal thread ID counter. 93 /// Creates a new thread ID, incrementing the internal thread ID counter.
94 u32 CreateNewThreadID(); 94 u64 CreateNewThreadID();
95 95
96 /// Creates a timer callback handle for the given timer. 96 /// Creates a timer callback handle for the given timer.
97 ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer); 97 ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index 0ea851a74..806078638 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -32,6 +32,7 @@ bool Object::IsWaitable() const {
32 } 32 }
33 33
34 UNREACHABLE(); 34 UNREACHABLE();
35 return false;
35} 36}
36 37
37} // namespace Kernel 38} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 459eedfa6..7da367251 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -120,6 +120,18 @@ struct CodeSet final {
120 120
121class Process final : public WaitObject { 121class Process final : public WaitObject {
122public: 122public:
123 enum : u64 {
124 /// Lowest allowed process ID for a kernel initial process.
125 InitialKIPIDMin = 1,
126 /// Highest allowed process ID for a kernel initial process.
127 InitialKIPIDMax = 80,
128
129 /// Lowest allowed process ID for a userland process.
130 ProcessIDMin = 81,
131 /// Highest allowed process ID for a userland process.
132 ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
133 };
134
123 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; 135 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
124 136
125 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); 137 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
@@ -162,7 +174,7 @@ public:
162 } 174 }
163 175
164 /// Gets the unique ID that identifies this particular process. 176 /// Gets the unique ID that identifies this particular process.
165 u32 GetProcessID() const { 177 u64 GetProcessID() const {
166 return process_id; 178 return process_id;
167 } 179 }
168 180
@@ -288,10 +300,10 @@ private:
288 ProcessStatus status; 300 ProcessStatus status;
289 301
290 /// The ID of this process 302 /// The ID of this process
291 u32 process_id = 0; 303 u64 process_id = 0;
292 304
293 /// Title ID corresponding to the process 305 /// Title ID corresponding to the process
294 u64 program_id; 306 u64 program_id = 0;
295 307
296 /// Resource limit descriptor for this process 308 /// Resource limit descriptor for this process
297 SharedPtr<ResourceLimit> resource_limit; 309 SharedPtr<ResourceLimit> resource_limit;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index c826dfd96..2e80b48c2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -391,7 +391,7 @@ static ResultCode SendSyncRequest(Handle handle) {
391} 391}
392 392
393/// Get the ID for the specified thread. 393/// Get the ID for the specified thread.
394static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { 394static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
395 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 395 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
396 396
397 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 397 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
@@ -405,20 +405,33 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
405 return RESULT_SUCCESS; 405 return RESULT_SUCCESS;
406} 406}
407 407
408/// Get the ID of the specified process 408/// Gets the ID of the specified process or a specified thread's owning process.
409static ResultCode GetProcessId(u32* process_id, Handle process_handle) { 409static ResultCode GetProcessId(u64* process_id, Handle handle) {
410 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); 410 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
411 411
412 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 412 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
413 const SharedPtr<Process> process = handle_table.Get<Process>(process_handle); 413 const SharedPtr<Process> process = handle_table.Get<Process>(handle);
414 if (!process) { 414 if (process) {
415 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", 415 *process_id = process->GetProcessID();
416 process_handle); 416 return RESULT_SUCCESS;
417 return ERR_INVALID_HANDLE;
418 } 417 }
419 418
420 *process_id = process->GetProcessID(); 419 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
421 return RESULT_SUCCESS; 420 if (thread) {
421 const Process* const owner_process = thread->GetOwnerProcess();
422 if (!owner_process) {
423 LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
424 return ERR_INVALID_HANDLE;
425 }
426
427 *process_id = owner_process->GetProcessID();
428 return RESULT_SUCCESS;
429 }
430
431 // NOTE: This should also handle debug objects before returning.
432
433 LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
434 return ERR_INVALID_HANDLE;
422} 435}
423 436
424/// Default thread wakeup callback for WaitSynchronization 437/// Default thread wakeup callback for WaitSynchronization
@@ -919,8 +932,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
919} 932}
920 933
921/// Sets the thread activity 934/// Sets the thread activity
922static ResultCode SetThreadActivity(Handle handle, u32 unknown) { 935static ResultCode SetThreadActivity(Handle handle, u32 activity) {
923 LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown); 936 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
937 if (activity > static_cast<u32>(ThreadActivity::Paused)) {
938 return ERR_INVALID_ENUM_VALUE;
939 }
940
941 const auto* current_process = Core::CurrentProcess();
942 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
943 if (!thread) {
944 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
945 return ERR_INVALID_HANDLE;
946 }
947
948 if (thread->GetOwnerProcess() != current_process) {
949 LOG_ERROR(Kernel_SVC,
950 "The current process does not own the current thread, thread_handle={:08X} "
951 "thread_pid={}, "
952 "current_process_pid={}",
953 handle, thread->GetOwnerProcess()->GetProcessID(),
954 current_process->GetProcessID());
955 return ERR_INVALID_HANDLE;
956 }
957
958 if (thread == GetCurrentThread()) {
959 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
960 return ERR_BUSY;
961 }
962
963 thread->SetActivity(static_cast<ThreadActivity>(activity));
924 return RESULT_SUCCESS; 964 return RESULT_SUCCESS;
925} 965}
926 966
@@ -947,7 +987,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
947 987
948 if (thread == GetCurrentThread()) { 988 if (thread == GetCurrentThread()) {
949 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); 989 LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
950 return ERR_ALREADY_REGISTERED; 990 return ERR_BUSY;
951 } 991 }
952 992
953 Core::ARM_Interface::ThreadContext ctx = thread->GetContext(); 993 Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
@@ -1232,7 +1272,10 @@ static ResultCode StartThread(Handle thread_handle) {
1232 ASSERT(thread->GetStatus() == ThreadStatus::Dormant); 1272 ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
1233 1273
1234 thread->ResumeFromWait(); 1274 thread->ResumeFromWait();
1235 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); 1275
1276 if (thread->GetStatus() == ThreadStatus::Ready) {
1277 Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
1278 }
1236 1279
1237 return RESULT_SUCCESS; 1280 return RESULT_SUCCESS;
1238} 1281}
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 2f758b959..2a2c2c5ea 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -73,7 +73,15 @@ void SvcWrap() {
73template <ResultCode func(u32*, u64)> 73template <ResultCode func(u32*, u64)>
74void SvcWrap() { 74void SvcWrap() {
75 u32 param_1 = 0; 75 u32 param_1 = 0;
76 u32 retval = func(&param_1, Param(1)).raw; 76 const u32 retval = func(&param_1, Param(1)).raw;
77 Core::CurrentArmInterface().SetReg(1, param_1);
78 FuncReturn(retval);
79}
80
81template <ResultCode func(u64*, u32)>
82void SvcWrap() {
83 u64 param_1 = 0;
84 const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
77 Core::CurrentArmInterface().SetReg(1, param_1); 85 Core::CurrentArmInterface().SetReg(1, param_1);
78 FuncReturn(retval); 86 FuncReturn(retval);
79} 87}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 63f8923fd..434655638 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -50,7 +50,7 @@ void Thread::Stop() {
50 50
51 // Clean up thread from ready queue 51 // Clean up thread from ready queue
52 // This is only needed when the thread is terminated forcefully (SVC TerminateProcess) 52 // This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
53 if (status == ThreadStatus::Ready) { 53 if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
54 scheduler->UnscheduleThread(this, current_priority); 54 scheduler->UnscheduleThread(this, current_priority);
55 } 55 }
56 56
@@ -140,6 +140,11 @@ void Thread::ResumeFromWait() {
140 140
141 wakeup_callback = nullptr; 141 wakeup_callback = nullptr;
142 142
143 if (activity == ThreadActivity::Paused) {
144 status = ThreadStatus::Paused;
145 return;
146 }
147
143 status = ThreadStatus::Ready; 148 status = ThreadStatus::Ready;
144 149
145 ChangeScheduler(); 150 ChangeScheduler();
@@ -391,6 +396,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
391 return wakeup_callback(reason, std::move(thread), std::move(object), index); 396 return wakeup_callback(reason, std::move(thread), std::move(object), index);
392} 397}
393 398
399void Thread::SetActivity(ThreadActivity value) {
400 activity = value;
401
402 if (value == ThreadActivity::Paused) {
403 // Set status if not waiting
404 if (status == ThreadStatus::Ready) {
405 status = ThreadStatus::Paused;
406 } else if (status == ThreadStatus::Running) {
407 status = ThreadStatus::Paused;
408 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
409 }
410 } else if (status == ThreadStatus::Paused) {
411 // Ready to reschedule
412 ResumeFromWait();
413 }
414}
415
394//////////////////////////////////////////////////////////////////////////////////////////////////// 416////////////////////////////////////////////////////////////////////////////////////////////////////
395 417
396/** 418/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 77aec099a..fe5398d56 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -45,6 +45,7 @@ enum ThreadProcessorId : s32 {
45enum class ThreadStatus { 45enum class ThreadStatus {
46 Running, ///< Currently running 46 Running, ///< Currently running
47 Ready, ///< Ready to run 47 Ready, ///< Ready to run
48 Paused, ///< Paused by SetThreadActivity or debug
48 WaitHLEEvent, ///< Waiting for hle event to finish 49 WaitHLEEvent, ///< Waiting for hle event to finish
49 WaitSleep, ///< Waiting due to a SleepThread SVC 50 WaitSleep, ///< Waiting due to a SleepThread SVC
50 WaitIPC, ///< Waiting for the reply from an IPC request 51 WaitIPC, ///< Waiting for the reply from an IPC request
@@ -61,6 +62,11 @@ enum class ThreadWakeupReason {
61 Timeout // The thread was woken up due to a wait timeout. 62 Timeout // The thread was woken up due to a wait timeout.
62}; 63};
63 64
65enum class ThreadActivity : u32 {
66 Normal = 0,
67 Paused = 1,
68};
69
64class Thread final : public WaitObject { 70class Thread final : public WaitObject {
65public: 71public:
66 using TLSMemory = std::vector<u8>; 72 using TLSMemory = std::vector<u8>;
@@ -151,7 +157,7 @@ public:
151 * Gets the thread's thread ID 157 * Gets the thread's thread ID
152 * @return The thread's ID 158 * @return The thread's ID
153 */ 159 */
154 u32 GetThreadID() const { 160 u64 GetThreadID() const {
155 return thread_id; 161 return thread_id;
156 } 162 }
157 163
@@ -371,6 +377,12 @@ public:
371 return affinity_mask; 377 return affinity_mask;
372 } 378 }
373 379
380 ThreadActivity GetActivity() const {
381 return activity;
382 }
383
384 void SetActivity(ThreadActivity value);
385
374private: 386private:
375 explicit Thread(KernelCore& kernel); 387 explicit Thread(KernelCore& kernel);
376 ~Thread() override; 388 ~Thread() override;
@@ -379,7 +391,7 @@ private:
379 391
380 Core::ARM_Interface::ThreadContext context{}; 392 Core::ARM_Interface::ThreadContext context{};
381 393
382 u32 thread_id = 0; 394 u64 thread_id = 0;
383 395
384 ThreadStatus status = ThreadStatus::Dormant; 396 ThreadStatus status = ThreadStatus::Dormant;
385 397
@@ -439,6 +451,8 @@ private:
439 TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); 451 TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
440 452
441 std::string name; 453 std::string name;
454
455 ThreadActivity activity = ThreadActivity::Normal;
442}; 456};
443 457
444/** 458/**
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index f39e096ca..10ad94aa6 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -190,6 +190,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
190 vma.type = VMAType::Free; 190 vma.type = VMAType::Free;
191 vma.permissions = VMAPermission::None; 191 vma.permissions = VMAPermission::None;
192 vma.state = MemoryState::Unmapped; 192 vma.state = MemoryState::Unmapped;
193 vma.attribute = MemoryAttribute::None;
193 194
194 vma.backing_block = nullptr; 195 vma.backing_block = nullptr;
195 vma.offset = 0; 196 vma.offset = 0;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index f83730cd6..d13ce4dca 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -8,6 +8,7 @@
8#include <stack> 8#include <stack>
9#include "audio_core/audio_renderer.h" 9#include "audio_core/audio_renderer.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/savedata_factory.h"
11#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
@@ -19,6 +20,7 @@
19#include "core/hle/service/am/applet_ae.h" 20#include "core/hle/service/am/applet_ae.h"
20#include "core/hle/service/am/applet_oe.h" 21#include "core/hle/service/am/applet_oe.h"
21#include "core/hle/service/am/applets/applets.h" 22#include "core/hle/service/am/applets/applets.h"
23#include "core/hle/service/am/applets/profile_select.h"
22#include "core/hle/service/am/applets/software_keyboard.h" 24#include "core/hle/service/am/applets/software_keyboard.h"
23#include "core/hle/service/am/applets/stub_applet.h" 25#include "core/hle/service/am/applets/stub_applet.h"
24#include "core/hle/service/am/idle.h" 26#include "core/hle/service/am/idle.h"
@@ -39,6 +41,7 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
39constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; 41constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
40 42
41enum class AppletId : u32 { 43enum class AppletId : u32 {
44 ProfileSelect = 0x10,
42 SoftwareKeyboard = 0x11, 45 SoftwareKeyboard = 0x11,
43}; 46};
44 47
@@ -71,10 +74,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
71IWindowController::~IWindowController() = default; 74IWindowController::~IWindowController() = default;
72 75
73void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { 76void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
74 LOG_WARNING(Service_AM, "(STUBBED) called"); 77 const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID();
78
79 LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
80
75 IPC::ResponseBuilder rb{ctx, 4}; 81 IPC::ResponseBuilder rb{ctx, 4};
76 rb.Push(RESULT_SUCCESS); 82 rb.Push(RESULT_SUCCESS);
77 rb.Push<u64>(0); 83 rb.Push<u64>(process_id);
78} 84}
79 85
80void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { 86void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
@@ -772,6 +778,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
772 778
773static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { 779static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
774 switch (id) { 780 switch (id) {
781 case AppletId::ProfileSelect:
782 return std::make_shared<Applets::ProfileSelect>();
775 case AppletId::SoftwareKeyboard: 783 case AppletId::SoftwareKeyboard:
776 return std::make_shared<Applets::SoftwareKeyboard>(); 784 return std::make_shared<Applets::SoftwareKeyboard>();
777 default: 785 default:
@@ -858,8 +866,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
858 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, 866 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
859 {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"}, 867 {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
860 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 868 {24, nullptr, "GetLaunchStorageInfoForDebug"},
861 {25, nullptr, "ExtendSaveData"}, 869 {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
862 {26, nullptr, "GetSaveDataSize"}, 870 {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
863 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, 871 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
864 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, 872 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
865 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, 873 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
@@ -1036,6 +1044,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
1036 rb.Push<u64>(0); 1044 rb.Push<u64>(0);
1037} 1045}
1038 1046
1047void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
1048 IPC::RequestParser rp{ctx};
1049 const auto type{rp.PopRaw<FileSys::SaveDataType>()};
1050 rp.Skip(1, false);
1051 const auto user_id{rp.PopRaw<u128>()};
1052 const auto new_normal_size{rp.PopRaw<u64>()};
1053 const auto new_journal_size{rp.PopRaw<u64>()};
1054
1055 LOG_DEBUG(Service_AM,
1056 "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
1057 "new_journal={:016X}",
1058 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
1059
1060 FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
1061 {new_normal_size, new_journal_size});
1062
1063 IPC::ResponseBuilder rb{ctx, 4};
1064 rb.Push(RESULT_SUCCESS);
1065
1066 // The following value is used upon failure to help the system recover.
1067 // Since we always succeed, this should be 0.
1068 rb.Push<u64>(0);
1069}
1070
1071void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
1072 IPC::RequestParser rp{ctx};
1073 const auto type{rp.PopRaw<FileSys::SaveDataType>()};
1074 rp.Skip(1, false);
1075 const auto user_id{rp.PopRaw<u128>()};
1076
1077 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
1078 user_id[1], user_id[0]);
1079
1080 const auto size =
1081 FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
1082
1083 IPC::ResponseBuilder rb{ctx, 6};
1084 rb.Push(RESULT_SUCCESS);
1085 rb.Push(size.normal);
1086 rb.Push(size.journal);
1087}
1088
1039void InstallInterfaces(SM::ServiceManager& service_manager, 1089void InstallInterfaces(SM::ServiceManager& service_manager,
1040 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 1090 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
1041 auto message_queue = std::make_shared<AppletMessageQueue>(); 1091 auto message_queue = std::make_shared<AppletMessageQueue>();
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 34c45fadf..b6113cfdd 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -206,6 +206,8 @@ private:
206 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); 206 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
207 void NotifyRunning(Kernel::HLERequestContext& ctx); 207 void NotifyRunning(Kernel::HLERequestContext& ctx);
208 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); 208 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
209 void ExtendSaveData(Kernel::HLERequestContext& ctx);
210 void GetSaveDataSize(Kernel::HLERequestContext& ctx);
209 void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 211 void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
210 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 212 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
211 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); 213 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
new file mode 100644
index 000000000..4c7b45454
--- /dev/null
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -0,0 +1,77 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6
7#include "common/assert.h"
8#include "common/string_util.h"
9#include "core/core.h"
10#include "core/frontend/applets/software_keyboard.h"
11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/profile_select.h"
13
14namespace Service::AM::Applets {
15
16constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
17
18ProfileSelect::ProfileSelect() = default;
19ProfileSelect::~ProfileSelect() = default;
20
21void ProfileSelect::Initialize() {
22 complete = false;
23 status = RESULT_SUCCESS;
24 final_data.clear();
25
26 Applet::Initialize();
27
28 const auto user_config_storage = broker.PopNormalDataToApplet();
29 ASSERT(user_config_storage != nullptr);
30 const auto& user_config = user_config_storage->GetData();
31
32 ASSERT(user_config.size() >= sizeof(UserSelectionConfig));
33 std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig));
34}
35
36bool ProfileSelect::TransactionComplete() const {
37 return complete;
38}
39
40ResultCode ProfileSelect::GetStatus() const {
41 return status;
42}
43
44void ProfileSelect::ExecuteInteractive() {
45 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
46}
47
48void ProfileSelect::Execute() {
49 if (complete) {
50 broker.PushNormalDataFromApplet(IStorage{final_data});
51 return;
52 }
53
54 const auto& frontend{Core::System::GetInstance().GetProfileSelector()};
55
56 frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
57}
58
59void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
60 UserSelectionOutput output{};
61
62 if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
63 output.result = 0;
64 output.uuid_selected = uuid->uuid;
65 } else {
66 status = ERR_USER_CANCELLED_SELECTION;
67 output.result = ERR_USER_CANCELLED_SELECTION.raw;
68 output.uuid_selected = Account::INVALID_UUID;
69 }
70
71 final_data = std::vector<u8>(sizeof(UserSelectionOutput));
72 std::memcpy(final_data.data(), &output, final_data.size());
73 broker.PushNormalDataFromApplet(IStorage{final_data});
74 broker.SignalStateChanged();
75}
76
77} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h
new file mode 100644
index 000000000..787485f22
--- /dev/null
+++ b/src/core/hle/service/am/applets/profile_select.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "common/common_funcs.h"
10#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/am/applets/applets.h"
12
13namespace Service::AM::Applets {
14
15struct UserSelectionConfig {
16 // TODO(DarkLordZach): RE this structure
17 // It seems to be flags and the like that determine the UI of the applet on the switch... from
18 // my research this is safe to ignore for now.
19 INSERT_PADDING_BYTES(0xA0);
20};
21static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size.");
22
23struct UserSelectionOutput {
24 u64 result;
25 u128 uuid_selected;
26};
27static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");
28
29class ProfileSelect final : public Applet {
30public:
31 ProfileSelect();
32 ~ProfileSelect() override;
33
34 void Initialize() override;
35
36 bool TransactionComplete() const override;
37 ResultCode GetStatus() const override;
38 void ExecuteInteractive() override;
39 void Execute() override;
40
41 void SelectionComplete(std::optional<Account::UUID> uuid);
42
43private:
44 UserSelectionConfig config;
45 bool complete = false;
46 ResultCode status = RESULT_SUCCESS;
47 std::vector<u8> final_data;
48};
49
50} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index b1490e6fa..c6da2df43 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -8,18 +8,23 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/bis_factory.h" 10#include "core/file_sys/bis_factory.h"
11#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/errors.h" 12#include "core/file_sys/errors.h"
12#include "core/file_sys/mode.h" 13#include "core/file_sys/mode.h"
14#include "core/file_sys/partition_filesystem.h"
15#include "core/file_sys/patch_manager.h"
13#include "core/file_sys/registered_cache.h" 16#include "core/file_sys/registered_cache.h"
14#include "core/file_sys/romfs_factory.h" 17#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/savedata_factory.h" 18#include "core/file_sys/savedata_factory.h"
16#include "core/file_sys/sdmc_factory.h" 19#include "core/file_sys/sdmc_factory.h"
17#include "core/file_sys/vfs.h" 20#include "core/file_sys/vfs.h"
18#include "core/file_sys/vfs_offset.h" 21#include "core/file_sys/vfs_offset.h"
22#include "core/hle/kernel/process.h"
19#include "core/hle/service/filesystem/filesystem.h" 23#include "core/hle/service/filesystem/filesystem.h"
20#include "core/hle/service/filesystem/fsp_ldr.h" 24#include "core/hle/service/filesystem/fsp_ldr.h"
21#include "core/hle/service/filesystem/fsp_pr.h" 25#include "core/hle/service/filesystem/fsp_pr.h"
22#include "core/hle/service/filesystem/fsp_srv.h" 26#include "core/hle/service/filesystem/fsp_srv.h"
27#include "core/loader/loader.h"
23 28
24namespace Service::FileSystem { 29namespace Service::FileSystem {
25 30
@@ -28,6 +33,10 @@ namespace Service::FileSystem {
28// TODO(DarkLordZach): Eventually make this configurable in settings. 33// TODO(DarkLordZach): Eventually make this configurable in settings.
29constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; 34constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
30 35
36// A default size for normal/journal save data size if application control metadata cannot be found.
37// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
38constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
39
31static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 40static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
32 std::string_view dir_name_) { 41 std::string_view dir_name_) {
33 std::string dir_name(FileUtil::SanitizePath(dir_name_)); 42 std::string dir_name(FileUtil::SanitizePath(dir_name_));
@@ -341,6 +350,44 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
341 return sdmc_factory->Open(); 350 return sdmc_factory->Open();
342} 351}
343 352
353FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
354 if (save_data_factory == nullptr) {
355 return {0, 0};
356 }
357
358 const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
359
360 if (value.normal == 0 && value.journal == 0) {
361 FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
362
363 FileSys::NACP nacp;
364 const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
365
366 if (res != Loader::ResultStatus::Success) {
367 FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
368 auto [nacp_unique, discard] = pm.GetControlMetadata();
369
370 if (nacp_unique != nullptr) {
371 new_size = {nacp_unique->GetDefaultNormalSaveSize(),
372 nacp_unique->GetDefaultJournalSaveSize()};
373 }
374 } else {
375 new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
376 }
377
378 WriteSaveDataSize(type, title_id, user_id, new_size);
379 return new_size;
380 }
381
382 return value;
383}
384
385void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
386 FileSys::SaveDataSize new_value) {
387 if (save_data_factory != nullptr)
388 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
389}
390
344FileSys::RegisteredCacheUnion GetUnionContents() { 391FileSys::RegisteredCacheUnion GetUnionContents() {
345 return FileSys::RegisteredCacheUnion{ 392 return FileSys::RegisteredCacheUnion{
346 {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}}; 393 {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 965414be0..6fd5e7b23 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -21,9 +21,11 @@ class SDMCFactory;
21enum class ContentRecordType : u8; 21enum class ContentRecordType : u8;
22enum class Mode : u32; 22enum class Mode : u32;
23enum class SaveDataSpaceId : u8; 23enum class SaveDataSpaceId : u8;
24enum class SaveDataType : u8;
24enum class StorageId : u8; 25enum class StorageId : u8;
25 26
26struct SaveDataDescriptor; 27struct SaveDataDescriptor;
28struct SaveDataSize;
27} // namespace FileSys 29} // namespace FileSys
28 30
29namespace Service { 31namespace Service {
@@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
48ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); 50ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
49ResultVal<FileSys::VirtualDir> OpenSDMC(); 51ResultVal<FileSys::VirtualDir> OpenSDMC();
50 52
53FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
55 FileSys::SaveDataSize new_value);
56
51FileSys::RegisteredCacheUnion GetUnionContents(); 57FileSys::RegisteredCacheUnion GetUnionContents();
52 58
53FileSys::RegisteredCache* GetSystemNANDContents(); 59FileSys::RegisteredCache* GetSystemNANDContents();
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index d6829d0b8..75fdb861a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -339,52 +339,6 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
339 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index]; 339 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
340 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 340 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
341 341
342 if (hold_type == NpadHoldType::Horizontal) {
343 ControllerPadState state{};
344 AnalogPosition temp_lstick_entry{};
345 AnalogPosition temp_rstick_entry{};
346 if (controller_type == NPadControllerType::JoyLeft) {
347 state.d_down.Assign(pad_state.pad_states.d_left.Value());
348 state.d_left.Assign(pad_state.pad_states.d_up.Value());
349 state.d_right.Assign(pad_state.pad_states.d_down.Value());
350 state.d_up.Assign(pad_state.pad_states.d_right.Value());
351 state.l.Assign(pad_state.pad_states.l.Value() |
352 pad_state.pad_states.left_sl.Value());
353 state.r.Assign(pad_state.pad_states.r.Value() |
354 pad_state.pad_states.left_sr.Value());
355
356 state.zl.Assign(pad_state.pad_states.zl.Value());
357 state.plus.Assign(pad_state.pad_states.minus.Value());
358
359 temp_lstick_entry = pad_state.l_stick;
360 temp_rstick_entry = pad_state.r_stick;
361 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
362 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
363 temp_lstick_entry.y *= -1;
364 } else if (controller_type == NPadControllerType::JoyRight) {
365 state.x.Assign(pad_state.pad_states.a.Value());
366 state.a.Assign(pad_state.pad_states.b.Value());
367 state.b.Assign(pad_state.pad_states.y.Value());
368 state.y.Assign(pad_state.pad_states.b.Value());
369
370 state.l.Assign(pad_state.pad_states.l.Value() |
371 pad_state.pad_states.right_sl.Value());
372 state.r.Assign(pad_state.pad_states.r.Value() |
373 pad_state.pad_states.right_sr.Value());
374 state.zr.Assign(pad_state.pad_states.zr.Value());
375 state.plus.Assign(pad_state.pad_states.plus.Value());
376
377 temp_lstick_entry = pad_state.l_stick;
378 temp_rstick_entry = pad_state.r_stick;
379 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
380 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
381 temp_rstick_entry.x *= -1;
382 }
383 pad_state.pad_states.raw = state.raw;
384 pad_state.l_stick = temp_lstick_entry;
385 pad_state.r_stick = temp_rstick_entry;
386 }
387
388 libnx_entry.connection_status.raw = 0; 342 libnx_entry.connection_status.raw = 0;
389 343
390 switch (controller_type) { 344 switch (controller_type) {
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0838e303b..cfd67adc0 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -15,6 +15,10 @@
15#include "core/file_sys/control_metadata.h" 15#include "core/file_sys/control_metadata.h"
16#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
17 17
18namespace FileSys {
19class NACP;
20} // namespace FileSys
21
18namespace Kernel { 22namespace Kernel {
19struct AddressMapping; 23struct AddressMapping;
20class Process; 24class Process;
@@ -245,11 +249,11 @@ public:
245 } 249 }
246 250
247 /** 251 /**
248 * Get the developer of the application 252 * Get the control data (CNMT) of the application
249 * @param developer Reference to store the application developer into 253 * @param control Reference to store the application control data into
250 * @return ResultStatus result of function 254 * @return ResultStatus result of function
251 */ 255 */
252 virtual ResultStatus ReadDeveloper(std::string& developer) { 256 virtual ResultStatus ReadControlData(FileSys::NACP& control) {
253 return ResultStatus::ErrorNotImplemented; 257 return ResultStatus::ErrorNotImplemented;
254 } 258 }
255 259
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index b4ab88ae8..4d4b44571 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -152,10 +152,10 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
152 return ResultStatus::Success; 152 return ResultStatus::Success;
153} 153}
154 154
155ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) { 155ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
156 if (nacp_file == nullptr) 156 if (nacp_file == nullptr)
157 return ResultStatus::ErrorNoControl; 157 return ResultStatus::ErrorNoControl;
158 developer = nacp_file->GetDeveloperName(); 158 nacp = *nacp_file;
159 return ResultStatus::Success; 159 return ResultStatus::Success;
160} 160}
161} // namespace Loader 161} // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 2b1e0719b..32eb0193d 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -43,7 +43,7 @@ public:
43 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
45 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
46 ResultStatus ReadDeveloper(std::string& developer) override; 46 ResultStatus ReadControlData(FileSys::NACP& nacp) override;
47 47
48private: 48private:
49 std::unique_ptr<FileSys::NSP> nsp; 49 std::unique_ptr<FileSys::NSP> nsp;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index bd5a83b49..e67e43c69 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -121,10 +121,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
121 return ResultStatus::Success; 121 return ResultStatus::Success;
122} 122}
123 123
124ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) { 124ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
125 if (nacp_file == nullptr) 125 if (nacp_file == nullptr)
126 return ResultStatus::ErrorNoControl; 126 return ResultStatus::ErrorNoControl;
127 developer = nacp_file->GetDeveloperName(); 127 control = *nacp_file;
128 return ResultStatus::Success; 128 return ResultStatus::Success;
129} 129}
130
130} // namespace Loader 131} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 15d1b1a23..9d3923f62 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -43,7 +43,7 @@ public:
43 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
45 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
46 ResultStatus ReadDeveloper(std::string& developer) override; 46 ResultStatus ReadControlData(FileSys::NACP& control) override;
47 47
48private: 48private:
49 std::unique_ptr<FileSys::XCI> xci; 49 std::unique_ptr<FileSys::XCI> xci;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 643afdee8..e9166dbd9 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -187,6 +187,7 @@ T Read(const VAddr vaddr) {
187 default: 187 default:
188 UNREACHABLE(); 188 UNREACHABLE();
189 } 189 }
190 return {};
190} 191}
191 192
192template <typename T> 193template <typename T>
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 25bb7604a..0faff6fdf 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -164,6 +164,7 @@ public:
164 return 3; 164 return 3;
165 default: 165 default:
166 UNREACHABLE(); 166 UNREACHABLE();
167 return 1;
167 } 168 }
168 } 169 }
169 170
@@ -871,6 +872,7 @@ public:
871 return 4; 872 return 4;
872 } 873 }
873 UNREACHABLE(); 874 UNREACHABLE();
875 return 1;
874 } 876 }
875 877
876 GPUVAddr StartAddress() const { 878 GPUVAddr StartAddress() const {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 2efeb6e1a..e53c77f2b 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -1049,7 +1049,7 @@ union Instruction {
1049 BitField<49, 1, u64> nodep_flag; 1049 BitField<49, 1, u64> nodep_flag;
1050 BitField<50, 3, u64> component_mask_selector; 1050 BitField<50, 3, u64> component_mask_selector;
1051 BitField<53, 4, u64> texture_info; 1051 BitField<53, 4, u64> texture_info;
1052 BitField<60, 1, u64> fp32_flag; 1052 BitField<59, 1, u64> fp32_flag;
1053 1053
1054 TextureType GetTextureType() const { 1054 TextureType GetTextureType() const {
1055 // The TEXS instruction has a weird encoding for the texture type. 1055 // The TEXS instruction has a weird encoding for the texture type.
@@ -1065,6 +1065,7 @@ union Instruction {
1065 LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}", 1065 LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
1066 static_cast<u32>(texture_info.Value())); 1066 static_cast<u32>(texture_info.Value()));
1067 UNREACHABLE(); 1067 UNREACHABLE();
1068 return TextureType::Texture1D;
1068 } 1069 }
1069 1070
1070 TextureProcessMode GetTextureProcessMode() const { 1071 TextureProcessMode GetTextureProcessMode() const {
@@ -1145,6 +1146,7 @@ union Instruction {
1145 LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}", 1146 LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
1146 static_cast<u32>(texture_info.Value())); 1147 static_cast<u32>(texture_info.Value()));
1147 UNREACHABLE(); 1148 UNREACHABLE();
1149 return TextureType::Texture1D;
1148 } 1150 }
1149 1151
1150 TextureProcessMode GetTextureProcessMode() const { 1152 TextureProcessMode GetTextureProcessMode() const {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 88c45a423..08cf6268f 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -102,6 +102,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
102 return 1; 102 return 1;
103 default: 103 default:
104 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); 104 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
105 return 1;
105 } 106 }
106} 107}
107 108
@@ -119,6 +120,7 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
119 return 2; 120 return 2;
120 default: 121 default:
121 UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format)); 122 UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
123 return 1;
122 } 124 }
123} 125}
124 126
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 9c55e9f1e..64f75db43 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -171,6 +171,7 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b)
171 171
172 default: 172 default:
173 UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", static_cast<u32>(operation)); 173 UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", static_cast<u32>(operation));
174 return 0;
174 } 175 }
175} 176}
176 177
@@ -268,6 +269,7 @@ bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value)
268 return value != 0; 269 return value != 0;
269 } 270 }
270 UNREACHABLE(); 271 UNREACHABLE();
272 return true;
271} 273}
272 274
273} // namespace Tegra 275} // namespace Tegra
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index a310491a8..47e76d8fe 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -192,6 +192,7 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
192 return linear_to_morton_fns[static_cast<std::size_t>(format)]; 192 return linear_to_morton_fns[static_cast<std::size_t>(format)];
193 } 193 }
194 UNREACHABLE(); 194 UNREACHABLE();
195 return morton_to_linear_fns[static_cast<std::size_t>(format)];
195} 196}
196 197
197/// 8x8 Z-Order coordinate from 2D coordinates 198/// 8x8 Z-Order coordinate from 2D coordinates
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 1482cdb40..94223f45f 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); 27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
28} 28}
29 29
30void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
31 const Layout::FramebufferLayout& layout) {
32 if (renderer_settings.screenshot_requested) {
33 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
34 return;
35 }
36 renderer_settings.screenshot_bits = data;
37 renderer_settings.screenshot_complete_callback = std::move(callback);
38 renderer_settings.screenshot_framebuffer_layout = layout;
39 renderer_settings.screenshot_requested = true;
40}
41
30} // namespace VideoCore 42} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 669e26e15..1d54c3723 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -9,6 +9,7 @@
9#include <optional> 9#include <optional>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/frontend/emu_window.h"
12#include "video_core/gpu.h" 13#include "video_core/gpu.h"
13#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
14 15
@@ -21,6 +22,12 @@ namespace VideoCore {
21struct RendererSettings { 22struct RendererSettings {
22 std::atomic_bool use_framelimiter{false}; 23 std::atomic_bool use_framelimiter{false};
23 std::atomic_bool set_background_color{false}; 24 std::atomic_bool set_background_color{false};
25
26 // Screenshot
27 std::atomic<bool> screenshot_requested{false};
28 void* screenshot_bits;
29 std::function<void()> screenshot_complete_callback;
30 Layout::FramebufferLayout screenshot_framebuffer_layout;
24}; 31};
25 32
26class RendererBase : NonCopyable { 33class RendererBase : NonCopyable {
@@ -57,9 +64,29 @@ public:
57 return *rasterizer; 64 return *rasterizer;
58 } 65 }
59 66
67 Core::Frontend::EmuWindow& GetRenderWindow() {
68 return render_window;
69 }
70
71 const Core::Frontend::EmuWindow& GetRenderWindow() const {
72 return render_window;
73 }
74
75 RendererSettings& Settings() {
76 return renderer_settings;
77 }
78
79 const RendererSettings& Settings() const {
80 return renderer_settings;
81 }
82
60 /// Refreshes the settings common to all renderers 83 /// Refreshes the settings common to all renderers
61 void RefreshBaseSettings(); 84 void RefreshBaseSettings();
62 85
86 /// Request a screenshot of the next frame
87 void RequestScreenshot(void* data, std::function<void()> callback,
88 const Layout::FramebufferLayout& layout);
89
63protected: 90protected:
64 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. 91 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
65 std::unique_ptr<RasterizerInterface> rasterizer; 92 std::unique_ptr<RasterizerInterface> rasterizer;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 5f4cdd119..7ea07631a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -101,8 +101,18 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
101 params.srgb_conversion = config.tic.IsSrgbConversionEnabled(); 101 params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
102 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), 102 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
103 params.srgb_conversion); 103 params.srgb_conversion);
104
105 if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) {
106 // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled,
107 // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also
108 // causes GetFormatType to properly return 'Depth' below).
109 params.pixel_format = PixelFormat::Z16;
110 }
111
104 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 112 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
105 params.type = GetFormatType(params.pixel_format); 113 params.type = GetFormatType(params.pixel_format);
114 UNIMPLEMENTED_IF(params.type == SurfaceType::ColorTexture && config.tsc.depth_compare_enabled);
115
106 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); 116 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
107 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); 117 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
108 params.unaligned_height = config.tic.Height(); 118 params.unaligned_height = config.tic.Height();
@@ -257,7 +267,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
257 {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI 267 {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI
258 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F 268 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
259 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U 269 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U
260 {GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI 270 {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI
261 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, 271 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
262 false}, // R11FG11FB10F 272 false}, // R11FG11FB10F
263 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI 273 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index b4ef6030d..de3671acf 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -67,6 +67,7 @@ public:
67 6, "ShaderTrianglesAdjacency"); 67 6, "ShaderTrianglesAdjacency");
68 default: 68 default:
69 UNREACHABLE_MSG("Unknown primitive mode."); 69 UNREACHABLE_MSG("Unknown primitive mode.");
70 return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
70 } 71 }
71 } 72 }
72 73
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index a7d8c404d..76d9b3538 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -364,6 +364,7 @@ public:
364 return value; 364 return value;
365 default: 365 default:
366 UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size)); 366 UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
367 return value;
367 } 368 }
368 } 369 }
369 370
@@ -626,6 +627,7 @@ public:
626 return "floatBitsToInt(" + value + ')'; 627 return "floatBitsToInt(" + value + ')';
627 } else { 628 } else {
628 UNREACHABLE(); 629 UNREACHABLE();
630 return value;
629 } 631 }
630 } 632 }
631 633
@@ -1753,7 +1755,7 @@ private:
1753 instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL; 1755 instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL;
1754 1756
1755 constexpr std::array<const char*, 4> coord_container{ 1757 constexpr std::array<const char*, 4> coord_container{
1756 {"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}}; 1758 {"", "int coords = (", "ivec2 coords = ivec2(", "ivec3 coords = ivec3("}};
1757 1759
1758 std::string coord = coord_container[total_coord_count]; 1760 std::string coord = coord_container[total_coord_count];
1759 1761
@@ -2062,6 +2064,8 @@ private:
2062 std::to_string(instr.alu.GetSignedImm20_20())}; 2064 std::to_string(instr.alu.GetSignedImm20_20())};
2063 default: 2065 default:
2064 UNREACHABLE(); 2066 UNREACHABLE();
2067 return {regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2068 std::to_string(instr.alu.GetSignedImm20_20())};
2065 } 2069 }
2066 }(); 2070 }();
2067 const std::string offset = '(' + packed_shift + " & 0xff)"; 2071 const std::string offset = '(' + packed_shift + " & 0xff)";
@@ -3312,6 +3316,7 @@ private:
3312 return std::to_string(instr.r2p.immediate_mask); 3316 return std::to_string(instr.r2p.immediate_mask);
3313 default: 3317 default:
3314 UNREACHABLE(); 3318 UNREACHABLE();
3319 return std::to_string(instr.r2p.immediate_mask);
3315 } 3320 }
3316 }(); 3321 }();
3317 const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + 3322 const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
@@ -3775,7 +3780,10 @@ private:
3775 } 3780 }
3776 break; 3781 break;
3777 } 3782 }
3778 default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); } 3783 default: {
3784 UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
3785 break;
3786 }
3779 } 3787 }
3780 3788
3781 break; 3789 break;
@@ -3930,4 +3938,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
3930 return {}; 3938 return {};
3931} 3939}
3932 3940
3933} // namespace OpenGL::GLShader::Decompiler \ No newline at end of file 3941} // namespace OpenGL::GLShader::Decompiler
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4fd0d66c5..235732d86 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers(
138 138
139 // Load the framebuffer from memory, draw it to the screen, and swap buffers 139 // Load the framebuffer from memory, draw it to the screen, and swap buffers
140 LoadFBToScreenInfo(*framebuffer); 140 LoadFBToScreenInfo(*framebuffer);
141 DrawScreen(); 141
142 if (renderer_settings.screenshot_requested)
143 CaptureScreenshot();
144
145 DrawScreen(render_window.GetFramebufferLayout());
146
142 render_window.SwapBuffers(); 147 render_window.SwapBuffers();
143 } 148 }
144 149
@@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
383/** 388/**
384 * Draws the emulated screens to the emulator window. 389 * Draws the emulated screens to the emulator window.
385 */ 390 */
386void RendererOpenGL::DrawScreen() { 391void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
387 if (renderer_settings.set_background_color) { 392 if (renderer_settings.set_background_color) {
388 // Update background color before drawing 393 // Update background color before drawing
389 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 394 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
390 0.0f); 395 0.0f);
391 } 396 }
392 397
393 const auto& layout = render_window.GetFramebufferLayout();
394 const auto& screen = layout.screen; 398 const auto& screen = layout.screen;
395 399
396 glViewport(0, 0, layout.width, layout.height); 400 glViewport(0, 0, layout.width, layout.height);
@@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() {
414/// Updates the framerate 418/// Updates the framerate
415void RendererOpenGL::UpdateFramerate() {} 419void RendererOpenGL::UpdateFramerate() {}
416 420
421void RendererOpenGL::CaptureScreenshot() {
422 // Draw the current frame to the screenshot framebuffer
423 screenshot_framebuffer.Create();
424 GLuint old_read_fb = state.draw.read_framebuffer;
425 GLuint old_draw_fb = state.draw.draw_framebuffer;
426 state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
427 state.Apply();
428
429 Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
430
431 GLuint renderbuffer;
432 glGenRenderbuffers(1, &renderbuffer);
433 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
434 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
435 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
436
437 DrawScreen(layout);
438
439 glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
440 renderer_settings.screenshot_bits);
441
442 screenshot_framebuffer.Release();
443 state.draw.read_framebuffer = old_read_fb;
444 state.draw.draw_framebuffer = old_draw_fb;
445 state.Apply();
446 glDeleteRenderbuffers(1, &renderbuffer);
447
448 renderer_settings.screenshot_complete_callback();
449 renderer_settings.screenshot_requested = false;
450}
451
417static const char* GetSource(GLenum source) { 452static const char* GetSource(GLenum source) {
418#define RET(s) \ 453#define RET(s) \
419 case GL_DEBUG_SOURCE_##s: \ 454 case GL_DEBUG_SOURCE_##s: \
@@ -427,6 +462,7 @@ static const char* GetSource(GLenum source) {
427 RET(OTHER); 462 RET(OTHER);
428 default: 463 default:
429 UNREACHABLE(); 464 UNREACHABLE();
465 return "Unknown source";
430 } 466 }
431#undef RET 467#undef RET
432} 468}
@@ -445,6 +481,7 @@ static const char* GetType(GLenum type) {
445 RET(MARKER); 481 RET(MARKER);
446 default: 482 default:
447 UNREACHABLE(); 483 UNREACHABLE();
484 return "Unknown type";
448 } 485 }
449#undef RET 486#undef RET
450} 487}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index c0868c0e4..b85cc262f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -16,6 +16,10 @@ namespace Core::Frontend {
16class EmuWindow; 16class EmuWindow;
17} 17}
18 18
19namespace Layout {
20struct FramebufferLayout;
21}
22
19namespace OpenGL { 23namespace OpenGL {
20 24
21/// Structure used for storing information about the textures for the Switch screen 25/// Structure used for storing information about the textures for the Switch screen
@@ -66,10 +70,12 @@ private:
66 70
67 void ConfigureFramebufferTexture(TextureInfo& texture, 71 void ConfigureFramebufferTexture(TextureInfo& texture,
68 const Tegra::FramebufferConfig& framebuffer); 72 const Tegra::FramebufferConfig& framebuffer);
69 void DrawScreen(); 73 void DrawScreen(const Layout::FramebufferLayout& layout);
70 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); 74 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
71 void UpdateFramerate(); 75 void UpdateFramerate();
72 76
77 void CaptureScreenshot();
78
73 // Loads framebuffer from emulated memory into the display information structure 79 // Loads framebuffer from emulated memory into the display information structure
74 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); 80 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
75 // Fills active OpenGL texture with the given RGBA color. 81 // Fills active OpenGL texture with the given RGBA color.
@@ -82,6 +88,7 @@ private:
82 OGLVertexArray vertex_array; 88 OGLVertexArray vertex_array;
83 OGLBuffer vertex_buffer; 89 OGLBuffer vertex_buffer;
84 OGLProgram shader; 90 OGLProgram shader;
91 OGLFramebuffer screenshot_framebuffer;
85 92
86 /// Display information for Switch screen 93 /// Display information for Switch screen
87 ScreenInfo screen_info; 94 ScreenInfo screen_info;
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 9582dd2ca..a97b1562b 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -65,6 +65,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
65 default: 65 default:
66 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 66 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
67 UNREACHABLE(); 67 UNREACHABLE();
68 return PixelFormat::S8Z24;
68 } 69 }
69} 70}
70 71
@@ -141,6 +142,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
141 default: 142 default:
142 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 143 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
143 UNREACHABLE(); 144 UNREACHABLE();
145 return PixelFormat::RGBA8_SRGB;
144 } 146 }
145} 147}
146 148
@@ -327,6 +329,7 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
327 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), 329 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
328 static_cast<u32>(component_type)); 330 static_cast<u32>(component_type));
329 UNREACHABLE(); 331 UNREACHABLE();
332 return PixelFormat::ABGR8U;
330 } 333 }
331} 334}
332 335
@@ -346,6 +349,7 @@ ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
346 default: 349 default:
347 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); 350 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
348 UNREACHABLE(); 351 UNREACHABLE();
352 return ComponentType::UNorm;
349 } 353 }
350} 354}
351 355
@@ -393,6 +397,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
393 default: 397 default:
394 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 398 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
395 UNREACHABLE(); 399 UNREACHABLE();
400 return ComponentType::UNorm;
396 } 401 }
397} 402}
398 403
@@ -403,6 +408,7 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat
403 default: 408 default:
404 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 409 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
405 UNREACHABLE(); 410 UNREACHABLE();
411 return PixelFormat::ABGR8U;
406 } 412 }
407} 413}
408 414
@@ -418,6 +424,7 @@ ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
418 default: 424 default:
419 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 425 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
420 UNREACHABLE(); 426 UNREACHABLE();
427 return ComponentType::UNorm;
421 } 428 }
422} 429}
423 430
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index bbae9285f..5db75de22 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -226,7 +226,7 @@ u32 BytesPerPixel(TextureFormat format) {
226 return 8; 226 return 8;
227 default: 227 default:
228 UNIMPLEMENTED_MSG("Format not implemented"); 228 UNIMPLEMENTED_MSG("Format not implemented");
229 break; 229 return 1;
230 } 230 }
231} 231}
232 232
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 07e3a7d24..f7de3471b 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,6 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include "core/core.h"
7#include "core/settings.h"
6#include "video_core/renderer_base.h" 8#include "video_core/renderer_base.h"
7#include "video_core/renderer_opengl/renderer_opengl.h" 9#include "video_core/renderer_opengl/renderer_opengl.h"
8#include "video_core/video_core.h" 10#include "video_core/video_core.h"
@@ -13,4 +15,10 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
13 return std::make_unique<OpenGL::RendererOpenGL>(emu_window); 15 return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
14} 16}
15 17
18u16 GetResolutionScaleFactor(const RendererBase& renderer) {
19 return !Settings::values.resolution_factor
20 ? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
21 : Settings::values.resolution_factor;
22}
23
16} // namespace VideoCore 24} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index f79f85dfe..5b373bcb1 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -22,4 +22,6 @@ class RendererBase;
22 */ 22 */
23std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); 23std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
24 24
25u16 GetResolutionScaleFactor(const RendererBase& renderer);
26
25} // namespace VideoCore 27} // namespace VideoCore
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 3232aa8fb..17ecaafde 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -7,6 +7,8 @@ add_executable(yuzu
7 Info.plist 7 Info.plist
8 about_dialog.cpp 8 about_dialog.cpp
9 about_dialog.h 9 about_dialog.h
10 applets/profile_select.cpp
11 applets/profile_select.h
10 applets/software_keyboard.cpp 12 applets/software_keyboard.cpp
11 applets/software_keyboard.h 13 applets/software_keyboard.h
12 bootmanager.cpp 14 bootmanager.cpp
@@ -31,6 +33,8 @@ add_executable(yuzu
31 configuration/configure_input.h 33 configuration/configure_input.h
32 configuration/configure_input_player.cpp 34 configuration/configure_input_player.cpp
33 configuration/configure_input_player.h 35 configuration/configure_input_player.h
36 configuration/configure_input_simple.cpp
37 configuration/configure_input_simple.h
34 configuration/configure_mouse_advanced.cpp 38 configuration/configure_mouse_advanced.cpp
35 configuration/configure_mouse_advanced.h 39 configuration/configure_mouse_advanced.h
36 configuration/configure_system.cpp 40 configuration/configure_system.cpp
@@ -87,6 +91,7 @@ set(UIS
87 configuration/configure_graphics.ui 91 configuration/configure_graphics.ui
88 configuration/configure_input.ui 92 configuration/configure_input.ui
89 configuration/configure_input_player.ui 93 configuration/configure_input_player.ui
94 configuration/configure_input_simple.ui
90 configuration/configure_mouse_advanced.ui 95 configuration/configure_mouse_advanced.ui
91 configuration/configure_per_general.ui 96 configuration/configure_per_general.ui
92 configuration/configure_system.ui 97 configuration/configure_system.ui
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
new file mode 100644
index 000000000..5c1b65a2c
--- /dev/null
+++ b/src/yuzu/applets/profile_select.cpp
@@ -0,0 +1,168 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <mutex>
6#include <QDialogButtonBox>
7#include <QLabel>
8#include <QLineEdit>
9#include <QScrollArea>
10#include <QStandardItemModel>
11#include <QVBoxLayout>
12#include "common/file_util.h"
13#include "common/string_util.h"
14#include "core/hle/lock.h"
15#include "yuzu/applets/profile_select.h"
16#include "yuzu/main.h"
17
18// Same backup JPEG used by acc IProfile::GetImage if no jpeg found
19constexpr std::array<u8, 107> backup_jpeg{
20 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
21 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
22 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
23 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
24 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
25 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
26 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
27};
28
29QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
30 return QtProfileSelectionDialog::tr(
31 "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
32 "00112233-4455-6677-8899-AABBCCDDEEFF))")
33 .arg(username, QString::fromStdString(uuid.FormatSwitch()));
34}
35
36QString GetImagePath(Service::Account::UUID uuid) {
37 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
38 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
39 return QString::fromStdString(path);
40}
41
42QPixmap GetIcon(Service::Account::UUID uuid) {
43 QPixmap icon{GetImagePath(uuid)};
44
45 if (!icon) {
46 icon.fill(Qt::black);
47 icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size()));
48 }
49
50 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
51}
52
53QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
54 : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
55 outer_layout = new QVBoxLayout;
56
57 instruction_label = new QLabel(tr("Select a user:"));
58
59 scroll_area = new QScrollArea;
60
61 buttons = new QDialogButtonBox;
62 buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
63 buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole);
64
65 connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept);
66 connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject);
67
68 outer_layout->addWidget(instruction_label);
69 outer_layout->addWidget(scroll_area);
70 outer_layout->addWidget(buttons);
71
72 layout = new QVBoxLayout;
73 tree_view = new QTreeView;
74 item_model = new QStandardItemModel(tree_view);
75 tree_view->setModel(item_model);
76
77 tree_view->setAlternatingRowColors(true);
78 tree_view->setSelectionMode(QHeaderView::SingleSelection);
79 tree_view->setSelectionBehavior(QHeaderView::SelectRows);
80 tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
81 tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
82 tree_view->setSortingEnabled(true);
83 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
84 tree_view->setUniformRowHeights(true);
85 tree_view->setIconSize({64, 64});
86 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
87
88 item_model->insertColumns(0, 1);
89 item_model->setHeaderData(0, Qt::Horizontal, "Users");
90
91 // We must register all custom types with the Qt Automoc system so that we are able to use it
92 // with signals/slots. In this case, QList falls under the umbrells of custom types.
93 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
94
95 layout->setContentsMargins(0, 0, 0, 0);
96 layout->setSpacing(0);
97 layout->addWidget(tree_view);
98
99 scroll_area->setLayout(layout);
100
101 connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
102
103 const auto& profiles = profile_manager->GetAllUsers();
104 for (const auto& user : profiles) {
105 Service::Account::ProfileBase profile;
106 if (!profile_manager->GetProfileBase(user, profile))
107 continue;
108
109 const auto username = Common::StringFromFixedZeroTerminatedBuffer(
110 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
111
112 list_items.push_back(QList<QStandardItem*>{new QStandardItem{
113 GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}});
114 }
115
116 for (const auto& item : list_items)
117 item_model->appendRow(item);
118
119 setLayout(outer_layout);
120 setWindowTitle(tr("Profile Selector"));
121 resize(550, 400);
122}
123
124QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
125
126void QtProfileSelectionDialog::accept() {
127 ok = true;
128 QDialog::accept();
129}
130
131void QtProfileSelectionDialog::reject() {
132 ok = false;
133 user_index = 0;
134 QDialog::reject();
135}
136
137bool QtProfileSelectionDialog::GetStatus() const {
138 return ok;
139}
140
141u32 QtProfileSelectionDialog::GetIndex() const {
142 return user_index;
143}
144
145void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) {
146 user_index = index.row();
147}
148
149QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
150 connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent,
151 &GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
152 connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this,
153 &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection);
154}
155
156QtProfileSelector::~QtProfileSelector() = default;
157
158void QtProfileSelector::SelectProfile(
159 std::function<void(std::optional<Service::Account::UUID>)> callback) const {
160 this->callback = std::move(callback);
161 emit MainWindowSelectProfile();
162}
163
164void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) {
165 // Acquire the HLE mutex
166 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
167 callback(uuid);
168}
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h
new file mode 100644
index 000000000..868573324
--- /dev/null
+++ b/src/yuzu/applets/profile_select.h
@@ -0,0 +1,73 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include <QDialog>
9#include <QList>
10#include "core/frontend/applets/profile_select.h"
11
12class GMainWindow;
13class QDialogButtonBox;
14class QGraphicsScene;
15class QLabel;
16class QScrollArea;
17class QStandardItem;
18class QStandardItemModel;
19class QTreeView;
20class QVBoxLayout;
21
22class QtProfileSelectionDialog final : public QDialog {
23 Q_OBJECT
24
25public:
26 explicit QtProfileSelectionDialog(QWidget* parent);
27 ~QtProfileSelectionDialog() override;
28
29 void accept() override;
30 void reject() override;
31
32 bool GetStatus() const;
33 u32 GetIndex() const;
34
35private:
36 bool ok = false;
37 u32 user_index = 0;
38
39 void SelectUser(const QModelIndex& index);
40
41 QVBoxLayout* layout;
42 QTreeView* tree_view;
43 QStandardItemModel* item_model;
44 QGraphicsScene* scene;
45
46 std::vector<QList<QStandardItem*>> list_items;
47
48 QVBoxLayout* outer_layout;
49 QLabel* instruction_label;
50 QScrollArea* scroll_area;
51 QDialogButtonBox* buttons;
52
53 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
54};
55
56class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
57 Q_OBJECT
58
59public:
60 explicit QtProfileSelector(GMainWindow& parent);
61 ~QtProfileSelector() override;
62
63 void SelectProfile(
64 std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
65
66signals:
67 void MainWindowSelectProfile() const;
68
69private:
70 void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid);
71
72 mutable std::function<void(std::optional<Service::Account::UUID>)> callback;
73};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 384e17921..40db7a5e9 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -14,6 +14,8 @@
14#include "input_common/keyboard.h" 14#include "input_common/keyboard.h"
15#include "input_common/main.h" 15#include "input_common/main.h"
16#include "input_common/motion_emu.h" 16#include "input_common/motion_emu.h"
17#include "video_core/renderer_base.h"
18#include "video_core/video_core.h"
17#include "yuzu/bootmanager.h" 19#include "yuzu/bootmanager.h"
18 20
19EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 21EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
@@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() {
333 BackupGeometry(); 335 BackupGeometry();
334} 336}
335 337
338void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
339 auto& renderer = Core::System::GetInstance().Renderer();
340
341 if (!res_scale)
342 res_scale = VideoCore::GetResolutionScaleFactor(renderer);
343
344 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
345 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
346 renderer.RequestScreenshot(screenshot_image.bits(),
347 [=] {
348 screenshot_image.mirrored(false, true).save(screenshot_path);
349 LOG_INFO(Frontend, "The screenshot is saved.");
350 },
351 layout);
352}
353
336void GRenderWindow::OnMinimalClientAreaChangeRequest( 354void GRenderWindow::OnMinimalClientAreaChangeRequest(
337 const std::pair<unsigned, unsigned>& minimal_size) { 355 const std::pair<unsigned, unsigned>& minimal_size) {
338 setMinimumSize(minimal_size.first, minimal_size.second); 356 setMinimumSize(minimal_size.first, minimal_size.second);
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 873985564..4e3028215 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -8,6 +8,7 @@
8#include <condition_variable> 8#include <condition_variable>
9#include <mutex> 9#include <mutex>
10#include <QGLWidget> 10#include <QGLWidget>
11#include <QImage>
11#include <QThread> 12#include <QThread>
12#include "common/thread.h" 13#include "common/thread.h"
13#include "core/core.h" 14#include "core/core.h"
@@ -139,6 +140,8 @@ public:
139 140
140 void InitRenderTarget(); 141 void InitRenderTarget();
141 142
143 void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
144
142public slots: 145public slots:
143 void moveContext(); // overridden 146 void moveContext(); // overridden
144 147
@@ -165,6 +168,9 @@ private:
165 168
166 EmuThread* emu_thread; 169 EmuThread* emu_thread;
167 170
171 /// Temporary storage of the screenshot taken
172 QImage screenshot_image;
173
168protected: 174protected:
169 void showEvent(QShowEvent* event) override; 175 void showEvent(QShowEvent* event) override;
170}; 176};
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index eb2077b0f..c4349ccc8 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -4,6 +4,7 @@
4 4
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "configure_input_simple.h"
7#include "core/hle/service/acc/profile_manager.h" 8#include "core/hle/service/acc/profile_manager.h"
8#include "core/hle/service/hid/controllers/npad.h" 9#include "core/hle/service/hid/controllers/npad.h"
9#include "input_common/main.h" 10#include "input_common/main.h"
@@ -339,6 +340,13 @@ void Config::ReadTouchscreenValues() {
339 qt_config->endGroup(); 340 qt_config->endGroup();
340} 341}
341 342
343void Config::ApplyDefaultProfileIfInputInvalid() {
344 if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
345 [](const Settings::PlayerInput& in) { return in.connected; })) {
346 ApplyInputProfileConfiguration(UISettings::values.profile_index);
347 }
348}
349
342void Config::ReadValues() { 350void Config::ReadValues() {
343 qt_config->beginGroup("Controls"); 351 qt_config->beginGroup("Controls");
344 352
@@ -460,6 +468,8 @@ void Config::ReadValues() {
460 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 468 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
461 UISettings::values.enable_discord_presence = 469 UISettings::values.enable_discord_presence =
462 qt_config->value("enable_discord_presence", true).toBool(); 470 qt_config->value("enable_discord_presence", true).toBool();
471 UISettings::values.screenshot_resolution_factor =
472 static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt());
463 473
464 qt_config->beginGroup("UIGameList"); 474 qt_config->beginGroup("UIGameList");
465 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 475 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
@@ -518,6 +528,9 @@ void Config::ReadValues() {
518 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 528 UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
519 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); 529 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
520 UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); 530 UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
531 UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt();
532
533 ApplyDefaultProfileIfInputInvalid();
521 534
522 qt_config->endGroup(); 535 qt_config->endGroup();
523} 536}
@@ -678,6 +691,8 @@ void Config::SaveValues() {
678 qt_config->beginGroup("UI"); 691 qt_config->beginGroup("UI");
679 qt_config->setValue("theme", UISettings::values.theme); 692 qt_config->setValue("theme", UISettings::values.theme);
680 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); 693 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
694 qt_config->setValue("screenshot_resolution_factor",
695 UISettings::values.screenshot_resolution_factor);
681 696
682 qt_config->beginGroup("UIGameList"); 697 qt_config->beginGroup("UIGameList");
683 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 698 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
@@ -699,6 +714,7 @@ void Config::SaveValues() {
699 qt_config->beginGroup("Paths"); 714 qt_config->beginGroup("Paths");
700 qt_config->setValue("romsPath", UISettings::values.roms_path); 715 qt_config->setValue("romsPath", UISettings::values.roms_path);
701 qt_config->setValue("symbolsPath", UISettings::values.symbols_path); 716 qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
717 qt_config->setValue("screenshotPath", UISettings::values.screenshot_path);
702 qt_config->setValue("gameListRootDir", UISettings::values.gamedir); 718 qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
703 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); 719 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
704 qt_config->setValue("recentFiles", UISettings::values.recent_files); 720 qt_config->setValue("recentFiles", UISettings::values.recent_files);
@@ -720,6 +736,7 @@ void Config::SaveValues() {
720 qt_config->setValue("firstStart", UISettings::values.first_start); 736 qt_config->setValue("firstStart", UISettings::values.first_start);
721 qt_config->setValue("calloutFlags", UISettings::values.callout_flags); 737 qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
722 qt_config->setValue("showConsole", UISettings::values.show_console); 738 qt_config->setValue("showConsole", UISettings::values.show_console);
739 qt_config->setValue("profileIndex", UISettings::values.profile_index);
723 qt_config->endGroup(); 740 qt_config->endGroup();
724} 741}
725 742
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index a1c27bbf9..e73ad19bb 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -34,6 +34,7 @@ private:
34 void ReadKeyboardValues(); 34 void ReadKeyboardValues();
35 void ReadMouseValues(); 35 void ReadMouseValues();
36 void ReadTouchscreenValues(); 36 void ReadTouchscreenValues();
37 void ApplyDefaultProfileIfInputInvalid();
37 38
38 void SaveValues(); 39 void SaveValues();
39 void SavePlayerValues(); 40 void SavePlayerValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 9b297df28..8706b80d2 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>461</width> 9 <width>461</width>
10 <height>500</height> 10 <height>659</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -24,17 +24,17 @@
24 <string>General</string> 24 <string>General</string>
25 </attribute> 25 </attribute>
26 </widget> 26 </widget>
27 <widget class="ConfigureGameList" name="gameListTab"> 27 <widget class="ConfigureGameList" name="gameListTab">
28 <attribute name="title"> 28 <attribute name="title">
29 <string>Game List</string> 29 <string>Game List</string>
30 </attribute> 30 </attribute>
31 </widget> 31 </widget>
32 <widget class="ConfigureSystem" name="systemTab"> 32 <widget class="ConfigureSystem" name="systemTab">
33 <attribute name="title"> 33 <attribute name="title">
34 <string>System</string> 34 <string>System</string>
35 </attribute> 35 </attribute>
36 </widget> 36 </widget>
37 <widget class="ConfigureInput" name="inputTab"> 37 <widget class="ConfigureInputSimple" name="inputTab">
38 <attribute name="title"> 38 <attribute name="title">
39 <string>Input</string> 39 <string>Input</string>
40 </attribute> 40 </attribute>
@@ -54,11 +54,11 @@
54 <string>Debug</string> 54 <string>Debug</string>
55 </attribute> 55 </attribute>
56 </widget> 56 </widget>
57 <widget class="ConfigureWeb" name="webTab"> 57 <widget class="ConfigureWeb" name="webTab">
58 <attribute name="title"> 58 <attribute name="title">
59 <string>Web</string> 59 <string>Web</string>
60 </attribute> 60 </attribute>
61 </widget> 61 </widget>
62 </widget> 62 </widget>
63 </item> 63 </item>
64 <item> 64 <item>
@@ -77,12 +77,12 @@
77 <header>configuration/configure_general.h</header> 77 <header>configuration/configure_general.h</header>
78 <container>1</container> 78 <container>1</container>
79 </customwidget> 79 </customwidget>
80 <customwidget> 80 <customwidget>
81 <class>ConfigureGameList</class> 81 <class>ConfigureGameList</class>
82 <extends>QWidget</extends> 82 <extends>QWidget</extends>
83 <header>configuration/configure_gamelist.h</header> 83 <header>configuration/configure_gamelist.h</header>
84 <container>1</container> 84 <container>1</container>
85 </customwidget> 85 </customwidget>
86 <customwidget> 86 <customwidget>
87 <class>ConfigureSystem</class> 87 <class>ConfigureSystem</class>
88 <extends>QWidget</extends> 88 <extends>QWidget</extends>
@@ -102,9 +102,9 @@
102 <container>1</container> 102 <container>1</container>
103 </customwidget> 103 </customwidget>
104 <customwidget> 104 <customwidget>
105 <class>ConfigureInput</class> 105 <class>ConfigureInputSimple</class>
106 <extends>QWidget</extends> 106 <extends>QWidget</extends>
107 <header>configuration/configure_input.h</header> 107 <header>configuration/configure_input_simple.h</header>
108 <container>1</container> 108 <container>1</container>
109 </customwidget> 109 </customwidget>
110 <customwidget> 110 <customwidget>
@@ -113,12 +113,12 @@
113 <header>configuration/configure_graphics.h</header> 113 <header>configuration/configure_graphics.h</header>
114 <container>1</container> 114 <container>1</container>
115 </customwidget> 115 </customwidget>
116 <customwidget> 116 <customwidget>
117 <class>ConfigureWeb</class> 117 <class>ConfigureWeb</class>
118 <extends>QWidget</extends> 118 <extends>QWidget</extends>
119 <header>configuration/configure_web.h</header> 119 <header>configuration/configure_web.h</header>
120 <container>1</container> 120 <container>1</container>
121 </customwidget> 121 </customwidget>
122 </customwidgets> 122 </customwidgets>
123 <resources/> 123 <resources/>
124 <connections> 124 <connections>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 830d26115..f39d57998 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -20,6 +20,33 @@
20#include "yuzu/configuration/configure_input_player.h" 20#include "yuzu/configuration/configure_input_player.h"
21#include "yuzu/configuration/configure_mouse_advanced.h" 21#include "yuzu/configuration/configure_mouse_advanced.h"
22 22
23void OnDockedModeChanged(bool last_state, bool new_state) {
24 if (last_state == new_state) {
25 return;
26 }
27
28 Core::System& system{Core::System::GetInstance()};
29 if (!system.IsPoweredOn()) {
30 return;
31 }
32 Service::SM::ServiceManager& sm = system.ServiceManager();
33
34 // Message queue is shared between these services, we just need to signal an operation
35 // change to one and it will handle both automatically
36 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
37 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
38 bool has_signalled = false;
39
40 if (applet_oe != nullptr) {
41 applet_oe->GetMessageQueue()->OperationModeChanged();
42 has_signalled = true;
43 }
44
45 if (applet_ae != nullptr && !has_signalled) {
46 applet_ae->GetMessageQueue()->OperationModeChanged();
47 }
48}
49
23namespace { 50namespace {
24template <typename Dialog, typename... Args> 51template <typename Dialog, typename... Args>
25void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { 52void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
@@ -34,7 +61,7 @@ void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
34} // Anonymous namespace 61} // Anonymous namespace
35 62
36ConfigureInput::ConfigureInput(QWidget* parent) 63ConfigureInput::ConfigureInput(QWidget* parent)
37 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { 64 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
38 ui->setupUi(this); 65 ui->setupUi(this);
39 66
40 players_controller = { 67 players_controller = {
@@ -90,37 +117,6 @@ ConfigureInput::ConfigureInput(QWidget* parent)
90 117
91ConfigureInput::~ConfigureInput() = default; 118ConfigureInput::~ConfigureInput() = default;
92 119
93void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
94 if (ui->use_docked_mode->isChecked() && ui->handheld_connected->isChecked()) {
95 ui->handheld_connected->setChecked(false);
96 }
97
98 if (last_state == new_state) {
99 return;
100 }
101
102 Core::System& system{Core::System::GetInstance()};
103 if (!system.IsPoweredOn()) {
104 return;
105 }
106 Service::SM::ServiceManager& sm = system.ServiceManager();
107
108 // Message queue is shared between these services, we just need to signal an operation
109 // change to one and it will handle both automatically
110 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
111 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
112 bool has_signalled = false;
113
114 if (applet_oe != nullptr) {
115 applet_oe->GetMessageQueue()->OperationModeChanged();
116 has_signalled = true;
117 }
118
119 if (applet_ae != nullptr && !has_signalled) {
120 applet_ae->GetMessageQueue()->OperationModeChanged();
121 }
122}
123
124void ConfigureInput::applyConfiguration() { 120void ConfigureInput::applyConfiguration() {
125 for (std::size_t i = 0; i < players_controller.size(); ++i) { 121 for (std::size_t i = 0; i < players_controller.size(); ++i) {
126 const auto controller_type_index = players_controller[i]->currentIndex(); 122 const auto controller_type_index = players_controller[i]->currentIndex();
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 1649e4c0b..b8e62cc2b 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,8 +7,8 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9 9
10#include <QDialog>
10#include <QKeyEvent> 11#include <QKeyEvent>
11#include <QWidget>
12 12
13#include "ui_configure_input.h" 13#include "ui_configure_input.h"
14 14
@@ -20,7 +20,9 @@ namespace Ui {
20class ConfigureInput; 20class ConfigureInput;
21} 21}
22 22
23class ConfigureInput : public QWidget { 23void OnDockedModeChanged(bool last_state, bool new_state);
24
25class ConfigureInput : public QDialog {
24 Q_OBJECT 26 Q_OBJECT
25 27
26public: 28public:
@@ -33,8 +35,6 @@ public:
33private: 35private:
34 void updateUIEnabled(); 36 void updateUIEnabled();
35 37
36 void OnDockedModeChanged(bool last_state, bool new_state);
37
38 /// Load configuration settings. 38 /// Load configuration settings.
39 void loadConfiguration(); 39 void loadConfiguration();
40 /// Restore all buttons to their default values. 40 /// Restore all buttons to their default values.
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index dae8277bc..0a2d9f024 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -1,13 +1,13 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInput</class> 3 <class>ConfigureInput</class>
4 <widget class="QWidget" name="ConfigureInput"> 4 <widget class="QDialog" name="ConfigureInput">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>473</width> 9 <width>384</width>
10 <height>685</height> 10 <height>576</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -478,6 +478,13 @@
478 </property> 478 </property>
479 </spacer> 479 </spacer>
480 </item> 480 </item>
481 <item>
482 <widget class="QDialogButtonBox" name="buttonBox">
483 <property name="standardButtons">
484 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
485 </property>
486 </widget>
487 </item>
481 </layout> 488 </layout>
482 </item> 489 </item>
483 </layout> 490 </layout>
@@ -485,5 +492,38 @@
485 </layout> 492 </layout>
486 </widget> 493 </widget>
487 <resources/> 494 <resources/>
488 <connections/> 495 <connections>
496 <connection>
497 <sender>buttonBox</sender>
498 <signal>accepted()</signal>
499 <receiver>ConfigureInput</receiver>
500 <slot>accept()</slot>
501 <hints>
502 <hint type="sourcelabel">
503 <x>294</x>
504 <y>553</y>
505 </hint>
506 <hint type="destinationlabel">
507 <x>191</x>
508 <y>287</y>
509 </hint>
510 </hints>
511 </connection>
512 <connection>
513 <sender>buttonBox</sender>
514 <signal>rejected()</signal>
515 <receiver>ConfigureInput</receiver>
516 <slot>reject()</slot>
517 <hints>
518 <hint type="sourcelabel">
519 <x>294</x>
520 <y>553</y>
521 </hint>
522 <hint type="destinationlabel">
523 <x>191</x>
524 <y>287</y>
525 </hint>
526 </hints>
527 </connection>
528 </connections>
489</ui> 529</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 7dadd83c1..ba2b32c4f 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QColorDialog> 8#include <QColorDialog>
9#include <QGridLayout>
9#include <QMenu> 10#include <QMenu>
10#include <QMessageBox> 11#include <QMessageBox>
11#include <QTimer> 12#include <QTimer>
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp
new file mode 100644
index 000000000..07d71e9d1
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_simple.cpp
@@ -0,0 +1,137 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <tuple>
7
8#include "ui_configure_input_simple.h"
9#include "yuzu/configuration/configure_input.h"
10#include "yuzu/configuration/configure_input_player.h"
11#include "yuzu/configuration/configure_input_simple.h"
12#include "yuzu/ui_settings.h"
13
14namespace {
15
16template <typename Dialog, typename... Args>
17void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
18 caller->applyConfiguration();
19 Dialog dialog(caller, std::forward<Args>(args)...);
20
21 const auto res = dialog.exec();
22 if (res == QDialog::Accepted) {
23 dialog.applyConfiguration();
24 }
25}
26
27// OnProfileSelect functions should (when applicable):
28// - Set controller types
29// - Set controller enabled
30// - Set docked mode
31// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
32//
33// OnProfileSelect function should NOT however:
34// - Reset any button mappings
35// - Open any dialogs
36// - Block in any way
37
38constexpr std::size_t HANDHELD_INDEX = 8;
39
40void HandheldOnProfileSelect() {
41 Settings::values.players[HANDHELD_INDEX].connected = true;
42 Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
43
44 for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
45 Settings::values.players[player].connected = false;
46 }
47
48 Settings::values.use_docked_mode = false;
49 Settings::values.keyboard_enabled = false;
50 Settings::values.mouse_enabled = false;
51 Settings::values.debug_pad_enabled = false;
52 Settings::values.touchscreen.enabled = true;
53}
54
55void DualJoyconsDockedOnProfileSelect() {
56 Settings::values.players[0].connected = true;
57 Settings::values.players[0].type = Settings::ControllerType::DualJoycon;
58
59 for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
60 Settings::values.players[player].connected = false;
61 }
62
63 Settings::values.use_docked_mode = true;
64 Settings::values.keyboard_enabled = false;
65 Settings::values.mouse_enabled = false;
66 Settings::values.debug_pad_enabled = false;
67 Settings::values.touchscreen.enabled = false;
68}
69
70// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
71// is clicked)
72using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
73
74constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
75 {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
76 [](ConfigureInputSimple* caller) {
77 CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
78 }},
79 {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
80 [](ConfigureInputSimple* caller) {
81 CallConfigureDialog<ConfigureInputPlayer>(caller, 1, false);
82 }},
83 {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
84}};
85
86} // namespace
87
88void ApplyInputProfileConfiguration(int profile_index) {
89 std::get<1>(
90 INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
91}
92
93ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
94 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
95 ui->setupUi(this);
96
97 for (const auto& profile : INPUT_PROFILES) {
98 const QString label = tr(std::get<0>(profile));
99 ui->profile_combobox->addItem(label, label);
100 }
101
102 connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
103 &ConfigureInputSimple::OnSelectProfile);
104 connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure);
105
106 this->loadConfiguration();
107}
108
109ConfigureInputSimple::~ConfigureInputSimple() = default;
110
111void ConfigureInputSimple::applyConfiguration() {
112 auto index = ui->profile_combobox->currentIndex();
113 // Make the stored index for "Custom" very large so that if new profiles are added it
114 // doesn't change.
115 if (index >= static_cast<int>(INPUT_PROFILES.size() - 1))
116 index = std::numeric_limits<int>::max();
117
118 UISettings::values.profile_index = index;
119}
120
121void ConfigureInputSimple::loadConfiguration() {
122 const auto index = UISettings::values.profile_index;
123 if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0)
124 ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
125 else
126 ui->profile_combobox->setCurrentIndex(index);
127}
128
129void ConfigureInputSimple::OnSelectProfile(int index) {
130 const auto old_docked = Settings::values.use_docked_mode;
131 ApplyInputProfileConfiguration(index);
132 OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
133}
134
135void ConfigureInputSimple::OnConfigure() {
136 std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
137}
diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h
new file mode 100644
index 000000000..5b6b69994
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_simple.h
@@ -0,0 +1,40 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include <QWidget>
10
11class QPushButton;
12class QString;
13class QTimer;
14
15namespace Ui {
16class ConfigureInputSimple;
17}
18
19// Used by configuration loader to apply a profile if the input is invalid.
20void ApplyInputProfileConfiguration(int profile_index);
21
22class ConfigureInputSimple : public QWidget {
23 Q_OBJECT
24
25public:
26 explicit ConfigureInputSimple(QWidget* parent = nullptr);
27 ~ConfigureInputSimple() override;
28
29 /// Save all button configurations to settings file
30 void applyConfiguration();
31
32private:
33 /// Load configuration settings.
34 void loadConfiguration();
35
36 void OnSelectProfile(int index);
37 void OnConfigure();
38
39 std::unique_ptr<Ui::ConfigureInputSimple> ui;
40};
diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui
new file mode 100644
index 000000000..c4889caa9
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_simple.ui
@@ -0,0 +1,97 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputSimple</class>
4 <widget class="QWidget" name="ConfigureInputSimple">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>473</width>
10 <height>685</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigureInputSimple</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item>
20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title">
22 <string>Profile</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="2">
26 <widget class="QPushButton" name="profile_configure">
27 <property name="text">
28 <string>Configure</string>
29 </property>
30 </widget>
31 </item>
32 <item row="1" column="0">
33 <spacer name="horizontalSpacer">
34 <property name="orientation">
35 <enum>Qt::Horizontal</enum>
36 </property>
37 <property name="sizeHint" stdset="0">
38 <size>
39 <width>40</width>
40 <height>20</height>
41 </size>
42 </property>
43 </spacer>
44 </item>
45 <item row="1" column="3">
46 <spacer name="horizontalSpacer_2">
47 <property name="orientation">
48 <enum>Qt::Horizontal</enum>
49 </property>
50 <property name="sizeHint" stdset="0">
51 <size>
52 <width>40</width>
53 <height>20</height>
54 </size>
55 </property>
56 </spacer>
57 </item>
58 <item row="1" column="1">
59 <widget class="QComboBox" name="profile_combobox">
60 <property name="minimumSize">
61 <size>
62 <width>250</width>
63 <height>0</height>
64 </size>
65 </property>
66 </widget>
67 </item>
68 <item row="0" column="1" colspan="2">
69 <widget class="QLabel" name="label">
70 <property name="text">
71 <string>Choose a controller configuration:</string>
72 </property>
73 </widget>
74 </item>
75 </layout>
76 </widget>
77 </item>
78 </layout>
79 </item>
80 <item>
81 <spacer name="verticalSpacer">
82 <property name="orientation">
83 <enum>Qt::Vertical</enum>
84 </property>
85 <property name="sizeHint" stdset="0">
86 <size>
87 <width>20</width>
88 <height>40</height>
89 </size>
90 </property>
91 </spacer>
92 </item>
93 </layout>
94 </widget>
95 <resources/>
96 <connections/>
97</ui>
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp
index 80109b434..dffaba5ed 100644
--- a/src/yuzu/configuration/configure_per_general.cpp
+++ b/src/yuzu/configuration/configure_per_general.cpp
@@ -47,8 +47,8 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
47 tree_view->setContextMenuPolicy(Qt::NoContextMenu); 47 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
48 48
49 item_model->insertColumns(0, 2); 49 item_model->insertColumns(0, 2);
50 item_model->setHeaderData(0, Qt::Horizontal, "Patch Name"); 50 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
51 item_model->setHeaderData(1, Qt::Horizontal, "Version"); 51 item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
52 52
53 // We must register all custom types with the Qt Automoc system so that we are able to use it 53 // We must register all custom types with the Qt Automoc system so that we are able to use it
54 // with signals/slots. In this case, QList falls under the umbrells of custom types. 54 // with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -108,9 +108,9 @@ void ConfigurePerGameGeneral::loadConfiguration() {
108 if (loader->ReadTitle(title) == Loader::ResultStatus::Success) 108 if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
109 ui->display_name->setText(QString::fromStdString(title)); 109 ui->display_name->setText(QString::fromStdString(title));
110 110
111 std::string developer; 111 FileSys::NACP nacp;
112 if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success) 112 if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
113 ui->display_developer->setText(QString::fromStdString(developer)); 113 ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
114 114
115 ui->display_version->setText(QStringLiteral("1.0.0")); 115 ui->display_version->setText(QStringLiteral("1.0.0"));
116 } 116 }
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 707747422..209798521 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -30,6 +30,7 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
30 return Tegra::Texture::TextureFormat::A2B10G10R10; 30 return Tegra::Texture::TextureFormat::A2B10G10R10;
31 default: 31 default:
32 UNIMPLEMENTED_MSG("Unimplemented RT format"); 32 UNIMPLEMENTED_MSG("Unimplemented RT format");
33 return Tegra::Texture::TextureFormat::A8R8G8B8;
33 } 34 }
34} 35}
35 36
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 6b3a757e0..1adf6e330 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -221,6 +221,9 @@ QString WaitTreeThread::GetText() const {
221 case Kernel::ThreadStatus::Ready: 221 case Kernel::ThreadStatus::Ready:
222 status = tr("ready"); 222 status = tr("ready");
223 break; 223 break;
224 case Kernel::ThreadStatus::Paused:
225 status = tr("paused");
226 break;
224 case Kernel::ThreadStatus::WaitHLEEvent: 227 case Kernel::ThreadStatus::WaitHLEEvent:
225 status = tr("waiting for HLE return"); 228 status = tr("waiting for HLE return");
226 break; 229 break;
@@ -262,6 +265,8 @@ QColor WaitTreeThread::GetColor() const {
262 return QColor(Qt::GlobalColor::darkGreen); 265 return QColor(Qt::GlobalColor::darkGreen);
263 case Kernel::ThreadStatus::Ready: 266 case Kernel::ThreadStatus::Ready:
264 return QColor(Qt::GlobalColor::darkBlue); 267 return QColor(Qt::GlobalColor::darkBlue);
268 case Kernel::ThreadStatus::Paused:
269 return QColor(Qt::GlobalColor::lightGray);
265 case Kernel::ThreadStatus::WaitHLEEvent: 270 case Kernel::ThreadStatus::WaitHLEEvent:
266 case Kernel::ThreadStatus::WaitIPC: 271 case Kernel::ThreadStatus::WaitIPC:
267 return QColor(Qt::GlobalColor::darkRed); 272 return QColor(Qt::GlobalColor::darkRed);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 90b212ba5..01a0f94ab 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,6 +8,7 @@
8#include <thread> 8#include <thread>
9 9
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "applets/profile_select.h"
11#include "applets/software_keyboard.h" 12#include "applets/software_keyboard.h"
12#include "configuration/configure_per_general.h" 13#include "configuration/configure_per_general.h"
13#include "core/file_sys/vfs.h" 14#include "core/file_sys/vfs.h"
@@ -208,6 +209,28 @@ GMainWindow::~GMainWindow() {
208 delete render_window; 209 delete render_window;
209} 210}
210 211
212void GMainWindow::ProfileSelectorSelectProfile() {
213 QtProfileSelectionDialog dialog(this);
214 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
215 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
216 dialog.setWindowModality(Qt::WindowModal);
217 dialog.exec();
218
219 if (!dialog.GetStatus()) {
220 emit ProfileSelectorFinishedSelection(std::nullopt);
221 return;
222 }
223
224 Service::Account::ProfileManager manager;
225 const auto uuid = manager.GetUser(dialog.GetIndex());
226 if (!uuid.has_value()) {
227 emit ProfileSelectorFinishedSelection(std::nullopt);
228 return;
229 }
230
231 emit ProfileSelectorFinishedSelection(uuid);
232}
233
211void GMainWindow::SoftwareKeyboardGetText( 234void GMainWindow::SoftwareKeyboardGetText(
212 const Core::Frontend::SoftwareKeyboardParameters& parameters) { 235 const Core::Frontend::SoftwareKeyboardParameters& parameters) {
213 QtSoftwareKeyboardDialog dialog(this, parameters); 236 QtSoftwareKeyboardDialog dialog(this, parameters);
@@ -335,6 +358,9 @@ void GMainWindow::InitializeHotkeys() {
335 Qt::ApplicationShortcut); 358 Qt::ApplicationShortcut);
336 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), 359 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
337 Qt::ApplicationShortcut); 360 Qt::ApplicationShortcut);
361 hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
362 QKeySequence(QKeySequence::Print));
363
338 hotkey_registry.LoadHotkeys(); 364 hotkey_registry.LoadHotkeys();
339 365
340 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 366 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -394,6 +420,12 @@ void GMainWindow::InitializeHotkeys() {
394 OnLoadAmiibo(); 420 OnLoadAmiibo();
395 } 421 }
396 }); 422 });
423 connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this),
424 &QShortcut::activated, this, [&] {
425 if (emu_thread->IsRunning()) {
426 OnCaptureScreenshot();
427 }
428 });
397} 429}
398 430
399void GMainWindow::SetDefaultUIGeometry() { 431void GMainWindow::SetDefaultUIGeometry() {
@@ -491,6 +523,10 @@ void GMainWindow::ConnectMenuEvents() {
491 hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key()); 523 hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
492 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 524 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
493 525
526 // Movie
527 connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
528 &GMainWindow::OnCaptureScreenshot);
529
494 // Help 530 // Help
495 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); 531 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
496 connect(ui.action_Rederive, &QAction::triggered, this, 532 connect(ui.action_Rederive, &QAction::triggered, this,
@@ -574,6 +610,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
574 610
575 system.SetGPUDebugContext(debug_context); 611 system.SetGPUDebugContext(debug_context);
576 612
613 system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this));
577 system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); 614 system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
578 615
579 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 616 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
@@ -727,6 +764,7 @@ void GMainWindow::ShutdownGame() {
727 ui.action_Restart->setEnabled(false); 764 ui.action_Restart->setEnabled(false);
728 ui.action_Report_Compatibility->setEnabled(false); 765 ui.action_Report_Compatibility->setEnabled(false);
729 ui.action_Load_Amiibo->setEnabled(false); 766 ui.action_Load_Amiibo->setEnabled(false);
767 ui.action_Capture_Screenshot->setEnabled(false);
730 render_window->hide(); 768 render_window->hide();
731 game_list->show(); 769 game_list->show();
732 game_list->setFilterFocus(); 770 game_list->setFilterFocus();
@@ -1290,6 +1328,7 @@ void GMainWindow::OnStartGame() {
1290 1328
1291 discord_rpc->Update(); 1329 discord_rpc->Update();
1292 ui.action_Load_Amiibo->setEnabled(true); 1330 ui.action_Load_Amiibo->setEnabled(true);
1331 ui.action_Capture_Screenshot->setEnabled(true);
1293} 1332}
1294 1333
1295void GMainWindow::OnPauseGame() { 1334void GMainWindow::OnPauseGame() {
@@ -1298,6 +1337,7 @@ void GMainWindow::OnPauseGame() {
1298 ui.action_Start->setEnabled(true); 1337 ui.action_Start->setEnabled(true);
1299 ui.action_Pause->setEnabled(false); 1338 ui.action_Pause->setEnabled(false);
1300 ui.action_Stop->setEnabled(true); 1339 ui.action_Stop->setEnabled(true);
1340 ui.action_Capture_Screenshot->setEnabled(false);
1301} 1341}
1302 1342
1303void GMainWindow::OnStopGame() { 1343void GMainWindow::OnStopGame() {
@@ -1460,6 +1500,18 @@ void GMainWindow::OnToggleFilterBar() {
1460 } 1500 }
1461} 1501}
1462 1502
1503void GMainWindow::OnCaptureScreenshot() {
1504 OnPauseGame();
1505 const QString path =
1506 QFileDialog::getSaveFileName(this, tr("Capture Screenshot"),
1507 UISettings::values.screenshot_path, tr("PNG Image (*.png)"));
1508 if (!path.isEmpty()) {
1509 UISettings::values.screenshot_path = QFileInfo(path).path();
1510 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
1511 }
1512 OnStartGame();
1513}
1514
1463void GMainWindow::UpdateStatusBar() { 1515void GMainWindow::UpdateStatusBar() {
1464 if (emu_thread == nullptr) { 1516 if (emu_thread == nullptr) {
1465 status_bar_update_timer.stop(); 1517 status_bar_update_timer.stop();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ca9c50367..4e37f6a2d 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -99,10 +99,12 @@ signals:
99 // Signal that tells widgets to update icons to use the current theme 99 // Signal that tells widgets to update icons to use the current theme
100 void UpdateThemedIcons(); 100 void UpdateThemedIcons();
101 101
102 void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
102 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); 103 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
103 void SoftwareKeyboardFinishedCheckDialog(); 104 void SoftwareKeyboardFinishedCheckDialog();
104 105
105public slots: 106public slots:
107 void ProfileSelectorSelectProfile();
106 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); 108 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
107 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); 109 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
108 110
@@ -187,6 +189,7 @@ private slots:
187 void ShowFullscreen(); 189 void ShowFullscreen();
188 void HideFullscreen(); 190 void HideFullscreen();
189 void ToggleWindowMode(); 191 void ToggleWindowMode();
192 void OnCaptureScreenshot();
190 void OnCoreError(Core::System::ResultStatus, std::string); 193 void OnCoreError(Core::System::ResultStatus, std::string);
191 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 194 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
192 195
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 75e96387f..ffcabb495 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -101,11 +101,13 @@
101 <addaction name="action_Show_Status_Bar"/> 101 <addaction name="action_Show_Status_Bar"/>
102 <addaction name="menu_View_Debugging"/> 102 <addaction name="menu_View_Debugging"/>
103 </widget> 103 </widget>
104 <widget class ="QMenu" name="menu_Tools"> 104 <widget class="QMenu" name="menu_Tools">
105 <property name="title"> 105 <property name="title">
106 <string>Tools</string> 106 <string>Tools</string>
107 </property> 107 </property>
108 <addaction name="action_Rederive" /> 108 <addaction name="action_Rederive"/>
109 <addaction name="separator"/>
110 <addaction name="action_Capture_Screenshot"/>
109 </widget> 111 </widget>
110 <widget class="QMenu" name="menu_Help"> 112 <widget class="QMenu" name="menu_Help">
111 <property name="title"> 113 <property name="title">
@@ -118,7 +120,7 @@
118 <addaction name="menu_File"/> 120 <addaction name="menu_File"/>
119 <addaction name="menu_Emulation"/> 121 <addaction name="menu_Emulation"/>
120 <addaction name="menu_View"/> 122 <addaction name="menu_View"/>
121 <addaction name="menu_Tools" /> 123 <addaction name="menu_Tools"/>
122 <addaction name="menu_Help"/> 124 <addaction name="menu_Help"/>
123 </widget> 125 </widget>
124 <action name="action_Install_File_NAND"> 126 <action name="action_Install_File_NAND">
@@ -173,11 +175,11 @@
173 <string>&amp;Stop</string> 175 <string>&amp;Stop</string>
174 </property> 176 </property>
175 </action> 177 </action>
176 <action name="action_Rederive"> 178 <action name="action_Rederive">
177 <property name="text"> 179 <property name="text">
178 <string>Reinitialize keys...</string> 180 <string>Reinitialize keys...</string>
179 </property> 181 </property>
180 </action> 182 </action>
181 <action name="action_About"> 183 <action name="action_About">
182 <property name="text"> 184 <property name="text">
183 <string>About yuzu</string> 185 <string>About yuzu</string>
@@ -252,39 +254,47 @@
252 <string>Fullscreen</string> 254 <string>Fullscreen</string>
253 </property> 255 </property>
254 </action> 256 </action>
255 <action name="action_Restart"> 257 <action name="action_Restart">
256 <property name="enabled"> 258 <property name="enabled">
257 <bool>false</bool> 259 <bool>false</bool>
258 </property> 260 </property>
259 <property name="text"> 261 <property name="text">
260 <string>Restart</string> 262 <string>Restart</string>
261 </property> 263 </property>
262 </action> 264 </action>
263 <action name="action_Load_Amiibo"> 265 <action name="action_Load_Amiibo">
264 <property name="enabled"> 266 <property name="enabled">
265 <bool>false</bool> 267 <bool>false</bool>
266 </property> 268 </property>
267 <property name="text"> 269 <property name="text">
268 <string>Load Amiibo...</string> 270 <string>Load Amiibo...</string>
269 </property> 271 </property>
270 </action> 272 </action>
271 <action name="action_Report_Compatibility"> 273 <action name="action_Report_Compatibility">
272 <property name="enabled"> 274 <property name="enabled">
273 <bool>false</bool> 275 <bool>false</bool>
274 </property> 276 </property>
275 <property name="text"> 277 <property name="text">
276 <string>Report Compatibility</string> 278 <string>Report Compatibility</string>
277 </property> 279 </property>
278 <property name="visible"> 280 <property name="visible">
279 <bool>false</bool> 281 <bool>false</bool>
280 </property> 282 </property>
281 </action> 283 </action>
282 <action name="action_Open_yuzu_Folder"> 284 <action name="action_Open_yuzu_Folder">
283 <property name="text"> 285 <property name="text">
284 <string>Open yuzu Folder</string> 286 <string>Open yuzu Folder</string>
285 </property> 287 </property>
286 </action> 288 </action>
287 </widget> 289 <action name="action_Capture_Screenshot">
290 <property name="enabled">
291 <bool>false</bool>
292 </property>
293 <property name="text">
294 <string>Capture Screenshot</string>
295 </property>
296 </action>
297 </widget>
288 <resources/> 298 <resources/>
289 <connections/> 299 <connections/>
290</ui> 300</ui>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index e80aebc0a..58ba240fd 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -10,6 +10,7 @@
10#include <QByteArray> 10#include <QByteArray>
11#include <QString> 11#include <QString>
12#include <QStringList> 12#include <QStringList>
13#include "common/common_types.h"
13 14
14namespace UISettings { 15namespace UISettings {
15 16
@@ -42,8 +43,11 @@ struct Values {
42 // Discord RPC 43 // Discord RPC
43 bool enable_discord_presence; 44 bool enable_discord_presence;
44 45
46 u16 screenshot_resolution_factor;
47
45 QString roms_path; 48 QString roms_path;
46 QString symbols_path; 49 QString symbols_path;
50 QString screenshot_path;
47 QString gamedir; 51 QString gamedir;
48 bool gamedir_deepscan; 52 bool gamedir_deepscan;
49 QStringList recent_files; 53 QStringList recent_files;
@@ -58,6 +62,9 @@ struct Values {
58 // logging 62 // logging
59 bool show_console; 63 bool show_console;
60 64
65 // Controllers
66 int profile_index;
67
61 // Game List 68 // Game List
62 bool show_unknown; 69 bool show_unknown;
63 bool show_add_ons; 70 bool show_add_ons;