diff options
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 89 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.h | 49 | ||||
| -rw-r--r-- | src/core/file_sys/ips_layer.cpp | 69 | ||||
| -rw-r--r-- | src/core/file_sys/ips_layer.h | 11 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.cpp | 15 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 25 | ||||
| -rw-r--r-- | src/core/loader/nsp.cpp | 2 | ||||
| -rw-r--r-- | src/core/loader/nsp.h | 2 | ||||
| -rw-r--r-- | src/core/loader/xci.cpp | 2 | ||||
| -rw-r--r-- | src/core/loader/xci.h | 2 | ||||
| -rw-r--r-- | src/core/telemetry_session.cpp | 12 | ||||
| -rw-r--r-- | src/core/telemetry_session.h | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 10 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 71 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.h | 10 | ||||
| -rw-r--r-- | src/yuzu/game_list_worker.cpp | 11 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 48 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.h | 12 |
19 files changed, 364 insertions, 85 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 6f0ff953a..23e5d3f10 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -30,7 +30,7 @@ public: | |||
| 30 | return info; | 30 | return info; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | VoiceInfo& Info() { | 33 | VoiceInfo& GetInfo() { |
| 34 | return info; | 34 | return info; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -51,9 +51,30 @@ private: | |||
| 51 | VoiceInfo info{}; | 51 | VoiceInfo info{}; |
| 52 | }; | 52 | }; |
| 53 | 53 | ||
| 54 | class AudioRenderer::EffectState { | ||
| 55 | public: | ||
| 56 | const EffectOutStatus& GetOutStatus() const { | ||
| 57 | return out_status; | ||
| 58 | } | ||
| 59 | |||
| 60 | const EffectInStatus& GetInfo() const { | ||
| 61 | return info; | ||
| 62 | } | ||
| 63 | |||
| 64 | EffectInStatus& GetInfo() { | ||
| 65 | return info; | ||
| 66 | } | ||
| 67 | |||
| 68 | void UpdateState(); | ||
| 69 | |||
| 70 | private: | ||
| 71 | EffectOutStatus out_status{}; | ||
| 72 | EffectInStatus info{}; | ||
| 73 | }; | ||
| 54 | AudioRenderer::AudioRenderer(AudioRendererParameter params, | 74 | AudioRenderer::AudioRenderer(AudioRendererParameter params, |
| 55 | Kernel::SharedPtr<Kernel::Event> buffer_event) | 75 | Kernel::SharedPtr<Kernel::Event> buffer_event) |
| 56 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { | 76 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), |
| 77 | effects(params.effect_count) { | ||
| 57 | 78 | ||
| 58 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 79 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 59 | stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", | 80 | stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", |
| @@ -96,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 96 | memory_pool_count * sizeof(MemoryPoolInfo)); | 117 | memory_pool_count * sizeof(MemoryPoolInfo)); |
| 97 | 118 | ||
| 98 | // Copy VoiceInfo structs | 119 | // Copy VoiceInfo structs |
| 99 | std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + | 120 | std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + |
| 100 | config.voice_resource_size}; | 121 | config.memory_pools_size + config.voice_resource_size}; |
| 101 | for (auto& voice : voices) { | 122 | for (auto& voice : voices) { |
| 102 | std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); | 123 | std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo)); |
| 103 | offset += sizeof(VoiceInfo); | 124 | voice_offset += sizeof(VoiceInfo); |
| 125 | } | ||
| 126 | |||
| 127 | std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size + | ||
| 128 | config.memory_pools_size + config.voice_resource_size + | ||
| 129 | config.voices_size}; | ||
| 130 | for (auto& effect : effects) { | ||
| 131 | std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus)); | ||
| 132 | effect_offset += sizeof(EffectInStatus); | ||
| 133 | } | ||
| 134 | |||
| 135 | // Update memory pool state | ||
| 136 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | ||
| 137 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { | ||
| 138 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | ||
| 139 | memory_pool[index].state = MemoryPoolStates::Attached; | ||
| 140 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | ||
| 141 | memory_pool[index].state = MemoryPoolStates::Detached; | ||
| 142 | } | ||
| 104 | } | 143 | } |
| 105 | 144 | ||
| 106 | // Update voices | 145 | // Update voices |
| @@ -114,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 114 | } | 153 | } |
| 115 | } | 154 | } |
| 116 | 155 | ||
| 117 | // Update memory pool state | 156 | for (auto& effect : effects) { |
| 118 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | 157 | effect.UpdateState(); |
| 119 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { | ||
| 120 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | ||
| 121 | memory_pool[index].state = MemoryPoolStates::Attached; | ||
| 122 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | ||
| 123 | memory_pool[index].state = MemoryPoolStates::Detached; | ||
| 124 | } | ||
| 125 | } | 158 | } |
| 126 | 159 | ||
| 127 | // Release previous buffers and queue next ones for playback | 160 | // Release previous buffers and queue next ones for playback |
| @@ -144,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 144 | voice_out_status_offset += sizeof(VoiceOutStatus); | 177 | voice_out_status_offset += sizeof(VoiceOutStatus); |
| 145 | } | 178 | } |
| 146 | 179 | ||
| 180 | std::size_t effect_out_status_offset{ | ||
| 181 | sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + | ||
| 182 | response_data.voice_resource_size}; | ||
| 183 | for (const auto& effect : effects) { | ||
| 184 | std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(), | ||
| 185 | sizeof(EffectOutStatus)); | ||
| 186 | effect_out_status_offset += sizeof(EffectOutStatus); | ||
| 187 | } | ||
| 147 | return output_params; | 188 | return output_params; |
| 148 | } | 189 | } |
| 149 | 190 | ||
| @@ -244,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 244 | break; | 285 | break; |
| 245 | } | 286 | } |
| 246 | 287 | ||
| 247 | samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); | 288 | samples = |
| 289 | Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE); | ||
| 248 | 290 | ||
| 249 | is_refresh_pending = false; | 291 | is_refresh_pending = false; |
| 250 | } | 292 | } |
| 251 | 293 | ||
| 294 | void AudioRenderer::EffectState::UpdateState() { | ||
| 295 | if (info.is_new) { | ||
| 296 | out_status.state = EffectStatus::New; | ||
| 297 | } else { | ||
| 298 | if (info.type == Effect::Aux) { | ||
| 299 | ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0, | ||
| 300 | "Aux buffers tried to update"); | ||
| 301 | ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0, | ||
| 302 | "Aux buffers tried to update"); | ||
| 303 | ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0, | ||
| 304 | "Aux buffers tried to update"); | ||
| 305 | ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0, | ||
| 306 | "Aux buffers tried to update"); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 252 | static constexpr s16 ClampToS16(s32 value) { | 311 | static constexpr s16 ClampToS16(s32 value) { |
| 253 | return static_cast<s16>(std::clamp(value, -32768, 32767)); | 312 | return static_cast<s16>(std::clamp(value, -32768, 32767)); |
| 254 | } | 313 | } |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index dfef89e1d..046417da3 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -28,6 +28,16 @@ enum class PlayState : u8 { | |||
| 28 | Paused = 2, | 28 | Paused = 2, |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | enum class Effect : u8 { | ||
| 32 | None = 0, | ||
| 33 | Aux = 2, | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum class EffectStatus : u8 { | ||
| 37 | None = 0, | ||
| 38 | New = 1, | ||
| 39 | }; | ||
| 40 | |||
| 31 | struct AudioRendererParameter { | 41 | struct AudioRendererParameter { |
| 32 | u32_le sample_rate; | 42 | u32_le sample_rate; |
| 33 | u32_le sample_count; | 43 | u32_le sample_count; |
| @@ -128,6 +138,43 @@ struct VoiceOutStatus { | |||
| 128 | }; | 138 | }; |
| 129 | static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); | 139 | static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); |
| 130 | 140 | ||
| 141 | struct AuxInfo { | ||
| 142 | std::array<u8, 24> input_mix_buffers; | ||
| 143 | std::array<u8, 24> output_mix_buffers; | ||
| 144 | u32_le mix_buffer_count; | ||
| 145 | u32_le sample_rate; // Stored in the aux buffer currently | ||
| 146 | u32_le sampe_count; | ||
| 147 | u64_le send_buffer_info; | ||
| 148 | u64_le send_buffer_base; | ||
| 149 | |||
| 150 | u64_le return_buffer_info; | ||
| 151 | u64_le return_buffer_base; | ||
| 152 | }; | ||
| 153 | static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size"); | ||
| 154 | |||
| 155 | struct EffectInStatus { | ||
| 156 | Effect type; | ||
| 157 | u8 is_new; | ||
| 158 | u8 is_enabled; | ||
| 159 | INSERT_PADDING_BYTES(1); | ||
| 160 | u32_le mix_id; | ||
| 161 | u64_le buffer_base; | ||
| 162 | u64_le buffer_sz; | ||
| 163 | s32_le priority; | ||
| 164 | INSERT_PADDING_BYTES(4); | ||
| 165 | union { | ||
| 166 | std::array<u8, 0xa0> raw; | ||
| 167 | AuxInfo aux_info; | ||
| 168 | }; | ||
| 169 | }; | ||
| 170 | static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size"); | ||
| 171 | |||
| 172 | struct EffectOutStatus { | ||
| 173 | EffectStatus state; | ||
| 174 | INSERT_PADDING_BYTES(0xf); | ||
| 175 | }; | ||
| 176 | static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); | ||
| 177 | |||
| 131 | struct UpdateDataHeader { | 178 | struct UpdateDataHeader { |
| 132 | UpdateDataHeader() {} | 179 | UpdateDataHeader() {} |
| 133 | 180 | ||
| @@ -173,11 +220,13 @@ public: | |||
| 173 | Stream::State GetStreamState() const; | 220 | Stream::State GetStreamState() const; |
| 174 | 221 | ||
| 175 | private: | 222 | private: |
| 223 | class EffectState; | ||
| 176 | class VoiceState; | 224 | class VoiceState; |
| 177 | 225 | ||
| 178 | AudioRendererParameter worker_params; | 226 | AudioRendererParameter worker_params; |
| 179 | Kernel::SharedPtr<Kernel::Event> buffer_event; | 227 | Kernel::SharedPtr<Kernel::Event> buffer_event; |
| 180 | std::vector<VoiceState> voices; | 228 | std::vector<VoiceState> voices; |
| 229 | std::vector<EffectState> effects; | ||
| 181 | std::unique_ptr<AudioOut> audio_out; | 230 | std::unique_ptr<AudioOut> audio_out; |
| 182 | AudioCore::StreamPtr stream; | 231 | AudioCore::StreamPtr stream; |
| 183 | }; | 232 | }; |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 184509716..554eae9bc 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -2,9 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <map> | ||
| 5 | #include <sstream> | 8 | #include <sstream> |
| 6 | #include "common/assert.h" | 9 | #include <string> |
| 10 | #include <utility> | ||
| 11 | |||
| 7 | #include "common/hex_util.h" | 12 | #include "common/hex_util.h" |
| 13 | #include "common/logging/log.h" | ||
| 8 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 9 | #include "core/file_sys/ips_layer.h" | 15 | #include "core/file_sys/ips_layer.h" |
| 10 | #include "core/file_sys/vfs_vector.h" | 16 | #include "core/file_sys/vfs_vector.h" |
| @@ -17,22 +23,48 @@ enum class IPSFileType { | |||
| 17 | Error, | 23 | Error, |
| 18 | }; | 24 | }; |
| 19 | 25 | ||
| 20 | constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{ | 26 | constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{ |
| 21 | std::pair{"\\a", "\a"}, {"\\b", "\b"}, {"\\f", "\f"}, {"\\n", "\n"}, | 27 | {"\\a", "\a"}, |
| 22 | {"\\r", "\r"}, {"\\t", "\t"}, {"\\v", "\v"}, {"\\\\", "\\"}, | 28 | {"\\b", "\b"}, |
| 23 | {"\\\'", "\'"}, {"\\\"", "\""}, {"\\\?", "\?"}, | 29 | {"\\f", "\f"}, |
| 24 | }; | 30 | {"\\n", "\n"}, |
| 31 | {"\\r", "\r"}, | ||
| 32 | {"\\t", "\t"}, | ||
| 33 | {"\\v", "\v"}, | ||
| 34 | {"\\\\", "\\"}, | ||
| 35 | {"\\\'", "\'"}, | ||
| 36 | {"\\\"", "\""}, | ||
| 37 | {"\\\?", "\?"}, | ||
| 38 | }}; | ||
| 25 | 39 | ||
| 26 | static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { | 40 | static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { |
| 27 | if (magic.size() != 5) | 41 | if (magic.size() != 5) { |
| 28 | return IPSFileType::Error; | 42 | return IPSFileType::Error; |
| 29 | if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) | 43 | } |
| 44 | |||
| 45 | constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}}; | ||
| 46 | if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) { | ||
| 30 | return IPSFileType::IPS; | 47 | return IPSFileType::IPS; |
| 31 | if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) | 48 | } |
| 49 | |||
| 50 | constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}}; | ||
| 51 | if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) { | ||
| 32 | return IPSFileType::IPS32; | 52 | return IPSFileType::IPS32; |
| 53 | } | ||
| 54 | |||
| 33 | return IPSFileType::Error; | 55 | return IPSFileType::Error; |
| 34 | } | 56 | } |
| 35 | 57 | ||
| 58 | static bool IsEOF(IPSFileType type, const std::vector<u8>& data) { | ||
| 59 | constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}}; | ||
| 60 | if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) { | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}}; | ||
| 65 | return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin()); | ||
| 66 | } | ||
| 67 | |||
| 36 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | 68 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { |
| 37 | if (in == nullptr || ips == nullptr) | 69 | if (in == nullptr || ips == nullptr) |
| 38 | return nullptr; | 70 | return nullptr; |
| @@ -47,8 +79,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 47 | u64 offset = 5; // After header | 79 | u64 offset = 5; // After header |
| 48 | while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { | 80 | while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { |
| 49 | offset += temp.size(); | 81 | offset += temp.size(); |
| 50 | if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || | 82 | if (IsEOF(type, temp)) { |
| 51 | type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) { | ||
| 52 | break; | 83 | break; |
| 53 | } | 84 | } |
| 54 | 85 | ||
| @@ -88,11 +119,20 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 88 | } | 119 | } |
| 89 | } | 120 | } |
| 90 | 121 | ||
| 91 | if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) | 122 | if (!IsEOF(type, temp)) { |
| 92 | return nullptr; | 123 | return nullptr; |
| 93 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); | 124 | } |
| 125 | |||
| 126 | return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), | ||
| 127 | in->GetContainingDirectory()); | ||
| 94 | } | 128 | } |
| 95 | 129 | ||
| 130 | struct IPSwitchCompiler::IPSwitchPatch { | ||
| 131 | std::string name; | ||
| 132 | bool enabled; | ||
| 133 | std::map<u32, std::vector<u8>> records; | ||
| 134 | }; | ||
| 135 | |||
| 96 | IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) { | 136 | IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) { |
| 97 | Parse(); | 137 | Parse(); |
| 98 | } | 138 | } |
| @@ -291,7 +331,8 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { | |||
| 291 | } | 331 | } |
| 292 | } | 332 | } |
| 293 | 333 | ||
| 294 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); | 334 | return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), |
| 335 | in->GetContainingDirectory()); | ||
| 295 | } | 336 | } |
| 296 | 337 | ||
| 297 | } // namespace FileSys | 338 | } // namespace FileSys |
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index 57da00da8..450b2f71e 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h | |||
| @@ -4,8 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | ||
| 8 | 10 | ||
| 11 | #include "common/common_types.h" | ||
| 9 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 10 | 13 | ||
| 11 | namespace FileSys { | 14 | namespace FileSys { |
| @@ -22,17 +25,13 @@ public: | |||
| 22 | VirtualFile Apply(const VirtualFile& in) const; | 25 | VirtualFile Apply(const VirtualFile& in) const; |
| 23 | 26 | ||
| 24 | private: | 27 | private: |
| 28 | struct IPSwitchPatch; | ||
| 29 | |||
| 25 | void ParseFlag(const std::string& flag); | 30 | void ParseFlag(const std::string& flag); |
| 26 | void Parse(); | 31 | void Parse(); |
| 27 | 32 | ||
| 28 | bool valid = false; | 33 | bool valid = false; |
| 29 | 34 | ||
| 30 | struct IPSwitchPatch { | ||
| 31 | std::string name; | ||
| 32 | bool enabled; | ||
| 33 | std::map<u32, std::vector<u8>> records; | ||
| 34 | }; | ||
| 35 | |||
| 36 | VirtualFile patch_text; | 35 | VirtualFile patch_text; |
| 37 | std::vector<IPSwitchPatch> patches; | 36 | std::vector<IPSwitchPatch> patches; |
| 38 | std::array<u8, 0x20> nso_build_id{}; | 37 | std::array<u8, 0x20> nso_build_id{}; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index b14d7cb0a..019caebe9 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -345,23 +345,22 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 345 | return out; | 345 | return out; |
| 346 | } | 346 | } |
| 347 | 347 | ||
| 348 | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 348 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { |
| 349 | const auto& installed{Service::FileSystem::GetUnionContents()}; | 349 | const auto& installed{Service::FileSystem::GetUnionContents()}; |
| 350 | 350 | ||
| 351 | const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); | 351 | const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); |
| 352 | if (base_control_nca == nullptr) | 352 | if (base_control_nca == nullptr) |
| 353 | return {}; | 353 | return {}; |
| 354 | 354 | ||
| 355 | return ParseControlNCA(base_control_nca); | 355 | return ParseControlNCA(*base_control_nca); |
| 356 | } | 356 | } |
| 357 | 357 | ||
| 358 | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | 358 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { |
| 359 | const std::shared_ptr<NCA>& nca) const { | 359 | const auto base_romfs = nca.GetRomFS(); |
| 360 | const auto base_romfs = nca->GetRomFS(); | ||
| 361 | if (base_romfs == nullptr) | 360 | if (base_romfs == nullptr) |
| 362 | return {}; | 361 | return {}; |
| 363 | 362 | ||
| 364 | const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); | 363 | const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); |
| 365 | if (romfs == nullptr) | 364 | if (romfs == nullptr) |
| 366 | return {}; | 365 | return {}; |
| 367 | 366 | ||
| @@ -373,7 +372,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | |||
| 373 | if (nacp_file == nullptr) | 372 | if (nacp_file == nullptr) |
| 374 | nacp_file = extracted->GetFile("Control.nacp"); | 373 | nacp_file = extracted->GetFile("Control.nacp"); |
| 375 | 374 | ||
| 376 | const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); | 375 | auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); |
| 377 | 376 | ||
| 378 | VirtualFile icon_file; | 377 | VirtualFile icon_file; |
| 379 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | 378 | for (const auto& language : FileSys::LANGUAGE_NAMES) { |
| @@ -382,6 +381,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | |||
| 382 | break; | 381 | break; |
| 383 | } | 382 | } |
| 384 | 383 | ||
| 385 | return {nacp, icon_file}; | 384 | return {std::move(nacp), icon_file}; |
| 386 | } | 385 | } |
| 387 | } // namespace FileSys | 386 | } // namespace FileSys |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index eb6fc4607..7d168837f 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -57,11 +57,10 @@ public: | |||
| 57 | 57 | ||
| 58 | // Given title_id of the program, attempts to get the control data of the update and parse it, | 58 | // Given title_id of the program, attempts to get the control data of the update and parse it, |
| 59 | // falling back to the base control data. | 59 | // falling back to the base control data. |
| 60 | std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const; | 60 | std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; |
| 61 | 61 | ||
| 62 | // Version of GetControlMetadata that takes an arbitrary NCA | 62 | // Version of GetControlMetadata that takes an arbitrary NCA |
| 63 | std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA( | 63 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; |
| 64 | const std::shared_ptr<NCA>& nca) const; | ||
| 65 | 64 | ||
| 66 | private: | 65 | private: |
| 67 | u64 title_id; | 66 | u64 title_id; |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 6c4af7e47..b488b508d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -301,13 +301,28 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 301 | return Mutex::Release(mutex_addr); | 301 | return Mutex::Release(mutex_addr); |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | struct BreakReason { | ||
| 305 | union { | ||
| 306 | u64 raw; | ||
| 307 | BitField<31, 1, u64> dont_kill_application; | ||
| 308 | }; | ||
| 309 | }; | ||
| 310 | |||
| 304 | /// Break program execution | 311 | /// Break program execution |
| 305 | static void Break(u64 reason, u64 info1, u64 info2) { | 312 | static void Break(u64 reason, u64 info1, u64 info2) { |
| 306 | LOG_CRITICAL( | 313 | BreakReason break_reason{reason}; |
| 307 | Debug_Emulated, | 314 | if (break_reason.dont_kill_application) { |
| 308 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 315 | LOG_ERROR( |
| 309 | reason, info1, info2); | 316 | Debug_Emulated, |
| 310 | ASSERT(false); | 317 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| 318 | reason, info1, info2); | ||
| 319 | } else { | ||
| 320 | LOG_CRITICAL( | ||
| 321 | Debug_Emulated, | ||
| 322 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 323 | reason, info1, info2); | ||
| 324 | ASSERT(false); | ||
| 325 | } | ||
| 311 | } | 326 | } |
| 312 | 327 | ||
| 313 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 328 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 5534ce01c..13e57848d 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -35,7 +35,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) | |||
| 35 | return; | 35 | return; |
| 36 | 36 | ||
| 37 | std::tie(nacp_file, icon_file) = | 37 | std::tie(nacp_file, icon_file) = |
| 38 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); | 38 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | AppLoader_NSP::~AppLoader_NSP() = default; | 41 | AppLoader_NSP::~AppLoader_NSP() = default; |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index b006594a6..db91cd01e 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -49,7 +49,7 @@ private: | |||
| 49 | std::unique_ptr<AppLoader> secondary_loader; | 49 | std::unique_ptr<AppLoader> secondary_loader; |
| 50 | 50 | ||
| 51 | FileSys::VirtualFile icon_file; | 51 | FileSys::VirtualFile icon_file; |
| 52 | std::shared_ptr<FileSys::NACP> nacp_file; | 52 | std::unique_ptr<FileSys::NACP> nacp_file; |
| 53 | u64 title_id; | 53 | u64 title_id; |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index ee5452eb9..7a619acb4 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -30,7 +30,7 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | |||
| 30 | return; | 30 | return; |
| 31 | 31 | ||
| 32 | std::tie(nacp_file, icon_file) = | 32 | std::tie(nacp_file, icon_file) = |
| 33 | FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); | 33 | FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | AppLoader_XCI::~AppLoader_XCI() = default; | 36 | AppLoader_XCI::~AppLoader_XCI() = default; |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 770ed1437..46f8dfc9e 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -49,7 +49,7 @@ private: | |||
| 49 | std::unique_ptr<AppLoader_NCA> nca_loader; | 49 | std::unique_ptr<AppLoader_NCA> nca_loader; |
| 50 | 50 | ||
| 51 | FileSys::VirtualFile icon_file; | 51 | FileSys::VirtualFile icon_file; |
| 52 | std::shared_ptr<FileSys::NACP> nacp_file; | 52 | std::unique_ptr<FileSys::NACP> nacp_file; |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | } // namespace Loader | 55 | } // namespace Loader |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index f29fff1e7..7b04792b5 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -2,12 +2,16 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 6 | |||
| 7 | #include <mbedtls/ctr_drbg.h> | ||
| 8 | #include <mbedtls/entropy.h> | ||
| 9 | |||
| 5 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 7 | #include "common/file_util.h" | 12 | #include "common/file_util.h" |
| 13 | #include "common/logging/log.h" | ||
| 8 | 14 | ||
| 9 | #include <mbedtls/ctr_drbg.h> | ||
| 10 | #include <mbedtls/entropy.h> | ||
| 11 | #include "core/core.h" | 15 | #include "core/core.h" |
| 12 | #include "core/file_sys/control_metadata.h" | 16 | #include "core/file_sys/control_metadata.h" |
| 13 | #include "core/file_sys/patch_manager.h" | 17 | #include "core/file_sys/patch_manager.h" |
| @@ -28,11 +32,11 @@ static u64 GenerateTelemetryId() { | |||
| 28 | mbedtls_entropy_context entropy; | 32 | mbedtls_entropy_context entropy; |
| 29 | mbedtls_entropy_init(&entropy); | 33 | mbedtls_entropy_init(&entropy); |
| 30 | mbedtls_ctr_drbg_context ctr_drbg; | 34 | mbedtls_ctr_drbg_context ctr_drbg; |
| 31 | std::string personalization = "yuzu Telemetry ID"; | 35 | constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}}; |
| 32 | 36 | ||
| 33 | mbedtls_ctr_drbg_init(&ctr_drbg); | 37 | mbedtls_ctr_drbg_init(&ctr_drbg); |
| 34 | ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, | 38 | ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, |
| 35 | reinterpret_cast<const unsigned char*>(personalization.c_str()), | 39 | reinterpret_cast<const unsigned char*>(personalization.data()), |
| 36 | personalization.size()) == 0); | 40 | personalization.size()) == 0); |
| 37 | ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), | 41 | ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), |
| 38 | sizeof(u64)) == 0); | 42 | sizeof(u64)) == 0); |
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index cec271df0..2a4845797 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | ||
| 8 | #include "common/telemetry.h" | 9 | #include "common/telemetry.h" |
| 9 | 10 | ||
| 10 | namespace Core { | 11 | namespace Core { |
| @@ -30,8 +31,6 @@ public: | |||
| 30 | field_collection.AddField(type, name, std::move(value)); | 31 | field_collection.AddField(type, name, std::move(value)); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | static void FinalizeAsyncJob(); | ||
| 34 | |||
| 35 | private: | 34 | private: |
| 36 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session | 35 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session |
| 37 | std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields | 36 | std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields |
| @@ -53,7 +52,6 @@ u64 RegenerateTelemetryId(); | |||
| 53 | * Verifies the username and token. | 52 | * Verifies the username and token. |
| 54 | * @param username yuzu username to use for authentication. | 53 | * @param username yuzu username to use for authentication. |
| 55 | * @param token yuzu token to use for authentication. | 54 | * @param token yuzu token to use for authentication. |
| 56 | * @param func A function that gets exectued when the verification is finished | ||
| 57 | * @returns Future with bool indicating whether the verification succeeded | 55 | * @returns Future with bool indicating whether the verification succeeded |
| 58 | */ | 56 | */ |
| 59 | bool VerifyLogin(const std::string& username, const std::string& token); | 57 | bool VerifyLogin(const std::string& username, const std::string& token); |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 7e57de78a..85c668ca1 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -2299,8 +2299,7 @@ private: | |||
| 2299 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | 2299 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), |
| 2300 | "NDV is not implemented"); | 2300 | "NDV is not implemented"); |
| 2301 | 2301 | ||
| 2302 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 2302 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2303 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2304 | const bool is_array = instr.tmml.array != 0; | 2303 | const bool is_array = instr.tmml.array != 0; |
| 2305 | auto texture_type = instr.tmml.texture_type.Value(); | 2304 | auto texture_type = instr.tmml.texture_type.Value(); |
| 2306 | const std::string sampler = | 2305 | const std::string sampler = |
| @@ -2311,13 +2310,11 @@ private: | |||
| 2311 | std::string coord; | 2310 | std::string coord; |
| 2312 | switch (texture_type) { | 2311 | switch (texture_type) { |
| 2313 | case Tegra::Shader::TextureType::Texture1D: { | 2312 | case Tegra::Shader::TextureType::Texture1D: { |
| 2314 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2315 | coord = "float coords = " + x + ';'; | 2313 | coord = "float coords = " + x + ';'; |
| 2316 | break; | 2314 | break; |
| 2317 | } | 2315 | } |
| 2318 | case Tegra::Shader::TextureType::Texture2D: { | 2316 | case Tegra::Shader::TextureType::Texture2D: { |
| 2319 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2317 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2320 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2321 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2318 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; |
| 2322 | break; | 2319 | break; |
| 2323 | } | 2320 | } |
| @@ -2327,8 +2324,7 @@ private: | |||
| 2327 | UNREACHABLE(); | 2324 | UNREACHABLE(); |
| 2328 | 2325 | ||
| 2329 | // Fallback to interpreting as a 2D texture for now | 2326 | // Fallback to interpreting as a 2D texture for now |
| 2330 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2327 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2331 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2332 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2328 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; |
| 2333 | texture_type = Tegra::Shader::TextureType::Texture2D; | 2329 | texture_type = Tegra::Shader::TextureType::Texture2D; |
| 2334 | } | 2330 | } |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 4e4c108ab..e8ab23326 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | |||
| 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, |
| 111 | Common::g_scm_branch, Common::g_scm_desc); | 111 | Common::g_scm_branch, Common::g_scm_desc); |
| 112 | setWindowTitle(QString::fromStdString(window_title)); | 112 | setWindowTitle(QString::fromStdString(window_title)); |
| 113 | setAttribute(Qt::WA_AcceptTouchEvents); | ||
| 113 | 114 | ||
| 114 | InputCommon::Init(); | 115 | InputCommon::Init(); |
| 115 | InputCommon::StartJoystickEventHandler(); | 116 | InputCommon::StartJoystickEventHandler(); |
| @@ -190,11 +191,17 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 190 | return geometry; | 191 | return geometry; |
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | qreal GRenderWindow::windowPixelRatio() { | 194 | qreal GRenderWindow::windowPixelRatio() const { |
| 194 | // windowHandle() might not be accessible until the window is displayed to screen. | 195 | // windowHandle() might not be accessible until the window is displayed to screen. |
| 195 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | 196 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |
| 196 | } | 197 | } |
| 197 | 198 | ||
| 199 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | ||
| 200 | const qreal pixel_ratio = windowPixelRatio(); | ||
| 201 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | ||
| 202 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | ||
| 203 | } | ||
| 204 | |||
| 198 | void GRenderWindow::closeEvent(QCloseEvent* event) { | 205 | void GRenderWindow::closeEvent(QCloseEvent* event) { |
| 199 | emit Closed(); | 206 | emit Closed(); |
| 200 | QWidget::closeEvent(event); | 207 | QWidget::closeEvent(event); |
| @@ -209,31 +216,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 209 | } | 216 | } |
| 210 | 217 | ||
| 211 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 218 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 219 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 220 | return; // touch input is handled in TouchBeginEvent | ||
| 221 | |||
| 212 | auto pos = event->pos(); | 222 | auto pos = event->pos(); |
| 213 | if (event->button() == Qt::LeftButton) { | 223 | if (event->button() == Qt::LeftButton) { |
| 214 | qreal pixelRatio = windowPixelRatio(); | 224 | const auto [x, y] = ScaleTouch(pos); |
| 215 | this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), | 225 | this->TouchPressed(x, y); |
| 216 | static_cast<unsigned>(pos.y() * pixelRatio)); | ||
| 217 | } else if (event->button() == Qt::RightButton) { | 226 | } else if (event->button() == Qt::RightButton) { |
| 218 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | 227 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); |
| 219 | } | 228 | } |
| 220 | } | 229 | } |
| 221 | 230 | ||
| 222 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 231 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| 232 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 233 | return; // touch input is handled in TouchUpdateEvent | ||
| 234 | |||
| 223 | auto pos = event->pos(); | 235 | auto pos = event->pos(); |
| 224 | qreal pixelRatio = windowPixelRatio(); | 236 | const auto [x, y] = ScaleTouch(pos); |
| 225 | this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), | 237 | this->TouchMoved(x, y); |
| 226 | std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); | ||
| 227 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | 238 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); |
| 228 | } | 239 | } |
| 229 | 240 | ||
| 230 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 241 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 242 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 243 | return; // touch input is handled in TouchEndEvent | ||
| 244 | |||
| 231 | if (event->button() == Qt::LeftButton) | 245 | if (event->button() == Qt::LeftButton) |
| 232 | this->TouchReleased(); | 246 | this->TouchReleased(); |
| 233 | else if (event->button() == Qt::RightButton) | 247 | else if (event->button() == Qt::RightButton) |
| 234 | InputCommon::GetMotionEmu()->EndTilt(); | 248 | InputCommon::GetMotionEmu()->EndTilt(); |
| 235 | } | 249 | } |
| 236 | 250 | ||
| 251 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | ||
| 252 | // TouchBegin always has exactly one touch point, so take the .first() | ||
| 253 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | ||
| 254 | this->TouchPressed(x, y); | ||
| 255 | } | ||
| 256 | |||
| 257 | void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { | ||
| 258 | QPointF pos; | ||
| 259 | int active_points = 0; | ||
| 260 | |||
| 261 | // average all active touch points | ||
| 262 | for (const auto tp : event->touchPoints()) { | ||
| 263 | if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { | ||
| 264 | active_points++; | ||
| 265 | pos += tp.pos(); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | pos /= active_points; | ||
| 270 | |||
| 271 | const auto [x, y] = ScaleTouch(pos); | ||
| 272 | this->TouchMoved(x, y); | ||
| 273 | } | ||
| 274 | |||
| 275 | void GRenderWindow::TouchEndEvent() { | ||
| 276 | this->TouchReleased(); | ||
| 277 | } | ||
| 278 | |||
| 279 | bool GRenderWindow::event(QEvent* event) { | ||
| 280 | if (event->type() == QEvent::TouchBegin) { | ||
| 281 | TouchBeginEvent(static_cast<QTouchEvent*>(event)); | ||
| 282 | return true; | ||
| 283 | } else if (event->type() == QEvent::TouchUpdate) { | ||
| 284 | TouchUpdateEvent(static_cast<QTouchEvent*>(event)); | ||
| 285 | return true; | ||
| 286 | } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) { | ||
| 287 | TouchEndEvent(); | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | return QWidget::event(event); | ||
| 292 | } | ||
| 293 | |||
| 237 | void GRenderWindow::focusOutEvent(QFocusEvent* event) { | 294 | void GRenderWindow::focusOutEvent(QFocusEvent* event) { |
| 238 | QWidget::focusOutEvent(event); | 295 | QWidget::focusOutEvent(event); |
| 239 | InputCommon::GetKeyboard()->ReleaseAllKeys(); | 296 | InputCommon::GetKeyboard()->ReleaseAllKeys(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index f133bfadf..873985564 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | class QKeyEvent; | 16 | class QKeyEvent; |
| 17 | class QScreen; | 17 | class QScreen; |
| 18 | class QTouchEvent; | ||
| 18 | 19 | ||
| 19 | class GGLWidgetInternal; | 20 | class GGLWidgetInternal; |
| 20 | class GMainWindow; | 21 | class GMainWindow; |
| @@ -119,7 +120,7 @@ public: | |||
| 119 | void restoreGeometry(const QByteArray& geometry); // overridden | 120 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 120 | QByteArray saveGeometry(); // overridden | 121 | QByteArray saveGeometry(); // overridden |
| 121 | 122 | ||
| 122 | qreal windowPixelRatio(); | 123 | qreal windowPixelRatio() const; |
| 123 | 124 | ||
| 124 | void closeEvent(QCloseEvent* event) override; | 125 | void closeEvent(QCloseEvent* event) override; |
| 125 | 126 | ||
| @@ -130,6 +131,8 @@ public: | |||
| 130 | void mouseMoveEvent(QMouseEvent* event) override; | 131 | void mouseMoveEvent(QMouseEvent* event) override; |
| 131 | void mouseReleaseEvent(QMouseEvent* event) override; | 132 | void mouseReleaseEvent(QMouseEvent* event) override; |
| 132 | 133 | ||
| 134 | bool event(QEvent* event) override; | ||
| 135 | |||
| 133 | void focusOutEvent(QFocusEvent* event) override; | 136 | void focusOutEvent(QFocusEvent* event) override; |
| 134 | 137 | ||
| 135 | void OnClientAreaResized(unsigned width, unsigned height); | 138 | void OnClientAreaResized(unsigned width, unsigned height); |
| @@ -148,6 +151,11 @@ signals: | |||
| 148 | void Closed(); | 151 | void Closed(); |
| 149 | 152 | ||
| 150 | private: | 153 | private: |
| 154 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 155 | void TouchBeginEvent(const QTouchEvent* event); | ||
| 156 | void TouchUpdateEvent(const QTouchEvent* event); | ||
| 157 | void TouchEndEvent(); | ||
| 158 | |||
| 151 | void OnMinimalClientAreaChangeRequest( | 159 | void OnMinimalClientAreaChangeRequest( |
| 152 | const std::pair<unsigned, unsigned>& minimal_size) override; | 160 | const std::pair<unsigned, unsigned>& minimal_size) override; |
| 153 | 161 | ||
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index d2b3de683..8f99a1c78 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -27,9 +27,8 @@ | |||
| 27 | #include "yuzu/ui_settings.h" | 27 | #include "yuzu/ui_settings.h" |
| 28 | 28 | ||
| 29 | namespace { | 29 | namespace { |
| 30 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, | 30 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca, |
| 31 | const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, | 31 | std::vector<u8>& icon, std::string& name) { |
| 32 | std::string& name) { | ||
| 33 | auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); | 32 | auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); |
| 34 | if (icon_file != nullptr) | 33 | if (icon_file != nullptr) |
| 35 | icon = icon_file->ReadAllBytes(); | 34 | icon = icon_file->ReadAllBytes(); |
| @@ -110,7 +109,7 @@ void GameListWorker::AddInstalledTitlesToGameList() { | |||
| 110 | const FileSys::PatchManager patch{program_id}; | 109 | const FileSys::PatchManager patch{program_id}; |
| 111 | const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); | 110 | const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); |
| 112 | if (control != nullptr) | 111 | if (control != nullptr) |
| 113 | GetMetadataFromControlNCA(patch, control, icon, name); | 112 | GetMetadataFromControlNCA(patch, *control, icon, name); |
| 114 | 113 | ||
| 115 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 114 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 116 | 115 | ||
| @@ -197,8 +196,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 197 | res2 == Loader::ResultStatus::Success) { | 196 | res2 == Loader::ResultStatus::Success) { |
| 198 | // Use from metadata pool. | 197 | // Use from metadata pool. |
| 199 | if (nca_control_map.find(program_id) != nca_control_map.end()) { | 198 | if (nca_control_map.find(program_id) != nca_control_map.end()) { |
| 200 | const auto nca = nca_control_map[program_id]; | 199 | const auto& nca = nca_control_map[program_id]; |
| 201 | GetMetadataFromControlNCA(patch, nca, icon, name); | 200 | GetMetadataFromControlNCA(patch, *nca, icon, name); |
| 202 | } | 201 | } |
| 203 | } | 202 | } |
| 204 | 203 | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 155095095..a9ad92a80 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -40,6 +40,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | |||
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { | ||
| 44 | int w, h; | ||
| 45 | SDL_GetWindowSize(render_window, &w, &h); | ||
| 46 | |||
| 47 | touch_x *= w; | ||
| 48 | touch_y *= h; | ||
| 49 | |||
| 50 | return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)), | ||
| 51 | static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; | ||
| 52 | } | ||
| 53 | |||
| 54 | void EmuWindow_SDL2::OnFingerDown(float x, float y) { | ||
| 55 | // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind | ||
| 56 | // This isn't critical because the best we can do when we have that is to average them, like the | ||
| 57 | // 3DS does | ||
| 58 | |||
| 59 | const auto [px, py] = TouchToPixelPos(x, y); | ||
| 60 | TouchPressed(px, py); | ||
| 61 | } | ||
| 62 | |||
| 63 | void EmuWindow_SDL2::OnFingerMotion(float x, float y) { | ||
| 64 | const auto [px, py] = TouchToPixelPos(x, y); | ||
| 65 | TouchMoved(px, py); | ||
| 66 | } | ||
| 67 | |||
| 68 | void EmuWindow_SDL2::OnFingerUp() { | ||
| 69 | TouchReleased(); | ||
| 70 | } | ||
| 71 | |||
| 43 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | 72 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { |
| 44 | if (state == SDL_PRESSED) { | 73 | if (state == SDL_PRESSED) { |
| 45 | InputCommon::GetKeyboard()->PressKey(key); | 74 | InputCommon::GetKeyboard()->PressKey(key); |
| @@ -219,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 219 | OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); | 248 | OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); |
| 220 | break; | 249 | break; |
| 221 | case SDL_MOUSEMOTION: | 250 | case SDL_MOUSEMOTION: |
| 222 | OnMouseMotion(event.motion.x, event.motion.y); | 251 | // ignore if it came from touch |
| 252 | if (event.button.which != SDL_TOUCH_MOUSEID) | ||
| 253 | OnMouseMotion(event.motion.x, event.motion.y); | ||
| 223 | break; | 254 | break; |
| 224 | case SDL_MOUSEBUTTONDOWN: | 255 | case SDL_MOUSEBUTTONDOWN: |
| 225 | case SDL_MOUSEBUTTONUP: | 256 | case SDL_MOUSEBUTTONUP: |
| 226 | OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); | 257 | // ignore if it came from touch |
| 258 | if (event.button.which != SDL_TOUCH_MOUSEID) { | ||
| 259 | OnMouseButton(event.button.button, event.button.state, event.button.x, | ||
| 260 | event.button.y); | ||
| 261 | } | ||
| 262 | break; | ||
| 263 | case SDL_FINGERDOWN: | ||
| 264 | OnFingerDown(event.tfinger.x, event.tfinger.y); | ||
| 265 | break; | ||
| 266 | case SDL_FINGERMOTION: | ||
| 267 | OnFingerMotion(event.tfinger.x, event.tfinger.y); | ||
| 268 | break; | ||
| 269 | case SDL_FINGERUP: | ||
| 270 | OnFingerUp(); | ||
| 227 | break; | 271 | break; |
| 228 | case SDL_QUIT: | 272 | case SDL_QUIT: |
| 229 | is_open = false; | 273 | is_open = false; |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index d34902109..b0d4116cc 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -40,6 +40,18 @@ private: | |||
| 40 | /// Called by PollEvents when a mouse button is pressed or released | 40 | /// Called by PollEvents when a mouse button is pressed or released |
| 41 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); | 41 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); |
| 42 | 42 | ||
| 43 | /// Translates pixel position (0..1) to pixel positions | ||
| 44 | std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; | ||
| 45 | |||
| 46 | /// Called by PollEvents when a finger starts touching the touchscreen | ||
| 47 | void OnFingerDown(float x, float y); | ||
| 48 | |||
| 49 | /// Called by PollEvents when a finger moves while touching the touchscreen | ||
| 50 | void OnFingerMotion(float x, float y); | ||
| 51 | |||
| 52 | /// Called by PollEvents when a finger stops touching the touchscreen | ||
| 53 | void OnFingerUp(); | ||
| 54 | |||
| 43 | /// Called by PollEvents when any event that may cause the window to be resized occurs | 55 | /// Called by PollEvents when any event that may cause the window to be resized occurs |
| 44 | void OnResize(); | 56 | void OnResize(); |
| 45 | 57 | ||