diff options
Diffstat (limited to 'src')
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> | |||
| 12 | class Quaternion { | 12 | class Quaternion { |
| 13 | public: | 13 | public: |
| 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 | ||
| 430 | void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) { | ||
| 431 | impl->profile_selector = std::move(applet); | ||
| 432 | } | ||
| 433 | |||
| 434 | const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const { | ||
| 435 | return *impl->profile_selector; | ||
| 436 | } | ||
| 437 | |||
| 427 | void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) { | 438 | void 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 | ||
| 15 | namespace Core::Frontend { | 16 | namespace Core::Frontend { |
| 16 | class EmuWindow; | 17 | class 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 | ||
| 39 | NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { | 39 | NACP::NACP() = default; |
| 40 | file->ReadObject(raw.get()); | 40 | |
| 41 | NACP::NACP(VirtualFile file) { | ||
| 42 | file->ReadObject(&raw); | ||
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | NACP::~NACP() = default; | 45 | NACP::~NACP() = default; |
| 44 | 46 | ||
| 45 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | 47 | const 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 | ||
| 67 | u64 NACP::GetTitleId() const { | 69 | u64 NACP::GetTitleId() const { |
| 68 | return raw->title_id; | 70 | return raw.title_id; |
| 69 | } | 71 | } |
| 70 | 72 | ||
| 71 | u64 NACP::GetDLCBaseTitleId() const { | 73 | u64 NACP::GetDLCBaseTitleId() const { |
| 72 | return raw->dlc_base_title_id; | 74 | return raw.dlc_base_title_id; |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | std::string NACP::GetVersionString() const { | 77 | std::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 | |||
| 82 | u64 NACP::GetDefaultNormalSaveSize() const { | ||
| 83 | return raw.normal_save_data_size; | ||
| 84 | } | ||
| 85 | |||
| 86 | u64 NACP::GetDefaultJournalSaveSize() const { | ||
| 87 | return raw.journal_sava_data_size; | ||
| 78 | } | 88 | } |
| 79 | 89 | ||
| 80 | std::vector<u8> NACP::GetRawBytes() const { | 90 | std::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. |
| 29 | struct RawNACP { | 29 | struct 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. |
| 73 | class NACP { | 86 | class NACP { |
| 74 | public: | 87 | public: |
| 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 | ||
| 86 | private: | 102 | private: |
| 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 | ||
| 14 | namespace FileSys { | 14 | namespace FileSys { |
| 15 | 15 | ||
| 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | ||
| 17 | |||
| 16 | std::string SaveDataDescriptor::DebugInfo() const { | 18 | std::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 | ||
| 137 | SaveDataSize 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 | |||
| 152 | void 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 | }; |
| 47 | static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); | 47 | static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); |
| 48 | 48 | ||
| 49 | struct 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 |
| 50 | class SaveDataFactory { | 55 | class SaveDataFactory { |
| 51 | public: | 56 | public: |
| @@ -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 | |||
| 63 | private: | 71 | private: |
| 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 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ProfileSelectApplet::~ProfileSelectApplet() = default; | ||
| 11 | |||
| 12 | void 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 | |||
| 11 | namespace Core::Frontend { | ||
| 12 | |||
| 13 | class ProfileSelectApplet { | ||
| 14 | public: | ||
| 15 | virtual ~ProfileSelectApplet(); | ||
| 16 | |||
| 17 | virtual void SelectProfile( | ||
| 18 | std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0; | ||
| 19 | }; | ||
| 20 | |||
| 21 | class DefaultProfileSelectApplet final : public ProfileSelectApplet { | ||
| 22 | public: | ||
| 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 | ||
| 10 | namespace Layout { | 11 | namespace Layout { |
| 11 | 12 | ||
| @@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { | |||
| 42 | return res; | 43 | return res; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 46 | FramebufferLayout 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 @@ | |||
| 9 | namespace Layout { | 9 | namespace Layout { |
| 10 | 10 | ||
| 11 | enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; | 11 | enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; |
| 12 | enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 }; | ||
| 12 | 13 | ||
| 13 | /// Describes the layout of the window framebuffer | 14 | /// Describes the layout of the window framebuffer |
| 14 | struct FramebufferLayout { | 15 | struct FramebufferLayout { |
| @@ -34,4 +35,10 @@ struct FramebufferLayout { | |||
| 34 | */ | 35 | */ |
| 35 | FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height); | 36 | FramebufferLayout 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 | */ | ||
| 42 | FramebufferLayout 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 | ||
| 204 | static Kernel::Thread* FindThreadById(int id) { | 204 | static 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}; | |||
| 27 | constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; | 27 | constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; |
| 28 | constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; | 28 | constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; |
| 29 | constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; | 29 | constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; |
| 30 | constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122}; | 30 | constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122}; |
| 31 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; | 31 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; |
| 32 | constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; | 32 | constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; |
| 33 | constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; | 33 | constexpr 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 | ||
| 245 | u32 KernelCore::CreateNewThreadID() { | 243 | u64 KernelCore::CreateNewThreadID() { |
| 246 | return impl->next_thread_id++; | 244 | return impl->next_thread_id++; |
| 247 | } | 245 | } |
| 248 | 246 | ||
| 249 | u32 KernelCore::CreateNewProcessID() { | 247 | u64 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 | ||
| 121 | class Process final : public WaitObject { | 121 | class Process final : public WaitObject { |
| 122 | public: | 122 | public: |
| 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. |
| 394 | static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { | 394 | static 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. |
| 409 | static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | 409 | static 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 |
| 922 | static ResultCode SetThreadActivity(Handle handle, u32 unknown) { | 935 | static 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() { | |||
| 73 | template <ResultCode func(u32*, u64)> | 73 | template <ResultCode func(u32*, u64)> |
| 74 | void SvcWrap() { | 74 | void SvcWrap() { |
| 75 | u32 param_1 = 0; | 75 | u32 param_1 = 0; |
| 76 | u32 retval = func(¶m_1, Param(1)).raw; | 76 | const u32 retval = func(¶m_1, Param(1)).raw; |
| 77 | Core::CurrentArmInterface().SetReg(1, param_1); | ||
| 78 | FuncReturn(retval); | ||
| 79 | } | ||
| 80 | |||
| 81 | template <ResultCode func(u64*, u32)> | ||
| 82 | void SvcWrap() { | ||
| 83 | u64 param_1 = 0; | ||
| 84 | const u32 retval = func(¶m_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 | ||
| 399 | void 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 { | |||
| 45 | enum class ThreadStatus { | 45 | enum 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 | ||
| 65 | enum class ThreadActivity : u32 { | ||
| 66 | Normal = 0, | ||
| 67 | Paused = 1, | ||
| 68 | }; | ||
| 69 | |||
| 64 | class Thread final : public WaitObject { | 70 | class Thread final : public WaitObject { |
| 65 | public: | 71 | public: |
| 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 | |||
| 374 | private: | 386 | private: |
| 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}; | |||
| 39 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; | 41 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; |
| 40 | 42 | ||
| 41 | enum class AppletId : u32 { | 43 | enum class AppletId : u32 { |
| 44 | ProfileSelect = 0x10, | ||
| 42 | SoftwareKeyboard = 0x11, | 45 | SoftwareKeyboard = 0x11, |
| 43 | }; | 46 | }; |
| 44 | 47 | ||
| @@ -71,10 +74,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { | |||
| 71 | IWindowController::~IWindowController() = default; | 74 | IWindowController::~IWindowController() = default; |
| 72 | 75 | ||
| 73 | void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { | 76 | void 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 | ||
| 80 | void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { | 86 | void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { |
| @@ -772,6 +778,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default; | |||
| 772 | 778 | ||
| 773 | static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { | 779 | static 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 | ||
| 1047 | void 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 | |||
| 1071 | void 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 | |||
| 1039 | void InstallInterfaces(SM::ServiceManager& service_manager, | 1089 | void 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 | |||
| 14 | namespace Service::AM::Applets { | ||
| 15 | |||
| 16 | constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; | ||
| 17 | |||
| 18 | ProfileSelect::ProfileSelect() = default; | ||
| 19 | ProfileSelect::~ProfileSelect() = default; | ||
| 20 | |||
| 21 | void 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 | |||
| 36 | bool ProfileSelect::TransactionComplete() const { | ||
| 37 | return complete; | ||
| 38 | } | ||
| 39 | |||
| 40 | ResultCode ProfileSelect::GetStatus() const { | ||
| 41 | return status; | ||
| 42 | } | ||
| 43 | |||
| 44 | void ProfileSelect::ExecuteInteractive() { | ||
| 45 | UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); | ||
| 46 | } | ||
| 47 | |||
| 48 | void 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 | |||
| 59 | void 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 | |||
| 13 | namespace Service::AM::Applets { | ||
| 14 | |||
| 15 | struct 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 | }; | ||
| 21 | static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size."); | ||
| 22 | |||
| 23 | struct UserSelectionOutput { | ||
| 24 | u64 result; | ||
| 25 | u128 uuid_selected; | ||
| 26 | }; | ||
| 27 | static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); | ||
| 28 | |||
| 29 | class ProfileSelect final : public Applet { | ||
| 30 | public: | ||
| 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 | |||
| 43 | private: | ||
| 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 | ||
| 24 | namespace Service::FileSystem { | 29 | namespace 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. |
| 29 | constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; | 34 | constexpr 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) | ||
| 38 | constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; | ||
| 39 | |||
| 31 | static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, | 40 | static 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 | ||
| 353 | FileSys::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 | |||
| 385 | void 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 | |||
| 344 | FileSys::RegisteredCacheUnion GetUnionContents() { | 391 | FileSys::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; | |||
| 21 | enum class ContentRecordType : u8; | 21 | enum class ContentRecordType : u8; |
| 22 | enum class Mode : u32; | 22 | enum class Mode : u32; |
| 23 | enum class SaveDataSpaceId : u8; | 23 | enum class SaveDataSpaceId : u8; |
| 24 | enum class SaveDataType : u8; | ||
| 24 | enum class StorageId : u8; | 25 | enum class StorageId : u8; |
| 25 | 26 | ||
| 26 | struct SaveDataDescriptor; | 27 | struct SaveDataDescriptor; |
| 28 | struct SaveDataSize; | ||
| 27 | } // namespace FileSys | 29 | } // namespace FileSys |
| 28 | 30 | ||
| 29 | namespace Service { | 31 | namespace Service { |
| @@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | |||
| 48 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); | 50 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); |
| 49 | ResultVal<FileSys::VirtualDir> OpenSDMC(); | 51 | ResultVal<FileSys::VirtualDir> OpenSDMC(); |
| 50 | 52 | ||
| 53 | FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); | ||
| 54 | void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||
| 55 | FileSys::SaveDataSize new_value); | ||
| 56 | |||
| 51 | FileSys::RegisteredCacheUnion GetUnionContents(); | 57 | FileSys::RegisteredCacheUnion GetUnionContents(); |
| 52 | 58 | ||
| 53 | FileSys::RegisteredCache* GetSystemNANDContents(); | 59 | FileSys::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 | ||
| 18 | namespace FileSys { | ||
| 19 | class NACP; | ||
| 20 | } // namespace FileSys | ||
| 21 | |||
| 18 | namespace Kernel { | 22 | namespace Kernel { |
| 19 | struct AddressMapping; | 23 | struct AddressMapping; |
| 20 | class Process; | 24 | class 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 | ||
| 155 | ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) { | 155 | ResultStatus 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 | ||
| 48 | private: | 48 | private: |
| 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 | ||
| 124 | ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) { | 124 | ResultStatus 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 | ||
| 48 | private: | 48 | private: |
| 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 | ||
| 192 | template <typename T> | 193 | template <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 | ||
| 30 | void 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 { | |||
| 21 | struct RendererSettings { | 22 | struct 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 | ||
| 26 | class RendererBase : NonCopyable { | 33 | class 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 | |||
| 63 | protected: | 90 | protected: |
| 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 | */ |
| 386 | void RendererOpenGL::DrawScreen() { | 391 | void 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 |
| 415 | void RendererOpenGL::UpdateFramerate() {} | 419 | void RendererOpenGL::UpdateFramerate() {} |
| 416 | 420 | ||
| 421 | void 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 | |||
| 417 | static const char* GetSource(GLenum source) { | 452 | static 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 { | |||
| 16 | class EmuWindow; | 16 | class EmuWindow; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Layout { | ||
| 20 | struct FramebufferLayout; | ||
| 21 | } | ||
| 22 | |||
| 19 | namespace OpenGL { | 23 | namespace 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 | ||
| 18 | u16 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 | */ |
| 23 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); | 23 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); |
| 24 | 24 | ||
| 25 | u16 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 | ||
| 19 | constexpr 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 | |||
| 29 | QString 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 | |||
| 36 | QString 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 | |||
| 42 | QPixmap 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 | |||
| 53 | QtProfileSelectionDialog::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 | |||
| 124 | QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; | ||
| 125 | |||
| 126 | void QtProfileSelectionDialog::accept() { | ||
| 127 | ok = true; | ||
| 128 | QDialog::accept(); | ||
| 129 | } | ||
| 130 | |||
| 131 | void QtProfileSelectionDialog::reject() { | ||
| 132 | ok = false; | ||
| 133 | user_index = 0; | ||
| 134 | QDialog::reject(); | ||
| 135 | } | ||
| 136 | |||
| 137 | bool QtProfileSelectionDialog::GetStatus() const { | ||
| 138 | return ok; | ||
| 139 | } | ||
| 140 | |||
| 141 | u32 QtProfileSelectionDialog::GetIndex() const { | ||
| 142 | return user_index; | ||
| 143 | } | ||
| 144 | |||
| 145 | void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) { | ||
| 146 | user_index = index.row(); | ||
| 147 | } | ||
| 148 | |||
| 149 | QtProfileSelector::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 | |||
| 156 | QtProfileSelector::~QtProfileSelector() = default; | ||
| 157 | |||
| 158 | void QtProfileSelector::SelectProfile( | ||
| 159 | std::function<void(std::optional<Service::Account::UUID>)> callback) const { | ||
| 160 | this->callback = std::move(callback); | ||
| 161 | emit MainWindowSelectProfile(); | ||
| 162 | } | ||
| 163 | |||
| 164 | void 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 | |||
| 12 | class GMainWindow; | ||
| 13 | class QDialogButtonBox; | ||
| 14 | class QGraphicsScene; | ||
| 15 | class QLabel; | ||
| 16 | class QScrollArea; | ||
| 17 | class QStandardItem; | ||
| 18 | class QStandardItemModel; | ||
| 19 | class QTreeView; | ||
| 20 | class QVBoxLayout; | ||
| 21 | |||
| 22 | class QtProfileSelectionDialog final : public QDialog { | ||
| 23 | Q_OBJECT | ||
| 24 | |||
| 25 | public: | ||
| 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 | |||
| 35 | private: | ||
| 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 | |||
| 56 | class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { | ||
| 57 | Q_OBJECT | ||
| 58 | |||
| 59 | public: | ||
| 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 | |||
| 66 | signals: | ||
| 67 | void MainWindowSelectProfile() const; | ||
| 68 | |||
| 69 | private: | ||
| 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 | ||
| 19 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 21 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} |
| @@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() { | |||
| 333 | BackupGeometry(); | 335 | BackupGeometry(); |
| 334 | } | 336 | } |
| 335 | 337 | ||
| 338 | void 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 | |||
| 336 | void GRenderWindow::OnMinimalClientAreaChangeRequest( | 354 | void 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 | |||
| 142 | public slots: | 145 | public 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 | |||
| 168 | protected: | 174 | protected: |
| 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 | ||
| 343 | void 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 | |||
| 342 | void Config::ReadValues() { | 350 | void 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 | ||
| 23 | void 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 | |||
| 23 | namespace { | 50 | namespace { |
| 24 | template <typename Dialog, typename... Args> | 51 | template <typename Dialog, typename... Args> |
| 25 | void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { | 52 | void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { |
| @@ -34,7 +61,7 @@ void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { | |||
| 34 | } // Anonymous namespace | 61 | } // Anonymous namespace |
| 35 | 62 | ||
| 36 | ConfigureInput::ConfigureInput(QWidget* parent) | 63 | ConfigureInput::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 | ||
| 91 | ConfigureInput::~ConfigureInput() = default; | 118 | ConfigureInput::~ConfigureInput() = default; |
| 92 | 119 | ||
| 93 | void 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 | |||
| 124 | void ConfigureInput::applyConfiguration() { | 120 | void 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 { | |||
| 20 | class ConfigureInput; | 20 | class ConfigureInput; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | class ConfigureInput : public QWidget { | 23 | void OnDockedModeChanged(bool last_state, bool new_state); |
| 24 | |||
| 25 | class ConfigureInput : public QDialog { | ||
| 24 | Q_OBJECT | 26 | Q_OBJECT |
| 25 | 27 | ||
| 26 | public: | 28 | public: |
| @@ -33,8 +35,6 @@ public: | |||
| 33 | private: | 35 | private: |
| 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 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | template <typename Dialog, typename... Args> | ||
| 17 | void 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 | |||
| 38 | constexpr std::size_t HANDHELD_INDEX = 8; | ||
| 39 | |||
| 40 | void 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 | |||
| 55 | void 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) | ||
| 72 | using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>; | ||
| 73 | |||
| 74 | constexpr 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 | |||
| 88 | void 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 | |||
| 93 | ConfigureInputSimple::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 | |||
| 109 | ConfigureInputSimple::~ConfigureInputSimple() = default; | ||
| 110 | |||
| 111 | void 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 | |||
| 121 | void 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 | |||
| 129 | void 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 | |||
| 135 | void 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 | |||
| 11 | class QPushButton; | ||
| 12 | class QString; | ||
| 13 | class QTimer; | ||
| 14 | |||
| 15 | namespace Ui { | ||
| 16 | class ConfigureInputSimple; | ||
| 17 | } | ||
| 18 | |||
| 19 | // Used by configuration loader to apply a profile if the input is invalid. | ||
| 20 | void ApplyInputProfileConfiguration(int profile_index); | ||
| 21 | |||
| 22 | class ConfigureInputSimple : public QWidget { | ||
| 23 | Q_OBJECT | ||
| 24 | |||
| 25 | public: | ||
| 26 | explicit ConfigureInputSimple(QWidget* parent = nullptr); | ||
| 27 | ~ConfigureInputSimple() override; | ||
| 28 | |||
| 29 | /// Save all button configurations to settings file | ||
| 30 | void applyConfiguration(); | ||
| 31 | |||
| 32 | private: | ||
| 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 | ||
| 212 | void 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 | |||
| 211 | void GMainWindow::SoftwareKeyboardGetText( | 234 | void 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 | ||
| 399 | void GMainWindow::SetDefaultUIGeometry() { | 431 | void 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 | ||
| 1295 | void GMainWindow::OnPauseGame() { | 1334 | void 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 | ||
| 1303 | void GMainWindow::OnStopGame() { | 1343 | void GMainWindow::OnStopGame() { |
| @@ -1460,6 +1500,18 @@ void GMainWindow::OnToggleFilterBar() { | |||
| 1460 | } | 1500 | } |
| 1461 | } | 1501 | } |
| 1462 | 1502 | ||
| 1503 | void 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 | |||
| 1463 | void GMainWindow::UpdateStatusBar() { | 1515 | void 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 | ||
| 105 | public slots: | 106 | public 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>&Stop</string> | 175 | <string>&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 | ||
| 14 | namespace UISettings { | 15 | namespace 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; |