diff options
Diffstat (limited to 'src')
396 files changed, 7589 insertions, 2835 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 82e4850f7..c381dbe1d 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -17,6 +17,8 @@ add_library(audio_core STATIC | |||
| 17 | sink_stream.h | 17 | sink_stream.h |
| 18 | stream.cpp | 18 | stream.cpp |
| 19 | stream.h | 19 | stream.h |
| 20 | time_stretch.cpp | ||
| 21 | time_stretch.h | ||
| 20 | 22 | ||
| 21 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> | 23 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> |
| 22 | ) | 24 | ) |
| @@ -24,6 +26,7 @@ add_library(audio_core STATIC | |||
| 24 | create_target_directory_groups(audio_core) | 26 | create_target_directory_groups(audio_core) |
| 25 | 27 | ||
| 26 | target_link_libraries(audio_core PUBLIC common core) | 28 | target_link_libraries(audio_core PUBLIC common core) |
| 29 | target_link_libraries(audio_core PRIVATE SoundTouch) | ||
| 27 | 30 | ||
| 28 | if(ENABLE_CUBEB) | 31 | if(ENABLE_CUBEB) |
| 29 | target_link_libraries(audio_core PRIVATE cubeb) | 32 | target_link_libraries(audio_core PRIVATE cubeb) |
diff --git a/src/audio_core/algorithm/filter.cpp b/src/audio_core/algorithm/filter.cpp index 9fcd0614d..f65bf64f7 100644 --- a/src/audio_core/algorithm/filter.cpp +++ b/src/audio_core/algorithm/filter.cpp | |||
| @@ -35,12 +35,12 @@ Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2) | |||
| 35 | : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} | 35 | : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} |
| 36 | 36 | ||
| 37 | void Filter::Process(std::vector<s16>& signal) { | 37 | void Filter::Process(std::vector<s16>& signal) { |
| 38 | const size_t num_frames = signal.size() / 2; | 38 | const std::size_t num_frames = signal.size() / 2; |
| 39 | for (size_t i = 0; i < num_frames; i++) { | 39 | for (std::size_t i = 0; i < num_frames; i++) { |
| 40 | std::rotate(in.begin(), in.end() - 1, in.end()); | 40 | std::rotate(in.begin(), in.end() - 1, in.end()); |
| 41 | std::rotate(out.begin(), out.end() - 1, out.end()); | 41 | std::rotate(out.begin(), out.end() - 1, out.end()); |
| 42 | 42 | ||
| 43 | for (size_t ch = 0; ch < channel_count; ch++) { | 43 | for (std::size_t ch = 0; ch < channel_count; ch++) { |
| 44 | in[0][ch] = signal[i * channel_count + ch]; | 44 | in[0][ch] = signal[i * channel_count + ch]; |
| 45 | 45 | ||
| 46 | out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - | 46 | out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - |
| @@ -54,14 +54,14 @@ void Filter::Process(std::vector<s16>& signal) { | |||
| 54 | /// Calculates the appropriate Q for each biquad in a cascading filter. | 54 | /// Calculates the appropriate Q for each biquad in a cascading filter. |
| 55 | /// @param total_count The total number of biquads to be cascaded. | 55 | /// @param total_count The total number of biquads to be cascaded. |
| 56 | /// @param index 0-index of the biquad to calculate the Q value for. | 56 | /// @param index 0-index of the biquad to calculate the Q value for. |
| 57 | static double CascadingBiquadQ(size_t total_count, size_t index) { | 57 | static double CascadingBiquadQ(std::size_t total_count, std::size_t index) { |
| 58 | const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); | 58 | const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); |
| 59 | return 1.0 / (2.0 * std::cos(pole)); | 59 | return 1.0 / (2.0 * std::cos(pole)); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) { | 62 | CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) { |
| 63 | std::vector<Filter> cascade(cascade_size); | 63 | std::vector<Filter> cascade(cascade_size); |
| 64 | for (size_t i = 0; i < cascade_size; i++) { | 64 | for (std::size_t i = 0; i < cascade_size; i++) { |
| 65 | cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); | 65 | cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); |
| 66 | } | 66 | } |
| 67 | return CascadingFilter{std::move(cascade)}; | 67 | return CascadingFilter{std::move(cascade)}; |
diff --git a/src/audio_core/algorithm/filter.h b/src/audio_core/algorithm/filter.h index a41beef98..3546d149b 100644 --- a/src/audio_core/algorithm/filter.h +++ b/src/audio_core/algorithm/filter.h | |||
| @@ -30,7 +30,7 @@ public: | |||
| 30 | void Process(std::vector<s16>& signal); | 30 | void Process(std::vector<s16>& signal); |
| 31 | 31 | ||
| 32 | private: | 32 | private: |
| 33 | static constexpr size_t channel_count = 2; | 33 | static constexpr std::size_t channel_count = 2; |
| 34 | 34 | ||
| 35 | /// Coefficients are in normalized form (a0 = 1.0). | 35 | /// Coefficients are in normalized form (a0 = 1.0). |
| 36 | double a1, a2, b0, b1, b2; | 36 | double a1, a2, b0, b1, b2; |
| @@ -46,7 +46,7 @@ public: | |||
| 46 | /// Creates a cascading low-pass filter. | 46 | /// Creates a cascading low-pass filter. |
| 47 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. | 47 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. |
| 48 | /// @param cascade_size Number of biquads in cascade. | 48 | /// @param cascade_size Number of biquads in cascade. |
| 49 | static CascadingFilter LowPass(double cutoff, size_t cascade_size); | 49 | static CascadingFilter LowPass(double cutoff, std::size_t cascade_size); |
| 50 | 50 | ||
| 51 | /// Passthrough. | 51 | /// Passthrough. |
| 52 | CascadingFilter(); | 52 | CascadingFilter(); |
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp index 11459821f..3aea9b0f2 100644 --- a/src/audio_core/algorithm/interpolate.cpp +++ b/src/audio_core/algorithm/interpolate.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace AudioCore { | 14 | namespace AudioCore { |
| 15 | 15 | ||
| 16 | /// The Lanczos kernel | 16 | /// The Lanczos kernel |
| 17 | static double Lanczos(size_t a, double x) { | 17 | static double Lanczos(std::size_t a, double x) { |
| 18 | if (x == 0.0) | 18 | if (x == 0.0) |
| 19 | return 1.0; | 19 | return 1.0; |
| 20 | const double px = M_PI * x; | 20 | const double px = M_PI * x; |
| @@ -37,15 +37,15 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | |||
| 37 | } | 37 | } |
| 38 | state.nyquist.Process(input); | 38 | state.nyquist.Process(input); |
| 39 | 39 | ||
| 40 | constexpr size_t taps = InterpolationState::lanczos_taps; | 40 | constexpr std::size_t taps = InterpolationState::lanczos_taps; |
| 41 | const size_t num_frames = input.size() / 2; | 41 | const std::size_t num_frames = input.size() / 2; |
| 42 | 42 | ||
| 43 | std::vector<s16> output; | 43 | std::vector<s16> output; |
| 44 | output.reserve(static_cast<size_t>(input.size() / ratio + 4)); | 44 | output.reserve(static_cast<std::size_t>(input.size() / ratio + 4)); |
| 45 | 45 | ||
| 46 | double& pos = state.position; | 46 | double& pos = state.position; |
| 47 | auto& h = state.history; | 47 | auto& h = state.history; |
| 48 | for (size_t i = 0; i < num_frames; ++i) { | 48 | for (std::size_t i = 0; i < num_frames; ++i) { |
| 49 | std::rotate(h.begin(), h.end() - 1, h.end()); | 49 | std::rotate(h.begin(), h.end() - 1, h.end()); |
| 50 | h[0][0] = input[i * 2 + 0]; | 50 | h[0][0] = input[i * 2 + 0]; |
| 51 | h[0][1] = input[i * 2 + 1]; | 51 | h[0][1] = input[i * 2 + 1]; |
| @@ -53,7 +53,7 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | |||
| 53 | while (pos <= 1.0) { | 53 | while (pos <= 1.0) { |
| 54 | double l = 0.0; | 54 | double l = 0.0; |
| 55 | double r = 0.0; | 55 | double r = 0.0; |
| 56 | for (size_t j = 0; j < h.size(); j++) { | 56 | for (std::size_t j = 0; j < h.size(); j++) { |
| 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; | 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; |
| 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; | 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; |
| 59 | } | 59 | } |
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h index c79c2eef4..edbd6460f 100644 --- a/src/audio_core/algorithm/interpolate.h +++ b/src/audio_core/algorithm/interpolate.h | |||
| @@ -12,8 +12,8 @@ | |||
| 12 | namespace AudioCore { | 12 | namespace AudioCore { |
| 13 | 13 | ||
| 14 | struct InterpolationState { | 14 | struct InterpolationState { |
| 15 | static constexpr size_t lanczos_taps = 4; | 15 | static constexpr std::size_t lanczos_taps = 4; |
| 16 | static constexpr size_t history_size = lanczos_taps * 2 - 1; | 16 | static constexpr std::size_t history_size = lanczos_taps * 2 - 1; |
| 17 | 17 | ||
| 18 | double current_ratio = 0.0; | 18 | double current_ratio = 0.0; |
| 19 | CascadingFilter nyquist; | 19 | CascadingFilter nyquist; |
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 12632a95c..0c8f5b18e 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp | |||
| @@ -39,7 +39,8 @@ StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& | |||
| 39 | sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); | 39 | sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { | 42 | std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, |
| 43 | std::size_t max_count) { | ||
| 43 | return stream->GetTagsAndReleaseBuffers(max_count); | 44 | return stream->GetTagsAndReleaseBuffers(max_count); |
| 44 | } | 45 | } |
| 45 | 46 | ||
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h index 39b7e656b..df9607ac7 100644 --- a/src/audio_core/audio_out.h +++ b/src/audio_core/audio_out.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | Stream::ReleaseCallback&& release_callback); | 25 | Stream::ReleaseCallback&& release_callback); |
| 26 | 26 | ||
| 27 | /// Returns a vector of recently released buffers specified by tag for the specified stream | 27 | /// Returns a vector of recently released buffers specified by tag for the specified stream |
| 28 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count); | 28 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); |
| 29 | 29 | ||
| 30 | /// Starts an audio stream for playback | 30 | /// Starts an audio stream for playback |
| 31 | void StartStream(StreamPtr stream); | 31 | void StartStream(StreamPtr stream); |
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 397b107f5..83b75e61f 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -3,9 +3,12 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "audio_core/algorithm/interpolate.h" | 5 | #include "audio_core/algorithm/interpolate.h" |
| 6 | #include "audio_core/audio_out.h" | ||
| 6 | #include "audio_core/audio_renderer.h" | 7 | #include "audio_core/audio_renderer.h" |
| 8 | #include "audio_core/codec.h" | ||
| 7 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/hle/kernel/event.h" | ||
| 9 | #include "core/memory.h" | 12 | #include "core/memory.h" |
| 10 | 13 | ||
| 11 | namespace AudioCore { | 14 | namespace AudioCore { |
| @@ -13,20 +16,57 @@ namespace AudioCore { | |||
| 13 | constexpr u32 STREAM_SAMPLE_RATE{48000}; | 16 | constexpr u32 STREAM_SAMPLE_RATE{48000}; |
| 14 | constexpr u32 STREAM_NUM_CHANNELS{2}; | 17 | constexpr u32 STREAM_NUM_CHANNELS{2}; |
| 15 | 18 | ||
| 19 | class AudioRenderer::VoiceState { | ||
| 20 | public: | ||
| 21 | bool IsPlaying() const { | ||
| 22 | return is_in_use && info.play_state == PlayState::Started; | ||
| 23 | } | ||
| 24 | |||
| 25 | const VoiceOutStatus& GetOutStatus() const { | ||
| 26 | return out_status; | ||
| 27 | } | ||
| 28 | |||
| 29 | const VoiceInfo& GetInfo() const { | ||
| 30 | return info; | ||
| 31 | } | ||
| 32 | |||
| 33 | VoiceInfo& Info() { | ||
| 34 | return info; | ||
| 35 | } | ||
| 36 | |||
| 37 | void SetWaveIndex(std::size_t index); | ||
| 38 | std::vector<s16> DequeueSamples(std::size_t sample_count); | ||
| 39 | void UpdateState(); | ||
| 40 | void RefreshBuffer(); | ||
| 41 | |||
| 42 | private: | ||
| 43 | bool is_in_use{}; | ||
| 44 | bool is_refresh_pending{}; | ||
| 45 | std::size_t wave_index{}; | ||
| 46 | std::size_t offset{}; | ||
| 47 | Codec::ADPCMState adpcm_state{}; | ||
| 48 | InterpolationState interp_state{}; | ||
| 49 | std::vector<s16> samples; | ||
| 50 | VoiceOutStatus out_status{}; | ||
| 51 | VoiceInfo info{}; | ||
| 52 | }; | ||
| 53 | |||
| 16 | AudioRenderer::AudioRenderer(AudioRendererParameter params, | 54 | AudioRenderer::AudioRenderer(AudioRendererParameter params, |
| 17 | Kernel::SharedPtr<Kernel::Event> buffer_event) | 55 | Kernel::SharedPtr<Kernel::Event> buffer_event) |
| 18 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { | 56 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { |
| 19 | 57 | ||
| 20 | audio_core = std::make_unique<AudioCore::AudioOut>(); | 58 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 21 | stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", | 59 | stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", |
| 22 | [=]() { buffer_event->Signal(); }); | 60 | [=]() { buffer_event->Signal(); }); |
| 23 | audio_core->StartStream(stream); | 61 | audio_out->StartStream(stream); |
| 24 | 62 | ||
| 25 | QueueMixedBuffer(0); | 63 | QueueMixedBuffer(0); |
| 26 | QueueMixedBuffer(1); | 64 | QueueMixedBuffer(1); |
| 27 | QueueMixedBuffer(2); | 65 | QueueMixedBuffer(2); |
| 28 | } | 66 | } |
| 29 | 67 | ||
| 68 | AudioRenderer::~AudioRenderer() = default; | ||
| 69 | |||
| 30 | u32 AudioRenderer::GetSampleRate() const { | 70 | u32 AudioRenderer::GetSampleRate() const { |
| 31 | return worker_params.sample_rate; | 71 | return worker_params.sample_rate; |
| 32 | } | 72 | } |
| @@ -52,8 +92,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 52 | memory_pool_count * sizeof(MemoryPoolInfo)); | 92 | memory_pool_count * sizeof(MemoryPoolInfo)); |
| 53 | 93 | ||
| 54 | // Copy VoiceInfo structs | 94 | // Copy VoiceInfo structs |
| 55 | size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + | 95 | std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + |
| 56 | config.voice_resource_size}; | 96 | config.voice_resource_size}; |
| 57 | for (auto& voice : voices) { | 97 | for (auto& voice : voices) { |
| 58 | std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); | 98 | std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); |
| 59 | offset += sizeof(VoiceInfo); | 99 | offset += sizeof(VoiceInfo); |
| @@ -72,7 +112,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 72 | 112 | ||
| 73 | // Update memory pool state | 113 | // Update memory pool state |
| 74 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | 114 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); |
| 75 | for (size_t index = 0; index < memory_pool.size(); ++index) { | 115 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { |
| 76 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | 116 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { |
| 77 | memory_pool[index].state = MemoryPoolStates::Attached; | 117 | memory_pool[index].state = MemoryPoolStates::Attached; |
| 78 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | 118 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { |
| @@ -93,7 +133,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 93 | response_data.memory_pools_size); | 133 | response_data.memory_pools_size); |
| 94 | 134 | ||
| 95 | // Copy output voice status | 135 | // Copy output voice status |
| 96 | size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; | 136 | std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; |
| 97 | for (const auto& voice : voices) { | 137 | for (const auto& voice : voices) { |
| 98 | std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), | 138 | std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), |
| 99 | sizeof(VoiceOutStatus)); | 139 | sizeof(VoiceOutStatus)); |
| @@ -103,12 +143,12 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 103 | return output_params; | 143 | return output_params; |
| 104 | } | 144 | } |
| 105 | 145 | ||
| 106 | void AudioRenderer::VoiceState::SetWaveIndex(size_t index) { | 146 | void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { |
| 107 | wave_index = index & 3; | 147 | wave_index = index & 3; |
| 108 | is_refresh_pending = true; | 148 | is_refresh_pending = true; |
| 109 | } | 149 | } |
| 110 | 150 | ||
| 111 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) { | 151 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) { |
| 112 | if (!IsPlaying()) { | 152 | if (!IsPlaying()) { |
| 113 | return {}; | 153 | return {}; |
| 114 | } | 154 | } |
| @@ -117,9 +157,9 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) | |||
| 117 | RefreshBuffer(); | 157 | RefreshBuffer(); |
| 118 | } | 158 | } |
| 119 | 159 | ||
| 120 | const size_t max_size{samples.size() - offset}; | 160 | const std::size_t max_size{samples.size() - offset}; |
| 121 | const size_t dequeue_offset{offset}; | 161 | const std::size_t dequeue_offset{offset}; |
| 122 | size_t size{sample_count * STREAM_NUM_CHANNELS}; | 162 | std::size_t size{sample_count * STREAM_NUM_CHANNELS}; |
| 123 | if (size > max_size) { | 163 | if (size > max_size) { |
| 124 | size = max_size; | 164 | size = max_size; |
| 125 | } | 165 | } |
| @@ -184,7 +224,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 184 | case 1: | 224 | case 1: |
| 185 | // 1 channel is upsampled to 2 channel | 225 | // 1 channel is upsampled to 2 channel |
| 186 | samples.resize(new_samples.size() * 2); | 226 | samples.resize(new_samples.size() * 2); |
| 187 | for (size_t index = 0; index < new_samples.size(); ++index) { | 227 | for (std::size_t index = 0; index < new_samples.size(); ++index) { |
| 188 | samples[index * 2] = new_samples[index]; | 228 | samples[index * 2] = new_samples[index]; |
| 189 | samples[index * 2 + 1] = new_samples[index]; | 229 | samples[index * 2 + 1] = new_samples[index]; |
| 190 | } | 230 | } |
| @@ -210,7 +250,7 @@ static constexpr s16 ClampToS16(s32 value) { | |||
| 210 | } | 250 | } |
| 211 | 251 | ||
| 212 | void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | 252 | void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { |
| 213 | constexpr size_t BUFFER_SIZE{512}; | 253 | constexpr std::size_t BUFFER_SIZE{512}; |
| 214 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); | 254 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); |
| 215 | 255 | ||
| 216 | for (auto& voice : voices) { | 256 | for (auto& voice : voices) { |
| @@ -218,7 +258,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 218 | continue; | 258 | continue; |
| 219 | } | 259 | } |
| 220 | 260 | ||
| 221 | size_t offset{}; | 261 | std::size_t offset{}; |
| 222 | s64 samples_remaining{BUFFER_SIZE}; | 262 | s64 samples_remaining{BUFFER_SIZE}; |
| 223 | while (samples_remaining > 0) { | 263 | while (samples_remaining > 0) { |
| 224 | const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; | 264 | const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; |
| @@ -236,11 +276,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 236 | } | 276 | } |
| 237 | } | 277 | } |
| 238 | } | 278 | } |
| 239 | audio_core->QueueBuffer(stream, tag, std::move(buffer)); | 279 | audio_out->QueueBuffer(stream, tag, std::move(buffer)); |
| 240 | } | 280 | } |
| 241 | 281 | ||
| 242 | void AudioRenderer::ReleaseAndQueueBuffers() { | 282 | void AudioRenderer::ReleaseAndQueueBuffers() { |
| 243 | const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)}; | 283 | const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)}; |
| 244 | for (const auto& tag : released_buffers) { | 284 | for (const auto& tag : released_buffers) { |
| 245 | QueueMixedBuffer(tag); | 285 | QueueMixedBuffer(tag); |
| 246 | } | 286 | } |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index eba67f28e..2c4f5ab75 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -8,16 +8,20 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "audio_core/algorithm/interpolate.h" | ||
| 12 | #include "audio_core/audio_out.h" | ||
| 13 | #include "audio_core/codec.h" | ||
| 14 | #include "audio_core/stream.h" | 11 | #include "audio_core/stream.h" |
| 12 | #include "common/common_funcs.h" | ||
| 15 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 16 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 17 | #include "core/hle/kernel/event.h" | 15 | #include "core/hle/kernel/object.h" |
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | class Event; | ||
| 19 | } | ||
| 18 | 20 | ||
| 19 | namespace AudioCore { | 21 | namespace AudioCore { |
| 20 | 22 | ||
| 23 | class AudioOut; | ||
| 24 | |||
| 21 | enum class PlayState : u8 { | 25 | enum class PlayState : u8 { |
| 22 | Started = 0, | 26 | Started = 0, |
| 23 | Stopped = 1, | 27 | Stopped = 1, |
| @@ -158,6 +162,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size | |||
| 158 | class AudioRenderer { | 162 | class AudioRenderer { |
| 159 | public: | 163 | public: |
| 160 | AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); | 164 | AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); |
| 165 | ~AudioRenderer(); | ||
| 166 | |||
| 161 | std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); | 167 | std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); |
| 162 | void QueueMixedBuffer(Buffer::Tag tag); | 168 | void QueueMixedBuffer(Buffer::Tag tag); |
| 163 | void ReleaseAndQueueBuffers(); | 169 | void ReleaseAndQueueBuffers(); |
| @@ -166,45 +172,12 @@ public: | |||
| 166 | u32 GetMixBufferCount() const; | 172 | u32 GetMixBufferCount() const; |
| 167 | 173 | ||
| 168 | private: | 174 | private: |
| 169 | class VoiceState { | 175 | class VoiceState; |
| 170 | public: | ||
| 171 | bool IsPlaying() const { | ||
| 172 | return is_in_use && info.play_state == PlayState::Started; | ||
| 173 | } | ||
| 174 | |||
| 175 | const VoiceOutStatus& GetOutStatus() const { | ||
| 176 | return out_status; | ||
| 177 | } | ||
| 178 | |||
| 179 | const VoiceInfo& GetInfo() const { | ||
| 180 | return info; | ||
| 181 | } | ||
| 182 | |||
| 183 | VoiceInfo& Info() { | ||
| 184 | return info; | ||
| 185 | } | ||
| 186 | |||
| 187 | void SetWaveIndex(size_t index); | ||
| 188 | std::vector<s16> DequeueSamples(size_t sample_count); | ||
| 189 | void UpdateState(); | ||
| 190 | void RefreshBuffer(); | ||
| 191 | |||
| 192 | private: | ||
| 193 | bool is_in_use{}; | ||
| 194 | bool is_refresh_pending{}; | ||
| 195 | size_t wave_index{}; | ||
| 196 | size_t offset{}; | ||
| 197 | Codec::ADPCMState adpcm_state{}; | ||
| 198 | InterpolationState interp_state{}; | ||
| 199 | std::vector<s16> samples; | ||
| 200 | VoiceOutStatus out_status{}; | ||
| 201 | VoiceInfo info{}; | ||
| 202 | }; | ||
| 203 | 176 | ||
| 204 | AudioRendererParameter worker_params; | 177 | AudioRendererParameter worker_params; |
| 205 | Kernel::SharedPtr<Kernel::Event> buffer_event; | 178 | Kernel::SharedPtr<Kernel::Event> buffer_event; |
| 206 | std::vector<VoiceState> voices; | 179 | std::vector<VoiceState> voices; |
| 207 | std::unique_ptr<AudioCore::AudioOut> audio_core; | 180 | std::unique_ptr<AudioOut> audio_out; |
| 208 | AudioCore::StreamPtr stream; | 181 | AudioCore::StreamPtr stream; |
| 209 | }; | 182 | }; |
| 210 | 183 | ||
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp index c3021403f..454de798b 100644 --- a/src/audio_core/codec.cpp +++ b/src/audio_core/codec.cpp | |||
| @@ -8,27 +8,27 @@ | |||
| 8 | 8 | ||
| 9 | namespace AudioCore::Codec { | 9 | namespace AudioCore::Codec { |
| 10 | 10 | ||
| 11 | std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, | 11 | std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff, |
| 12 | ADPCMState& state) { | 12 | ADPCMState& state) { |
| 13 | // GC-ADPCM with scale factor and variable coefficients. | 13 | // GC-ADPCM with scale factor and variable coefficients. |
| 14 | // Frames are 8 bytes long containing 14 samples each. | 14 | // Frames are 8 bytes long containing 14 samples each. |
| 15 | // Samples are 4 bits (one nibble) long. | 15 | // Samples are 4 bits (one nibble) long. |
| 16 | 16 | ||
| 17 | constexpr size_t FRAME_LEN = 8; | 17 | constexpr std::size_t FRAME_LEN = 8; |
| 18 | constexpr size_t SAMPLES_PER_FRAME = 14; | 18 | constexpr std::size_t SAMPLES_PER_FRAME = 14; |
| 19 | constexpr std::array<int, 16> SIGNED_NIBBLES = { | 19 | constexpr std::array<int, 16> SIGNED_NIBBLES = { |
| 20 | {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; | 20 | {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; |
| 21 | 21 | ||
| 22 | const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; | 22 | const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; |
| 23 | const size_t ret_size = | 23 | const std::size_t ret_size = |
| 24 | sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. | 24 | sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. |
| 25 | std::vector<s16> ret(ret_size); | 25 | std::vector<s16> ret(ret_size); |
| 26 | 26 | ||
| 27 | int yn1 = state.yn1, yn2 = state.yn2; | 27 | int yn1 = state.yn1, yn2 = state.yn2; |
| 28 | 28 | ||
| 29 | const size_t NUM_FRAMES = | 29 | const std::size_t NUM_FRAMES = |
| 30 | (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. | 30 | (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. |
| 31 | for (size_t framei = 0; framei < NUM_FRAMES; framei++) { | 31 | for (std::size_t framei = 0; framei < NUM_FRAMES; framei++) { |
| 32 | const int frame_header = data[framei * FRAME_LEN]; | 32 | const int frame_header = data[framei * FRAME_LEN]; |
| 33 | const int scale = 1 << (frame_header & 0xF); | 33 | const int scale = 1 << (frame_header & 0xF); |
| 34 | const int idx = (frame_header >> 4) & 0x7; | 34 | const int idx = (frame_header >> 4) & 0x7; |
| @@ -53,9 +53,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coef | |||
| 53 | return static_cast<s16>(val); | 53 | return static_cast<s16>(val); |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | size_t outputi = framei * SAMPLES_PER_FRAME; | 56 | std::size_t outputi = framei * SAMPLES_PER_FRAME; |
| 57 | size_t datai = framei * FRAME_LEN + 1; | 57 | std::size_t datai = framei * FRAME_LEN + 1; |
| 58 | for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { | 58 | for (std::size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { |
| 59 | const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); | 59 | const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); |
| 60 | ret[outputi] = sample1; | 60 | ret[outputi] = sample1; |
| 61 | outputi++; | 61 | outputi++; |
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h index 3f845c42c..ef2ce01a8 100644 --- a/src/audio_core/codec.h +++ b/src/audio_core/codec.h | |||
| @@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>; | |||
| 38 | * @param state ADPCM state, this is updated with new state | 38 | * @param state ADPCM state, this is updated with new state |
| 39 | * @return Decoded stereo signed PCM16 data, sample_count in length | 39 | * @return Decoded stereo signed PCM16 data, sample_count in length |
| 40 | */ | 40 | */ |
| 41 | std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, | 41 | std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff, |
| 42 | ADPCMState& state); | 42 | ADPCMState& state); |
| 43 | 43 | ||
| 44 | }; // namespace AudioCore::Codec | 44 | }; // namespace AudioCore::Codec |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 5a1177d0c..392039688 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -3,27 +3,23 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <atomic> | ||
| 6 | #include <cstring> | 7 | #include <cstring> |
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | #include "audio_core/cubeb_sink.h" | 8 | #include "audio_core/cubeb_sink.h" |
| 10 | #include "audio_core/stream.h" | 9 | #include "audio_core/stream.h" |
| 10 | #include "audio_core/time_stretch.h" | ||
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/ring_buffer.h" | ||
| 13 | #include "core/settings.h" | ||
| 12 | 14 | ||
| 13 | namespace AudioCore { | 15 | namespace AudioCore { |
| 14 | 16 | ||
| 15 | class SinkStreamImpl final : public SinkStream { | 17 | class CubebSinkStream final : public SinkStream { |
| 16 | public: | 18 | public: |
| 17 | SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, | 19 | CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, |
| 18 | const std::string& name) | 20 | const std::string& name) |
| 19 | : ctx{ctx}, num_channels{num_channels_} { | 21 | : ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate, |
| 20 | 22 | num_channels} { | |
| 21 | if (num_channels == 6) { | ||
| 22 | // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2 | ||
| 23 | // channel for now | ||
| 24 | is_6_channel = true; | ||
| 25 | num_channels = 2; | ||
| 26 | } | ||
| 27 | 23 | ||
| 28 | cubeb_stream_params params{}; | 24 | cubeb_stream_params params{}; |
| 29 | params.rate = sample_rate; | 25 | params.rate = sample_rate; |
| @@ -38,7 +34,7 @@ public: | |||
| 38 | 34 | ||
| 39 | if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device, | 35 | if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device, |
| 40 | ¶ms, std::max(512u, minimum_latency), | 36 | ¶ms, std::max(512u, minimum_latency), |
| 41 | &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback, | 37 | &CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback, |
| 42 | this) != CUBEB_OK) { | 38 | this) != CUBEB_OK) { |
| 43 | LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); | 39 | LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); |
| 44 | return; | 40 | return; |
| @@ -50,7 +46,7 @@ public: | |||
| 50 | } | 46 | } |
| 51 | } | 47 | } |
| 52 | 48 | ||
| 53 | ~SinkStreamImpl() { | 49 | ~CubebSinkStream() { |
| 54 | if (!ctx) { | 50 | if (!ctx) { |
| 55 | return; | 51 | return; |
| 56 | } | 52 | } |
| @@ -62,27 +58,32 @@ public: | |||
| 62 | cubeb_stream_destroy(stream_backend); | 58 | cubeb_stream_destroy(stream_backend); |
| 63 | } | 59 | } |
| 64 | 60 | ||
| 65 | void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override { | 61 | void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override { |
| 66 | if (!ctx) { | 62 | if (source_num_channels > num_channels) { |
| 63 | // Downsample 6 channels to 2 | ||
| 64 | std::vector<s16> buf; | ||
| 65 | buf.reserve(samples.size() * num_channels / source_num_channels); | ||
| 66 | for (std::size_t i = 0; i < samples.size(); i += source_num_channels) { | ||
| 67 | for (std::size_t ch = 0; ch < num_channels; ch++) { | ||
| 68 | buf.push_back(samples[i + ch]); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | queue.Push(buf); | ||
| 67 | return; | 72 | return; |
| 68 | } | 73 | } |
| 69 | 74 | ||
| 70 | std::lock_guard lock{queue_mutex}; | 75 | queue.Push(samples); |
| 76 | } | ||
| 71 | 77 | ||
| 72 | queue.reserve(queue.size() + samples.size() * GetNumChannels()); | 78 | std::size_t SamplesInQueue(u32 num_channels) const override { |
| 79 | if (!ctx) | ||
| 80 | return 0; | ||
| 73 | 81 | ||
| 74 | if (is_6_channel) { | 82 | return queue.Size() / num_channels; |
| 75 | // Downsample 6 channels to 2 | 83 | } |
| 76 | const size_t sample_count_copy_size = samples.size() * 2; | 84 | |
| 77 | queue.reserve(sample_count_copy_size); | 85 | void Flush() override { |
| 78 | for (size_t i = 0; i < samples.size(); i += num_channels) { | 86 | should_flush = true; |
| 79 | queue.push_back(samples[i]); | ||
| 80 | queue.push_back(samples[i + 1]); | ||
| 81 | } | ||
| 82 | } else { | ||
| 83 | // Copy as-is | ||
| 84 | std::copy(samples.begin(), samples.end(), std::back_inserter(queue)); | ||
| 85 | } | ||
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | u32 GetNumChannels() const { | 89 | u32 GetNumChannels() const { |
| @@ -95,10 +96,11 @@ private: | |||
| 95 | cubeb* ctx{}; | 96 | cubeb* ctx{}; |
| 96 | cubeb_stream* stream_backend{}; | 97 | cubeb_stream* stream_backend{}; |
| 97 | u32 num_channels{}; | 98 | u32 num_channels{}; |
| 98 | bool is_6_channel{}; | ||
| 99 | 99 | ||
| 100 | std::mutex queue_mutex; | 100 | Common::RingBuffer<s16, 0x10000> queue; |
| 101 | std::vector<s16> queue; | 101 | std::array<s16, 2> last_frame; |
| 102 | std::atomic<bool> should_flush{}; | ||
| 103 | TimeStretcher time_stretch; | ||
| 102 | 104 | ||
| 103 | static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, | 105 | static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, |
| 104 | void* output_buffer, long num_frames); | 106 | void* output_buffer, long num_frames); |
| @@ -117,10 +119,10 @@ CubebSink::CubebSink(std::string target_device_name) { | |||
| 117 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); | 119 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); |
| 118 | } else { | 120 | } else { |
| 119 | const auto collection_end{collection.device + collection.count}; | 121 | const auto collection_end{collection.device + collection.count}; |
| 120 | const auto device{std::find_if(collection.device, collection_end, | 122 | const auto device{ |
| 121 | [&](const cubeb_device_info& device) { | 123 | std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { |
| 122 | return target_device_name == device.friendly_name; | 124 | return target_device_name == info.friendly_name; |
| 123 | })}; | 125 | })}; |
| 124 | if (device != collection_end) { | 126 | if (device != collection_end) { |
| 125 | output_device = device->devid; | 127 | output_device = device->devid; |
| 126 | } | 128 | } |
| @@ -144,44 +146,59 @@ CubebSink::~CubebSink() { | |||
| 144 | SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, | 146 | SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, |
| 145 | const std::string& name) { | 147 | const std::string& name) { |
| 146 | sink_streams.push_back( | 148 | sink_streams.push_back( |
| 147 | std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name)); | 149 | std::make_unique<CubebSinkStream>(ctx, sample_rate, num_channels, output_device, name)); |
| 148 | return *sink_streams.back(); | 150 | return *sink_streams.back(); |
| 149 | } | 151 | } |
| 150 | 152 | ||
| 151 | long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, | 153 | long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, |
| 152 | void* output_buffer, long num_frames) { | 154 | void* output_buffer, long num_frames) { |
| 153 | SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data); | 155 | CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data); |
| 154 | u8* buffer = reinterpret_cast<u8*>(output_buffer); | 156 | u8* buffer = reinterpret_cast<u8*>(output_buffer); |
| 155 | 157 | ||
| 156 | if (!impl) { | 158 | if (!impl) { |
| 157 | return {}; | 159 | return {}; |
| 158 | } | 160 | } |
| 159 | 161 | ||
| 160 | std::lock_guard lock{impl->queue_mutex}; | 162 | const std::size_t num_channels = impl->GetNumChannels(); |
| 161 | 163 | const std::size_t samples_to_write = num_channels * num_frames; | |
| 162 | const size_t frames_to_write{ | 164 | std::size_t samples_written; |
| 163 | std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))}; | 165 | |
| 166 | if (Settings::values.enable_audio_stretching) { | ||
| 167 | const std::vector<s16> in{impl->queue.Pop()}; | ||
| 168 | const std::size_t num_in{in.size() / num_channels}; | ||
| 169 | s16* const out{reinterpret_cast<s16*>(buffer)}; | ||
| 170 | const std::size_t out_frames = | ||
| 171 | impl->time_stretch.Process(in.data(), num_in, out, num_frames); | ||
| 172 | samples_written = out_frames * num_channels; | ||
| 173 | |||
| 174 | if (impl->should_flush) { | ||
| 175 | impl->time_stretch.Flush(); | ||
| 176 | impl->should_flush = false; | ||
| 177 | } | ||
| 178 | } else { | ||
| 179 | samples_written = impl->queue.Pop(buffer, samples_to_write); | ||
| 180 | } | ||
| 164 | 181 | ||
| 165 | memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels()); | 182 | if (samples_written >= num_channels) { |
| 166 | impl->queue.erase(impl->queue.begin(), | 183 | std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), |
| 167 | impl->queue.begin() + frames_to_write * impl->GetNumChannels()); | 184 | num_channels * sizeof(s16)); |
| 185 | } | ||
| 168 | 186 | ||
| 169 | if (frames_to_write < num_frames) { | 187 | // Fill the rest of the frames with last_frame |
| 170 | // Fill the rest of the frames with silence | 188 | for (std::size_t i = samples_written; i < samples_to_write; i += num_channels) { |
| 171 | memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0, | 189 | std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16)); |
| 172 | (num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels()); | ||
| 173 | } | 190 | } |
| 174 | 191 | ||
| 175 | return num_frames; | 192 | return num_frames; |
| 176 | } | 193 | } |
| 177 | 194 | ||
| 178 | void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {} | 195 | void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {} |
| 179 | 196 | ||
| 180 | std::vector<std::string> ListCubebSinkDevices() { | 197 | std::vector<std::string> ListCubebSinkDevices() { |
| 181 | std::vector<std::string> device_list; | 198 | std::vector<std::string> device_list; |
| 182 | cubeb* ctx; | 199 | cubeb* ctx; |
| 183 | 200 | ||
| 184 | if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) { | 201 | if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) { |
| 185 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | 202 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); |
| 186 | return {}; | 203 | return {}; |
| 187 | } | 204 | } |
| @@ -190,7 +207,7 @@ std::vector<std::string> ListCubebSinkDevices() { | |||
| 190 | if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { | 207 | if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { |
| 191 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); | 208 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); |
| 192 | } else { | 209 | } else { |
| 193 | for (size_t i = 0; i < collection.count; i++) { | 210 | for (std::size_t i = 0; i < collection.count; i++) { |
| 194 | const cubeb_device_info& device = collection.device[i]; | 211 | const cubeb_device_info& device = collection.device[i]; |
| 195 | if (device.friendly_name) { | 212 | if (device.friendly_name) { |
| 196 | device_list.emplace_back(device.friendly_name); | 213 | device_list.emplace_back(device.friendly_name); |
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index f235d93e5..a78d78893 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h | |||
| @@ -21,6 +21,12 @@ public: | |||
| 21 | private: | 21 | private: |
| 22 | struct NullSinkStreamImpl final : SinkStream { | 22 | struct NullSinkStreamImpl final : SinkStream { |
| 23 | void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} | 23 | void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} |
| 24 | |||
| 25 | std::size_t SamplesInQueue(u32 /*num_channels*/) const override { | ||
| 26 | return 0; | ||
| 27 | } | ||
| 28 | |||
| 29 | void Flush() override {} | ||
| 24 | } null_sink_stream; | 30 | } null_sink_stream; |
| 25 | }; | 31 | }; |
| 26 | 32 | ||
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index 955ba20fb..67cf1f3b2 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp | |||
| @@ -24,7 +24,7 @@ const std::vector<SinkDetails> g_sink_details = { | |||
| 24 | [] { return std::vector<std::string>{"null"}; }}, | 24 | [] { return std::vector<std::string>{"null"}; }}, |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | const SinkDetails& GetSinkDetails(std::string sink_id) { | 27 | const SinkDetails& GetSinkDetails(std::string_view sink_id) { |
| 28 | auto iter = | 28 | auto iter = |
| 29 | std::find_if(g_sink_details.begin(), g_sink_details.end(), | 29 | std::find_if(g_sink_details.begin(), g_sink_details.end(), |
| 30 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); | 30 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); |
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h index ea666c554..03534b187 100644 --- a/src/audio_core/sink_details.h +++ b/src/audio_core/sink_details.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <functional> | 7 | #include <functional> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | ||
| 10 | #include <string_view> | ||
| 9 | #include <utility> | 11 | #include <utility> |
| 10 | #include <vector> | 12 | #include <vector> |
| 11 | 13 | ||
| @@ -30,6 +32,6 @@ struct SinkDetails { | |||
| 30 | 32 | ||
| 31 | extern const std::vector<SinkDetails> g_sink_details; | 33 | extern const std::vector<SinkDetails> g_sink_details; |
| 32 | 34 | ||
| 33 | const SinkDetails& GetSinkDetails(std::string sink_id); | 35 | const SinkDetails& GetSinkDetails(std::string_view sink_id); |
| 34 | 36 | ||
| 35 | } // namespace AudioCore | 37 | } // namespace AudioCore |
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h index 41b6736d8..4309ad094 100644 --- a/src/audio_core/sink_stream.h +++ b/src/audio_core/sink_stream.h | |||
| @@ -25,6 +25,10 @@ public: | |||
| 25 | * @param samples Samples in interleaved stereo PCM16 format. | 25 | * @param samples Samples in interleaved stereo PCM16 format. |
| 26 | */ | 26 | */ |
| 27 | virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0; | 27 | virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0; |
| 28 | |||
| 29 | virtual std::size_t SamplesInQueue(u32 num_channels) const = 0; | ||
| 30 | |||
| 31 | virtual void Flush() = 0; | ||
| 28 | }; | 32 | }; |
| 29 | 33 | ||
| 30 | using SinkStreamPtr = std::unique_ptr<SinkStream>; | 34 | using SinkStreamPtr = std::unique_ptr<SinkStream>; |
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index ad9e2915c..449db2416 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -7,16 +7,18 @@ | |||
| 7 | 7 | ||
| 8 | #include "audio_core/sink.h" | 8 | #include "audio_core/sink.h" |
| 9 | #include "audio_core/sink_details.h" | 9 | #include "audio_core/sink_details.h" |
| 10 | #include "audio_core/sink_stream.h" | ||
| 10 | #include "audio_core/stream.h" | 11 | #include "audio_core/stream.h" |
| 11 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/microprofile.h" | ||
| 13 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 14 | #include "core/core_timing_util.h" | 16 | #include "core/core_timing_util.h" |
| 15 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| 16 | 18 | ||
| 17 | namespace AudioCore { | 19 | namespace AudioCore { |
| 18 | 20 | ||
| 19 | constexpr size_t MaxAudioBufferCount{32}; | 21 | constexpr std::size_t MaxAudioBufferCount{32}; |
| 20 | 22 | ||
| 21 | u32 Stream::GetNumChannels() const { | 23 | u32 Stream::GetNumChannels() const { |
| 22 | switch (format) { | 24 | switch (format) { |
| @@ -51,7 +53,7 @@ void Stream::Stop() { | |||
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { | 55 | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { |
| 54 | const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; | 56 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; |
| 55 | return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); | 57 | return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); |
| 56 | } | 58 | } |
| 57 | 59 | ||
| @@ -72,6 +74,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) { | |||
| 72 | void Stream::PlayNextBuffer() { | 74 | void Stream::PlayNextBuffer() { |
| 73 | if (!IsPlaying()) { | 75 | if (!IsPlaying()) { |
| 74 | // Ensure we are in playing state before playing the next buffer | 76 | // Ensure we are in playing state before playing the next buffer |
| 77 | sink_stream.Flush(); | ||
| 75 | return; | 78 | return; |
| 76 | } | 79 | } |
| 77 | 80 | ||
| @@ -82,6 +85,7 @@ void Stream::PlayNextBuffer() { | |||
| 82 | 85 | ||
| 83 | if (queued_buffers.empty()) { | 86 | if (queued_buffers.empty()) { |
| 84 | // No queued buffers - we are effectively paused | 87 | // No queued buffers - we are effectively paused |
| 88 | sink_stream.Flush(); | ||
| 85 | return; | 89 | return; |
| 86 | } | 90 | } |
| 87 | 91 | ||
| @@ -89,12 +93,16 @@ void Stream::PlayNextBuffer() { | |||
| 89 | queued_buffers.pop(); | 93 | queued_buffers.pop(); |
| 90 | 94 | ||
| 91 | VolumeAdjustSamples(active_buffer->Samples()); | 95 | VolumeAdjustSamples(active_buffer->Samples()); |
| 96 | |||
| 92 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); | 97 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); |
| 93 | 98 | ||
| 94 | CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); | 99 | CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); |
| 95 | } | 100 | } |
| 96 | 101 | ||
| 102 | MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255)); | ||
| 103 | |||
| 97 | void Stream::ReleaseActiveBuffer() { | 104 | void Stream::ReleaseActiveBuffer() { |
| 105 | MICROPROFILE_SCOPE(AudioOutput); | ||
| 98 | ASSERT(active_buffer); | 106 | ASSERT(active_buffer); |
| 99 | released_buffers.push(std::move(active_buffer)); | 107 | released_buffers.push(std::move(active_buffer)); |
| 100 | release_callback(); | 108 | release_callback(); |
| @@ -115,9 +123,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const { | |||
| 115 | return {}; | 123 | return {}; |
| 116 | } | 124 | } |
| 117 | 125 | ||
| 118 | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { | 126 | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) { |
| 119 | std::vector<Buffer::Tag> tags; | 127 | std::vector<Buffer::Tag> tags; |
| 120 | for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { | 128 | for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { |
| 121 | tags.push_back(released_buffers.front()->GetTag()); | 129 | tags.push_back(released_buffers.front()->GetTag()); |
| 122 | released_buffers.pop(); | 130 | released_buffers.pop(); |
| 123 | } | 131 | } |
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 049b92ca9..27db1112f 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h | |||
| @@ -11,13 +11,16 @@ | |||
| 11 | #include <queue> | 11 | #include <queue> |
| 12 | 12 | ||
| 13 | #include "audio_core/buffer.h" | 13 | #include "audio_core/buffer.h" |
| 14 | #include "audio_core/sink_stream.h" | ||
| 15 | #include "common/assert.h" | ||
| 16 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 17 | #include "core/core_timing.h" | 15 | |
| 16 | namespace CoreTiming { | ||
| 17 | struct EventType; | ||
| 18 | } | ||
| 18 | 19 | ||
| 19 | namespace AudioCore { | 20 | namespace AudioCore { |
| 20 | 21 | ||
| 22 | class SinkStream; | ||
| 23 | |||
| 21 | /** | 24 | /** |
| 22 | * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut | 25 | * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut |
| 23 | */ | 26 | */ |
| @@ -49,7 +52,7 @@ public: | |||
| 49 | bool ContainsBuffer(Buffer::Tag tag) const; | 52 | bool ContainsBuffer(Buffer::Tag tag) const; |
| 50 | 53 | ||
| 51 | /// Returns a vector of recently released buffers specified by tag | 54 | /// Returns a vector of recently released buffers specified by tag |
| 52 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count); | 55 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count); |
| 53 | 56 | ||
| 54 | /// Returns true if the stream is currently playing | 57 | /// Returns true if the stream is currently playing |
| 55 | bool IsPlaying() const { | 58 | bool IsPlaying() const { |
| @@ -57,7 +60,7 @@ public: | |||
| 57 | } | 60 | } |
| 58 | 61 | ||
| 59 | /// Returns the number of queued buffers | 62 | /// Returns the number of queued buffers |
| 60 | size_t GetQueueSize() const { | 63 | std::size_t GetQueueSize() const { |
| 61 | return queued_buffers.size(); | 64 | return queued_buffers.size(); |
| 62 | } | 65 | } |
| 63 | 66 | ||
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp new file mode 100644 index 000000000..fc14151da --- /dev/null +++ b/src/audio_core/time_stretch.cpp | |||
| @@ -0,0 +1,69 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | #include <cstddef> | ||
| 8 | #include "audio_core/time_stretch.h" | ||
| 9 | #include "common/logging/log.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | |||
| 13 | TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) | ||
| 14 | : m_sample_rate(sample_rate), m_channel_count(channel_count) { | ||
| 15 | m_sound_touch.setChannels(channel_count); | ||
| 16 | m_sound_touch.setSampleRate(sample_rate); | ||
| 17 | m_sound_touch.setPitch(1.0); | ||
| 18 | m_sound_touch.setTempo(1.0); | ||
| 19 | } | ||
| 20 | |||
| 21 | void TimeStretcher::Clear() { | ||
| 22 | m_sound_touch.clear(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void TimeStretcher::Flush() { | ||
| 26 | m_sound_touch.flush(); | ||
| 27 | } | ||
| 28 | |||
| 29 | std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out, | ||
| 30 | std::size_t num_out) { | ||
| 31 | const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds | ||
| 32 | |||
| 33 | // We were given actual_samples number of samples, and num_samples were requested from us. | ||
| 34 | double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); | ||
| 35 | |||
| 36 | const double max_latency = 1.0; // seconds | ||
| 37 | const double max_backlog = m_sample_rate * max_latency; | ||
| 38 | const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; | ||
| 39 | if (backlog_fullness > 5.0) { | ||
| 40 | // Too many samples in backlog: Don't push anymore on | ||
| 41 | num_in = 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | // We ideally want the backlog to be about 50% full. | ||
| 45 | // This gives some headroom both ways to prevent underflow and overflow. | ||
| 46 | // We tweak current_ratio to encourage this. | ||
| 47 | constexpr double tweak_time_scale = 0.05; // seconds | ||
| 48 | const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale); | ||
| 49 | current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0); | ||
| 50 | |||
| 51 | // This low-pass filter smoothes out variance in the calculated stretch ratio. | ||
| 52 | // The time-scale determines how responsive this filter is. | ||
| 53 | constexpr double lpf_time_scale = 2.0; // seconds | ||
| 54 | const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); | ||
| 55 | m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); | ||
| 56 | |||
| 57 | // Place a lower limit of 5% speed. When a game boots up, there will be | ||
| 58 | // many silence samples. These do not need to be timestretched. | ||
| 59 | m_stretch_ratio = std::max(m_stretch_ratio, 0.05); | ||
| 60 | m_sound_touch.setTempo(m_stretch_ratio); | ||
| 61 | |||
| 62 | LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio, | ||
| 63 | backlog_fullness); | ||
| 64 | |||
| 65 | m_sound_touch.putSamples(in, static_cast<u32>(num_in)); | ||
| 66 | return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out)); | ||
| 67 | } | ||
| 68 | |||
| 69 | } // namespace AudioCore | ||
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h new file mode 100644 index 000000000..decd760f1 --- /dev/null +++ b/src/audio_core/time_stretch.h | |||
| @@ -0,0 +1,35 @@ | |||
| 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 <cstddef> | ||
| 8 | #include <SoundTouch.h> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | |||
| 13 | class TimeStretcher { | ||
| 14 | public: | ||
| 15 | TimeStretcher(u32 sample_rate, u32 channel_count); | ||
| 16 | |||
| 17 | /// @param in Input sample buffer | ||
| 18 | /// @param num_in Number of input frames in `in` | ||
| 19 | /// @param out Output sample buffer | ||
| 20 | /// @param num_out Desired number of output frames in `out` | ||
| 21 | /// @returns Actual number of frames written to `out` | ||
| 22 | std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out); | ||
| 23 | |||
| 24 | void Clear(); | ||
| 25 | |||
| 26 | void Flush(); | ||
| 27 | |||
| 28 | private: | ||
| 29 | u32 m_sample_rate; | ||
| 30 | u32 m_channel_count; | ||
| 31 | soundtouch::SoundTouch m_sound_touch; | ||
| 32 | double m_stretch_ratio = 1.0; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace AudioCore | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d9424ea91..6a3f1fe08 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -1,13 +1,16 @@ | |||
| 1 | # Generate cpp with Git revision from template | 1 | # Generate cpp with Git revision from template |
| 2 | # Also if this is a CI build, add the build name (ie: Nightly, Bleeding Edge) to the scm_rev file as well | 2 | # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well |
| 3 | set(REPO_NAME "") | 3 | set(REPO_NAME "") |
| 4 | set(BUILD_VERSION "0") | ||
| 4 | if ($ENV{CI}) | 5 | if ($ENV{CI}) |
| 5 | if ($ENV{TRAVIS}) | 6 | if ($ENV{TRAVIS}) |
| 6 | set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG}) | 7 | set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG}) |
| 8 | set(BUILD_TAG $ENV{TRAVIS_TAG}) | ||
| 7 | elseif($ENV{APPVEYOR}) | 9 | elseif($ENV{APPVEYOR}) |
| 8 | set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) | 10 | set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) |
| 11 | set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) | ||
| 9 | endif() | 12 | endif() |
| 10 | # regex capture the string nightly or bleeding-edge into CMAKE_MATCH_1 | 13 | # regex capture the string nightly or canary into CMAKE_MATCH_1 |
| 11 | string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY}) | 14 | string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY}) |
| 12 | if (${CMAKE_MATCH_COUNT} GREATER 0) | 15 | if (${CMAKE_MATCH_COUNT} GREATER 0) |
| 13 | # capitalize the first letter of each word in the repo name. | 16 | # capitalize the first letter of each word in the repo name. |
| @@ -16,10 +19,21 @@ if ($ENV{CI}) | |||
| 16 | string(SUBSTRING ${WORD} 0 1 FIRST_LETTER) | 19 | string(SUBSTRING ${WORD} 0 1 FIRST_LETTER) |
| 17 | string(SUBSTRING ${WORD} 1 -1 REMAINDER) | 20 | string(SUBSTRING ${WORD} 1 -1 REMAINDER) |
| 18 | string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) | 21 | string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) |
| 19 | # this leaves a trailing space on the last word, but we actually want that | 22 | set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}") |
| 20 | # because of how it's styled in the title bar. | ||
| 21 | set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ") | ||
| 22 | endforeach() | 23 | endforeach() |
| 24 | if (BUILD_TAG) | ||
| 25 | string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG}) | ||
| 26 | if (${CMAKE_MATCH_COUNT} GREATER 0) | ||
| 27 | set(BUILD_VERSION ${CMAKE_MATCH_1}) | ||
| 28 | endif() | ||
| 29 | if (BUILD_VERSION) | ||
| 30 | # This leaves a trailing space on the last word, but we actually want that | ||
| 31 | # because of how it's styled in the title bar. | ||
| 32 | set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ") | ||
| 33 | else() | ||
| 34 | set(BUILD_FULLNAME "") | ||
| 35 | endif() | ||
| 36 | endif() | ||
| 23 | endif() | 37 | endif() |
| 24 | endif() | 38 | endif() |
| 25 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY) | 39 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY) |
| @@ -57,6 +71,7 @@ add_library(common STATIC | |||
| 57 | param_package.cpp | 71 | param_package.cpp |
| 58 | param_package.h | 72 | param_package.h |
| 59 | quaternion.h | 73 | quaternion.h |
| 74 | ring_buffer.h | ||
| 60 | scm_rev.cpp | 75 | scm_rev.cpp |
| 61 | scm_rev.h | 76 | scm_rev.h |
| 62 | scope_exit.h | 77 | scope_exit.h |
diff --git a/src/common/alignment.h b/src/common/alignment.h index b9dd38746..225770fab 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -8,13 +8,13 @@ | |||
| 8 | namespace Common { | 8 | namespace Common { |
| 9 | 9 | ||
| 10 | template <typename T> | 10 | template <typename T> |
| 11 | constexpr T AlignUp(T value, size_t size) { | 11 | constexpr T AlignUp(T value, std::size_t size) { |
| 12 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 12 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); |
| 13 | return static_cast<T>(value + (size - value % size) % size); | 13 | return static_cast<T>(value + (size - value % size) % size); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | template <typename T> | 16 | template <typename T> |
| 17 | constexpr T AlignDown(T value, size_t size) { | 17 | constexpr T AlignDown(T value, std::size_t size) { |
| 18 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 18 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); |
| 19 | return static_cast<T>(value - value % size); | 19 | return static_cast<T>(value - value % size); |
| 20 | } | 20 | } |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 732201de7..bf803da8d 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -129,8 +129,8 @@ private: | |||
| 129 | 129 | ||
| 130 | public: | 130 | public: |
| 131 | /// Constants to allow limited introspection of fields if needed | 131 | /// Constants to allow limited introspection of fields if needed |
| 132 | static constexpr size_t position = Position; | 132 | static constexpr std::size_t position = Position; |
| 133 | static constexpr size_t bits = Bits; | 133 | static constexpr std::size_t bits = Bits; |
| 134 | static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; | 134 | static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; |
| 135 | 135 | ||
| 136 | /** | 136 | /** |
diff --git a/src/common/bit_set.h b/src/common/bit_set.h index 5a197d8c1..5cd1352b2 100644 --- a/src/common/bit_set.h +++ b/src/common/bit_set.h | |||
| @@ -170,14 +170,14 @@ public: | |||
| 170 | m_val |= (IntTy)1 << bit; | 170 | m_val |= (IntTy)1 << bit; |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | static BitSet AllTrue(size_t count) { | 173 | static BitSet AllTrue(std::size_t count) { |
| 174 | return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); | 174 | return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | Ref operator[](size_t bit) { | 177 | Ref operator[](std::size_t bit) { |
| 178 | return Ref(this, (IntTy)1 << bit); | 178 | return Ref(this, (IntTy)1 << bit); |
| 179 | } | 179 | } |
| 180 | const Ref operator[](size_t bit) const { | 180 | const Ref operator[](std::size_t bit) const { |
| 181 | return (*const_cast<BitSet*>(this))[bit]; | 181 | return (*const_cast<BitSet*>(this))[bit]; |
| 182 | } | 182 | } |
| 183 | bool operator==(BitSet other) const { | 183 | bool operator==(BitSet other) const { |
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index de31ffbd8..4e1d874b5 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp | |||
| @@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { | |||
| 114 | return b; | 114 | return b; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | static uint64 HashLen0to16(const char* s, size_t len) { | 117 | static uint64 HashLen0to16(const char* s, std::size_t len) { |
| 118 | if (len >= 8) { | 118 | if (len >= 8) { |
| 119 | uint64 mul = k2 + len * 2; | 119 | uint64 mul = k2 + len * 2; |
| 120 | uint64 a = Fetch64(s) + k2; | 120 | uint64 a = Fetch64(s) + k2; |
| @@ -141,7 +141,7 @@ static uint64 HashLen0to16(const char* s, size_t len) { | |||
| 141 | 141 | ||
| 142 | // This probably works well for 16-byte strings as well, but it may be overkill | 142 | // This probably works well for 16-byte strings as well, but it may be overkill |
| 143 | // in that case. | 143 | // in that case. |
| 144 | static uint64 HashLen17to32(const char* s, size_t len) { | 144 | static uint64 HashLen17to32(const char* s, std::size_t len) { |
| 145 | uint64 mul = k2 + len * 2; | 145 | uint64 mul = k2 + len * 2; |
| 146 | uint64 a = Fetch64(s) * k1; | 146 | uint64 a = Fetch64(s) * k1; |
| 147 | uint64 b = Fetch64(s + 8); | 147 | uint64 b = Fetch64(s + 8); |
| @@ -170,7 +170,7 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint | |||
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | // Return an 8-byte hash for 33 to 64 bytes. | 172 | // Return an 8-byte hash for 33 to 64 bytes. |
| 173 | static uint64 HashLen33to64(const char* s, size_t len) { | 173 | static uint64 HashLen33to64(const char* s, std::size_t len) { |
| 174 | uint64 mul = k2 + len * 2; | 174 | uint64 mul = k2 + len * 2; |
| 175 | uint64 a = Fetch64(s) * k2; | 175 | uint64 a = Fetch64(s) * k2; |
| 176 | uint64 b = Fetch64(s + 8); | 176 | uint64 b = Fetch64(s + 8); |
| @@ -191,7 +191,7 @@ static uint64 HashLen33to64(const char* s, size_t len) { | |||
| 191 | return b + x; | 191 | return b + x; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | uint64 CityHash64(const char* s, size_t len) { | 194 | uint64 CityHash64(const char* s, std::size_t len) { |
| 195 | if (len <= 32) { | 195 | if (len <= 32) { |
| 196 | if (len <= 16) { | 196 | if (len <= 16) { |
| 197 | return HashLen0to16(s, len); | 197 | return HashLen0to16(s, len); |
| @@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, size_t len) { | |||
| 212 | x = x * k1 + Fetch64(s); | 212 | x = x * k1 + Fetch64(s); |
| 213 | 213 | ||
| 214 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. | 214 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. |
| 215 | len = (len - 1) & ~static_cast<size_t>(63); | 215 | len = (len - 1) & ~static_cast<std::size_t>(63); |
| 216 | do { | 216 | do { |
| 217 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; | 217 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; |
| 218 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; | 218 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; |
| @@ -229,17 +229,17 @@ uint64 CityHash64(const char* s, size_t len) { | |||
| 229 | HashLen16(v.second, w.second) + x); | 229 | HashLen16(v.second, w.second) + x); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { | 232 | uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) { |
| 233 | return CityHash64WithSeeds(s, len, k2, seed); | 233 | return CityHash64WithSeeds(s, len, k2, seed); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) { | 236 | uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) { |
| 237 | return HashLen16(CityHash64(s, len) - seed0, seed1); | 237 | return HashLen16(CityHash64(s, len) - seed0, seed1); |
| 238 | } | 238 | } |
| 239 | 239 | ||
| 240 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings | 240 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings |
| 241 | // of any length representable in signed long. Based on City and Murmur. | 241 | // of any length representable in signed long. Based on City and Murmur. |
| 242 | static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { | 242 | static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) { |
| 243 | uint64 a = Uint128Low64(seed); | 243 | uint64 a = Uint128Low64(seed); |
| 244 | uint64 b = Uint128High64(seed); | 244 | uint64 b = Uint128High64(seed); |
| 245 | uint64 c = 0; | 245 | uint64 c = 0; |
| @@ -269,7 +269,7 @@ static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { | |||
| 269 | return uint128(a ^ b, HashLen16(b, a)); | 269 | return uint128(a ^ b, HashLen16(b, a)); |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | 272 | uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) { |
| 273 | if (len < 128) { | 273 | if (len < 128) { |
| 274 | return CityMurmur(s, len, seed); | 274 | return CityMurmur(s, len, seed); |
| 275 | } | 275 | } |
| @@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | |||
| 313 | w.first *= 9; | 313 | w.first *= 9; |
| 314 | v.first *= k0; | 314 | v.first *= k0; |
| 315 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. | 315 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. |
| 316 | for (size_t tail_done = 0; tail_done < len;) { | 316 | for (std::size_t tail_done = 0; tail_done < len;) { |
| 317 | tail_done += 32; | 317 | tail_done += 32; |
| 318 | y = Rotate(x + y, 42) * k0 + v.second; | 318 | y = Rotate(x + y, 42) * k0 + v.second; |
| 319 | w.first += Fetch64(s + len - tail_done + 16); | 319 | w.first += Fetch64(s + len - tail_done + 16); |
| @@ -331,7 +331,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | |||
| 331 | return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); | 331 | return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); |
| 332 | } | 332 | } |
| 333 | 333 | ||
| 334 | uint128 CityHash128(const char* s, size_t len) { | 334 | uint128 CityHash128(const char* s, std::size_t len) { |
| 335 | return len >= 16 | 335 | return len >= 16 |
| 336 | ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) | 336 | ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) |
| 337 | : CityHash128WithSeed(s, len, uint128(k0, k1)); | 337 | : CityHash128WithSeed(s, len, uint128(k0, k1)); |
diff --git a/src/common/cityhash.h b/src/common/cityhash.h index bcebdb150..4b94f8e18 100644 --- a/src/common/cityhash.h +++ b/src/common/cityhash.h | |||
| @@ -63,7 +63,7 @@ | |||
| 63 | 63 | ||
| 64 | #include <utility> | 64 | #include <utility> |
| 65 | #include <stdint.h> | 65 | #include <stdint.h> |
| 66 | #include <stdlib.h> // for size_t. | 66 | #include <stdlib.h> // for std::size_t. |
| 67 | 67 | ||
| 68 | namespace Common { | 68 | namespace Common { |
| 69 | 69 | ||
| @@ -77,22 +77,22 @@ inline uint64_t Uint128High64(const uint128& x) { | |||
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | // Hash function for a byte array. | 79 | // Hash function for a byte array. |
| 80 | uint64_t CityHash64(const char* buf, size_t len); | 80 | uint64_t CityHash64(const char* buf, std::size_t len); |
| 81 | 81 | ||
| 82 | // Hash function for a byte array. For convenience, a 64-bit seed is also | 82 | // Hash function for a byte array. For convenience, a 64-bit seed is also |
| 83 | // hashed into the result. | 83 | // hashed into the result. |
| 84 | uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed); | 84 | uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed); |
| 85 | 85 | ||
| 86 | // Hash function for a byte array. For convenience, two seeds are also | 86 | // Hash function for a byte array. For convenience, two seeds are also |
| 87 | // hashed into the result. | 87 | // hashed into the result. |
| 88 | uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1); | 88 | uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1); |
| 89 | 89 | ||
| 90 | // Hash function for a byte array. | 90 | // Hash function for a byte array. |
| 91 | uint128 CityHash128(const char* s, size_t len); | 91 | uint128 CityHash128(const char* s, std::size_t len); |
| 92 | 92 | ||
| 93 | // Hash function for a byte array. For convenience, a 128-bit seed is also | 93 | // Hash function for a byte array. For convenience, a 128-bit seed is also |
| 94 | // hashed into the result. | 94 | // hashed into the result. |
| 95 | uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); | 95 | uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed); |
| 96 | 96 | ||
| 97 | // Hash 128 input bits down to 64 bits of output. | 97 | // Hash 128 input bits down to 64 bits of output. |
| 98 | // This is intended to be a reasonably good hash function. | 98 | // This is intended to be a reasonably good hash function. |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index baa721481..21a0b9738 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -76,7 +76,7 @@ namespace FileUtil { | |||
| 76 | // Modifies argument. | 76 | // Modifies argument. |
| 77 | static void StripTailDirSlashes(std::string& fname) { | 77 | static void StripTailDirSlashes(std::string& fname) { |
| 78 | if (fname.length() > 1) { | 78 | if (fname.length() > 1) { |
| 79 | size_t i = fname.length(); | 79 | std::size_t i = fname.length(); |
| 80 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) | 80 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) |
| 81 | --i; | 81 | --i; |
| 82 | fname.resize(i); | 82 | fname.resize(i); |
| @@ -201,7 +201,7 @@ bool CreateFullPath(const std::string& fullPath) { | |||
| 201 | return true; | 201 | return true; |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | size_t position = 0; | 204 | std::size_t position = 0; |
| 205 | while (true) { | 205 | while (true) { |
| 206 | // Find next sub path | 206 | // Find next sub path |
| 207 | position = fullPath.find(DIR_SEP_CHR, position); | 207 | position = fullPath.find(DIR_SEP_CHR, position); |
| @@ -299,7 +299,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { | |||
| 299 | std::array<char, 1024> buffer; | 299 | std::array<char, 1024> buffer; |
| 300 | while (!feof(input.get())) { | 300 | while (!feof(input.get())) { |
| 301 | // read input | 301 | // read input |
| 302 | size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); | 302 | std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); |
| 303 | if (rnum != buffer.size()) { | 303 | if (rnum != buffer.size()) { |
| 304 | if (ferror(input.get()) != 0) { | 304 | if (ferror(input.get()) != 0) { |
| 305 | LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", | 305 | LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", |
| @@ -309,7 +309,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { | |||
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | // write output | 311 | // write output |
| 312 | size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); | 312 | std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); |
| 313 | if (wnum != rnum) { | 313 | if (wnum != rnum) { |
| 314 | LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, | 314 | LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, |
| 315 | destFilename, GetLastErrorMsg()); | 315 | destFilename, GetLastErrorMsg()); |
| @@ -756,11 +756,11 @@ std::string GetNANDRegistrationDir(bool system) { | |||
| 756 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; | 756 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; |
| 757 | } | 757 | } |
| 758 | 758 | ||
| 759 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { | 759 | std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { |
| 760 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | 760 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); |
| 761 | } | 761 | } |
| 762 | 762 | ||
| 763 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { | 763 | std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { |
| 764 | IOFile file(filename, text_file ? "r" : "rb"); | 764 | IOFile file(filename, text_file ? "r" : "rb"); |
| 765 | 765 | ||
| 766 | if (!file.IsOpen()) | 766 | if (!file.IsOpen()) |
| @@ -829,7 +829,7 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) { | |||
| 829 | std::string_view GetParentPath(std::string_view path) { | 829 | std::string_view GetParentPath(std::string_view path) { |
| 830 | const auto name_bck_index = path.rfind('\\'); | 830 | const auto name_bck_index = path.rfind('\\'); |
| 831 | const auto name_fwd_index = path.rfind('/'); | 831 | const auto name_fwd_index = path.rfind('/'); |
| 832 | size_t name_index; | 832 | std::size_t name_index; |
| 833 | 833 | ||
| 834 | if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { | 834 | if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { |
| 835 | name_index = std::min(name_bck_index, name_fwd_index); | 835 | name_index = std::min(name_bck_index, name_fwd_index); |
| @@ -868,7 +868,7 @@ std::string_view GetFilename(std::string_view path) { | |||
| 868 | } | 868 | } |
| 869 | 869 | ||
| 870 | std::string_view GetExtensionFromFilename(std::string_view name) { | 870 | std::string_view GetExtensionFromFilename(std::string_view name) { |
| 871 | const size_t index = name.rfind('.'); | 871 | const std::size_t index = name.rfind('.'); |
| 872 | 872 | ||
| 873 | if (index == std::string_view::npos) { | 873 | if (index == std::string_view::npos) { |
| 874 | return {}; | 874 | return {}; |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 2f13d0b6b..24c1e413c 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -143,8 +143,9 @@ const std::string& GetExeDirectory(); | |||
| 143 | std::string AppDataRoamingDirectory(); | 143 | std::string AppDataRoamingDirectory(); |
| 144 | #endif | 144 | #endif |
| 145 | 145 | ||
| 146 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); | 146 | std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); |
| 147 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | 147 | |
| 148 | std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | ||
| 148 | 149 | ||
| 149 | /** | 150 | /** |
| 150 | * Splits the filename into 8.3 format | 151 | * Splits the filename into 8.3 format |
| @@ -177,10 +178,10 @@ std::string_view RemoveTrailingSlash(std::string_view path); | |||
| 177 | 178 | ||
| 178 | // Creates a new vector containing indices [first, last) from the original. | 179 | // Creates a new vector containing indices [first, last) from the original. |
| 179 | template <typename T> | 180 | template <typename T> |
| 180 | std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { | 181 | std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) { |
| 181 | if (first >= last) | 182 | if (first >= last) |
| 182 | return {}; | 183 | return {}; |
| 183 | last = std::min<size_t>(last, vector.size()); | 184 | last = std::min<std::size_t>(last, vector.size()); |
| 184 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | 185 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); |
| 185 | } | 186 | } |
| 186 | 187 | ||
| @@ -213,47 +214,47 @@ public: | |||
| 213 | bool Close(); | 214 | bool Close(); |
| 214 | 215 | ||
| 215 | template <typename T> | 216 | template <typename T> |
| 216 | size_t ReadArray(T* data, size_t length) const { | 217 | std::size_t ReadArray(T* data, std::size_t length) const { |
| 217 | static_assert(std::is_trivially_copyable_v<T>, | 218 | static_assert(std::is_trivially_copyable_v<T>, |
| 218 | "Given array does not consist of trivially copyable objects"); | 219 | "Given array does not consist of trivially copyable objects"); |
| 219 | 220 | ||
| 220 | if (!IsOpen()) { | 221 | if (!IsOpen()) { |
| 221 | return std::numeric_limits<size_t>::max(); | 222 | return std::numeric_limits<std::size_t>::max(); |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 224 | return std::fread(data, sizeof(T), length, m_file); | 225 | return std::fread(data, sizeof(T), length, m_file); |
| 225 | } | 226 | } |
| 226 | 227 | ||
| 227 | template <typename T> | 228 | template <typename T> |
| 228 | size_t WriteArray(const T* data, size_t length) { | 229 | std::size_t WriteArray(const T* data, std::size_t length) { |
| 229 | static_assert(std::is_trivially_copyable_v<T>, | 230 | static_assert(std::is_trivially_copyable_v<T>, |
| 230 | "Given array does not consist of trivially copyable objects"); | 231 | "Given array does not consist of trivially copyable objects"); |
| 231 | if (!IsOpen()) { | 232 | if (!IsOpen()) { |
| 232 | return std::numeric_limits<size_t>::max(); | 233 | return std::numeric_limits<std::size_t>::max(); |
| 233 | } | 234 | } |
| 234 | 235 | ||
| 235 | return std::fwrite(data, sizeof(T), length, m_file); | 236 | return std::fwrite(data, sizeof(T), length, m_file); |
| 236 | } | 237 | } |
| 237 | 238 | ||
| 238 | template <typename T> | 239 | template <typename T> |
| 239 | size_t ReadBytes(T* data, size_t length) const { | 240 | std::size_t ReadBytes(T* data, std::size_t length) const { |
| 240 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | 241 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| 241 | return ReadArray(reinterpret_cast<char*>(data), length); | 242 | return ReadArray(reinterpret_cast<char*>(data), length); |
| 242 | } | 243 | } |
| 243 | 244 | ||
| 244 | template <typename T> | 245 | template <typename T> |
| 245 | size_t WriteBytes(const T* data, size_t length) { | 246 | std::size_t WriteBytes(const T* data, std::size_t length) { |
| 246 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | 247 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| 247 | return WriteArray(reinterpret_cast<const char*>(data), length); | 248 | return WriteArray(reinterpret_cast<const char*>(data), length); |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | template <typename T> | 251 | template <typename T> |
| 251 | size_t WriteObject(const T& object) { | 252 | std::size_t WriteObject(const T& object) { |
| 252 | static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); | 253 | static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); |
| 253 | return WriteArray(&object, 1); | 254 | return WriteArray(&object, 1); |
| 254 | } | 255 | } |
| 255 | 256 | ||
| 256 | size_t WriteString(const std::string& str) { | 257 | std::size_t WriteString(const std::string& str) { |
| 257 | return WriteArray(str.c_str(), str.length()); | 258 | return WriteArray(str.c_str(), str.length()); |
| 258 | } | 259 | } |
| 259 | 260 | ||
diff --git a/src/common/hash.h b/src/common/hash.h index 2c761e545..40194d1ee 100644 --- a/src/common/hash.h +++ b/src/common/hash.h | |||
| @@ -17,7 +17,7 @@ namespace Common { | |||
| 17 | * @param len Length of data (in bytes) to compute hash over | 17 | * @param len Length of data (in bytes) to compute hash over |
| 18 | * @returns 64-bit hash value that was computed over the data block | 18 | * @returns 64-bit hash value that was computed over the data block |
| 19 | */ | 19 | */ |
| 20 | static inline u64 ComputeHash64(const void* data, size_t len) { | 20 | static inline u64 ComputeHash64(const void* data, std::size_t len) { |
| 21 | return CityHash64(static_cast<const char*>(data), len); | 21 | return CityHash64(static_cast<const char*>(data), len); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| @@ -63,7 +63,7 @@ struct HashableStruct { | |||
| 63 | return !(*this == o); | 63 | return !(*this == o); |
| 64 | }; | 64 | }; |
| 65 | 65 | ||
| 66 | size_t Hash() const { | 66 | std::size_t Hash() const { |
| 67 | return Common::ComputeStructHash64(state); | 67 | return Common::ComputeStructHash64(state); |
| 68 | } | 68 | } |
| 69 | }; | 69 | }; |
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp index 8e0a9e46f..589ae5cbf 100644 --- a/src/common/hex_util.cpp +++ b/src/common/hex_util.cpp | |||
| @@ -18,7 +18,7 @@ u8 ToHexNibble(char c1) { | |||
| 18 | return 0; | 18 | return 0; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | std::array<u8, 16> operator""_array16(const char* str, size_t len) { | 21 | std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { |
| 22 | if (len != 32) { | 22 | if (len != 32) { |
| 23 | LOG_ERROR(Common, | 23 | LOG_ERROR(Common, |
| 24 | "Attempting to parse string to array that is not of correct size (expected=32, " | 24 | "Attempting to parse string to array that is not of correct size (expected=32, " |
| @@ -29,7 +29,7 @@ std::array<u8, 16> operator""_array16(const char* str, size_t len) { | |||
| 29 | return HexStringToArray<16>(str); | 29 | return HexStringToArray<16>(str); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | std::array<u8, 32> operator""_array32(const char* str, size_t len) { | 32 | std::array<u8, 32> operator""_array32(const char* str, std::size_t len) { |
| 33 | if (len != 64) { | 33 | if (len != 64) { |
| 34 | LOG_ERROR(Common, | 34 | LOG_ERROR(Common, |
| 35 | "Attempting to parse string to array that is not of correct size (expected=64, " | 35 | "Attempting to parse string to array that is not of correct size (expected=64, " |
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index 5fb79bb72..863a5ccd9 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h | |||
| @@ -14,20 +14,20 @@ namespace Common { | |||
| 14 | 14 | ||
| 15 | u8 ToHexNibble(char c1); | 15 | u8 ToHexNibble(char c1); |
| 16 | 16 | ||
| 17 | template <size_t Size, bool le = false> | 17 | template <std::size_t Size, bool le = false> |
| 18 | std::array<u8, Size> HexStringToArray(std::string_view str) { | 18 | std::array<u8, Size> HexStringToArray(std::string_view str) { |
| 19 | std::array<u8, Size> out{}; | 19 | std::array<u8, Size> out{}; |
| 20 | if constexpr (le) { | 20 | if constexpr (le) { |
| 21 | for (size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) | 21 | for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) |
| 22 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | 22 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); |
| 23 | } else { | 23 | } else { |
| 24 | for (size_t i = 0; i < 2 * Size; i += 2) | 24 | for (std::size_t i = 0; i < 2 * Size; i += 2) |
| 25 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | 25 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); |
| 26 | } | 26 | } |
| 27 | return out; | 27 | return out; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | template <size_t Size> | 30 | template <std::size_t Size> |
| 31 | std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { | 31 | std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { |
| 32 | std::string out; | 32 | std::string out; |
| 33 | for (u8 c : array) | 33 | for (u8 c : array) |
| @@ -35,7 +35,7 @@ std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { | |||
| 35 | return out; | 35 | return out; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); | 38 | std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); |
| 39 | std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); | 39 | std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len); |
| 40 | 40 | ||
| 41 | } // namespace Common | 41 | } // namespace Common |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 1323f8d0f..efd776db6 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -135,7 +135,7 @@ FileBackend::FileBackend(const std::string& filename) | |||
| 135 | void FileBackend::Write(const Entry& entry) { | 135 | void FileBackend::Write(const Entry& entry) { |
| 136 | // prevent logs from going over the maximum size (in case its spamming and the user doesn't | 136 | // prevent logs from going over the maximum size (in case its spamming and the user doesn't |
| 137 | // know) | 137 | // know) |
| 138 | constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; | 138 | constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; |
| 139 | if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { | 139 | if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { |
| 140 | return; | 140 | return; |
| 141 | } | 141 | } |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index b3f4b9cef..11edbf1b6 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -100,7 +100,7 @@ public: | |||
| 100 | 100 | ||
| 101 | private: | 101 | private: |
| 102 | FileUtil::IOFile file; | 102 | FileUtil::IOFile file; |
| 103 | size_t bytes_written; | 103 | std::size_t bytes_written; |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | void AddBackend(std::unique_ptr<Backend> backend); | 106 | void AddBackend(std::unique_ptr<Backend> backend); |
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 2dd331152..2eccbcd8d 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void Filter::SetClassLevel(Class log_class, Level level) { | 73 | void Filter::SetClassLevel(Class log_class, Level level) { |
| 74 | class_levels[static_cast<size_t>(log_class)] = level; | 74 | class_levels[static_cast<std::size_t>(log_class)] = level; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | void Filter::ParseFilterString(std::string_view filter_view) { | 77 | void Filter::ParseFilterString(std::string_view filter_view) { |
| @@ -93,7 +93,8 @@ void Filter::ParseFilterString(std::string_view filter_view) { | |||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | bool Filter::CheckMessage(Class log_class, Level level) const { | 95 | bool Filter::CheckMessage(Class log_class, Level level) const { |
| 96 | return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]); | 96 | return static_cast<u8>(level) >= |
| 97 | static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]); | ||
| 97 | } | 98 | } |
| 98 | 99 | ||
| 99 | bool Filter::IsDebug() const { | 100 | bool Filter::IsDebug() const { |
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index d5ffc5a58..773df6f2c 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h | |||
| @@ -19,7 +19,7 @@ namespace Log { | |||
| 19 | class Filter { | 19 | class Filter { |
| 20 | public: | 20 | public: |
| 21 | /// Initializes the filter with all classes having `default_level` as the minimum level. | 21 | /// Initializes the filter with all classes having `default_level` as the minimum level. |
| 22 | Filter(Level default_level = Level::Info); | 22 | explicit Filter(Level default_level = Level::Info); |
| 23 | 23 | ||
| 24 | /// Resets the filter so that all classes have `level` as the minimum displayed level. | 24 | /// Resets the filter so that all classes have `level` as the minimum displayed level. |
| 25 | void ResetAll(Level level); | 25 | void ResetAll(Level level); |
| @@ -49,6 +49,6 @@ public: | |||
| 49 | bool IsDebug() const; | 49 | bool IsDebug() const; |
| 50 | 50 | ||
| 51 | private: | 51 | private: |
| 52 | std::array<Level, (size_t)Class::Count> class_levels; | 52 | std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels; |
| 53 | }; | 53 | }; |
| 54 | } // namespace Log | 54 | } // namespace Log |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index e12f47f8f..4d577524f 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -12,14 +12,14 @@ namespace Log { | |||
| 12 | /// Specifies the severity or level of detail of the log message. | 12 | /// Specifies the severity or level of detail of the log message. |
| 13 | enum class Level : u8 { | 13 | enum class Level : u8 { |
| 14 | Trace, ///< Extremely detailed and repetitive debugging information that is likely to | 14 | Trace, ///< Extremely detailed and repetitive debugging information that is likely to |
| 15 | /// pollute logs. | 15 | ///< pollute logs. |
| 16 | Debug, ///< Less detailed debugging information. | 16 | Debug, ///< Less detailed debugging information. |
| 17 | Info, ///< Status information from important points during execution. | 17 | Info, ///< Status information from important points during execution. |
| 18 | Warning, ///< Minor or potential problems found during execution of a task. | 18 | Warning, ///< Minor or potential problems found during execution of a task. |
| 19 | Error, ///< Major problems found during execution of a task that prevent it from being | 19 | Error, ///< Major problems found during execution of a task that prevent it from being |
| 20 | /// completed. | 20 | ///< completed. |
| 21 | Critical, ///< Major problems during execution that threathen the stability of the entire | 21 | Critical, ///< Major problems during execution that threaten the stability of the entire |
| 22 | /// application. | 22 | ///< application. |
| 23 | 23 | ||
| 24 | Count ///< Total number of logging levels | 24 | Count ///< Total number of logging levels |
| 25 | }; | 25 | }; |
| @@ -49,7 +49,7 @@ enum class Class : ClassType { | |||
| 49 | Kernel, ///< The HLE implementation of the CTR kernel | 49 | Kernel, ///< The HLE implementation of the CTR kernel |
| 50 | Kernel_SVC, ///< Kernel system calls | 50 | Kernel_SVC, ///< Kernel system calls |
| 51 | Service, ///< HLE implementation of system services. Each major service | 51 | Service, ///< HLE implementation of system services. Each major service |
| 52 | /// should have its own subclass. | 52 | ///< should have its own subclass. |
| 53 | Service_ACC, ///< The ACC (Accounts) service | 53 | Service_ACC, ///< The ACC (Accounts) service |
| 54 | Service_AM, ///< The AM (Applet manager) service | 54 | Service_AM, ///< The AM (Applet manager) service |
| 55 | Service_AOC, ///< The AOC (AddOn Content) service | 55 | Service_AOC, ///< The AOC (AddOn Content) service |
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 9609cec7c..b6d9e57c8 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h | |||
| @@ -15,6 +15,6 @@ struct Entry; | |||
| 15 | std::string FormatLogMessage(const Entry& entry); | 15 | std::string FormatLogMessage(const Entry& entry); |
| 16 | /// Formats and prints a log entry to stderr. | 16 | /// Formats and prints a log entry to stderr. |
| 17 | void PrintMessage(const Entry& entry); | 17 | void PrintMessage(const Entry& entry); |
| 18 | /// Prints the same message as `PrintMessage`, but colored acoording to the severity level. | 18 | /// Prints the same message as `PrintMessage`, but colored according to the severity level. |
| 19 | void PrintColoredMessage(const Entry& entry); | 19 | void PrintColoredMessage(const Entry& entry); |
| 20 | } // namespace Log | 20 | } // namespace Log |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 09462ccee..9736fb12a 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | // This is purposely not a full wrapper for virtualalloc/mmap, but it | 25 | // This is purposely not a full wrapper for virtualalloc/mmap, but it |
| 26 | // provides exactly the primitive operations that Dolphin needs. | 26 | // provides exactly the primitive operations that Dolphin needs. |
| 27 | 27 | ||
| 28 | void* AllocateExecutableMemory(size_t size, bool low) { | 28 | void* AllocateExecutableMemory(std::size_t size, bool low) { |
| 29 | #if defined(_WIN32) | 29 | #if defined(_WIN32) |
| 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); |
| 31 | #else | 31 | #else |
| @@ -74,7 +74,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { | |||
| 74 | return ptr; | 74 | return ptr; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | void* AllocateMemoryPages(size_t size) { | 77 | void* AllocateMemoryPages(std::size_t size) { |
| 78 | #ifdef _WIN32 | 78 | #ifdef _WIN32 |
| 79 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); | 79 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); |
| 80 | #else | 80 | #else |
| @@ -90,7 +90,7 @@ void* AllocateMemoryPages(size_t size) { | |||
| 90 | return ptr; | 90 | return ptr; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | void* AllocateAlignedMemory(size_t size, size_t alignment) { | 93 | void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) { |
| 94 | #ifdef _WIN32 | 94 | #ifdef _WIN32 |
| 95 | void* ptr = _aligned_malloc(size, alignment); | 95 | void* ptr = _aligned_malloc(size, alignment); |
| 96 | #else | 96 | #else |
| @@ -109,7 +109,7 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) { | |||
| 109 | return ptr; | 109 | return ptr; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | void FreeMemoryPages(void* ptr, size_t size) { | 112 | void FreeMemoryPages(void* ptr, std::size_t size) { |
| 113 | if (ptr) { | 113 | if (ptr) { |
| 114 | #ifdef _WIN32 | 114 | #ifdef _WIN32 |
| 115 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) | 115 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) |
| @@ -130,7 +130,7 @@ void FreeAlignedMemory(void* ptr) { | |||
| 130 | } | 130 | } |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { | 133 | void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { |
| 134 | #ifdef _WIN32 | 134 | #ifdef _WIN32 |
| 135 | DWORD oldValue; | 135 | DWORD oldValue; |
| 136 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) | 136 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) |
| @@ -140,7 +140,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { | |||
| 140 | #endif | 140 | #endif |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { | 143 | void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { |
| 144 | #ifdef _WIN32 | 144 | #ifdef _WIN32 |
| 145 | DWORD oldValue; | 145 | DWORD oldValue; |
| 146 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, | 146 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, |
diff --git a/src/common/memory_util.h b/src/common/memory_util.h index 76ca5a30c..aad071979 100644 --- a/src/common/memory_util.h +++ b/src/common/memory_util.h | |||
| @@ -7,13 +7,13 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | void* AllocateExecutableMemory(size_t size, bool low = true); | 10 | void* AllocateExecutableMemory(std::size_t size, bool low = true); |
| 11 | void* AllocateMemoryPages(size_t size); | 11 | void* AllocateMemoryPages(std::size_t size); |
| 12 | void FreeMemoryPages(void* ptr, size_t size); | 12 | void FreeMemoryPages(void* ptr, std::size_t size); |
| 13 | void* AllocateAlignedMemory(size_t size, size_t alignment); | 13 | void* AllocateAlignedMemory(std::size_t size, std::size_t alignment); |
| 14 | void FreeAlignedMemory(void* ptr); | 14 | void FreeAlignedMemory(void* ptr); |
| 15 | void WriteProtectMemory(void* ptr, size_t size, bool executable = false); | 15 | void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false); |
| 16 | void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); | 16 | void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false); |
| 17 | std::string MemUsage(); | 17 | std::string MemUsage(); |
| 18 | 18 | ||
| 19 | inline int GetPageSize() { | 19 | inline int GetPageSize() { |
diff --git a/src/common/misc.cpp b/src/common/misc.cpp index 3fa8a3bc4..68cb86cd1 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | // Call directly after the command or use the error num. | 16 | // Call directly after the command or use the error num. |
| 17 | // This function might change the error code. | 17 | // This function might change the error code. |
| 18 | std::string GetLastErrorMsg() { | 18 | std::string GetLastErrorMsg() { |
| 19 | static const size_t buff_size = 255; | 19 | static const std::size_t buff_size = 255; |
| 20 | char err_str[buff_size]; | 20 | char err_str[buff_size]; |
| 21 | 21 | ||
| 22 | #ifdef _WIN32 | 22 | #ifdef _WIN32 |
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h new file mode 100644 index 000000000..abe3b4dc2 --- /dev/null +++ b/src/common/ring_buffer.h | |||
| @@ -0,0 +1,119 @@ | |||
| 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 <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <atomic> | ||
| 10 | #include <cstddef> | ||
| 11 | #include <cstring> | ||
| 12 | #include <new> | ||
| 13 | #include <type_traits> | ||
| 14 | #include <vector> | ||
| 15 | #include "common/common_types.h" | ||
| 16 | |||
| 17 | namespace Common { | ||
| 18 | |||
| 19 | /// SPSC ring buffer | ||
| 20 | /// @tparam T Element type | ||
| 21 | /// @tparam capacity Number of slots in ring buffer | ||
| 22 | /// @tparam granularity Slot size in terms of number of elements | ||
| 23 | template <typename T, std::size_t capacity, std::size_t granularity = 1> | ||
| 24 | class RingBuffer { | ||
| 25 | /// A "slot" is made of `granularity` elements of `T`. | ||
| 26 | static constexpr std::size_t slot_size = granularity * sizeof(T); | ||
| 27 | // T must be safely memcpy-able and have a trivial default constructor. | ||
| 28 | static_assert(std::is_trivial_v<T>); | ||
| 29 | // Ensure capacity is sensible. | ||
| 30 | static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity); | ||
| 31 | static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); | ||
| 32 | // Ensure lock-free. | ||
| 33 | static_assert(std::atomic_size_t::is_always_lock_free); | ||
| 34 | |||
| 35 | public: | ||
| 36 | /// Pushes slots into the ring buffer | ||
| 37 | /// @param new_slots Pointer to the slots to push | ||
| 38 | /// @param slot_count Number of slots to push | ||
| 39 | /// @returns The number of slots actually pushed | ||
| 40 | std::size_t Push(const void* new_slots, std::size_t slot_count) { | ||
| 41 | const std::size_t write_index = m_write_index.load(); | ||
| 42 | const std::size_t slots_free = capacity + m_read_index.load() - write_index; | ||
| 43 | const std::size_t push_count = std::min(slot_count, slots_free); | ||
| 44 | |||
| 45 | const std::size_t pos = write_index % capacity; | ||
| 46 | const std::size_t first_copy = std::min(capacity - pos, push_count); | ||
| 47 | const std::size_t second_copy = push_count - first_copy; | ||
| 48 | |||
| 49 | const char* in = static_cast<const char*>(new_slots); | ||
| 50 | std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size); | ||
| 51 | in += first_copy * slot_size; | ||
| 52 | std::memcpy(m_data.data(), in, second_copy * slot_size); | ||
| 53 | |||
| 54 | m_write_index.store(write_index + push_count); | ||
| 55 | |||
| 56 | return push_count; | ||
| 57 | } | ||
| 58 | |||
| 59 | std::size_t Push(const std::vector<T>& input) { | ||
| 60 | return Push(input.data(), input.size()); | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Pops slots from the ring buffer | ||
| 64 | /// @param output Where to store the popped slots | ||
| 65 | /// @param max_slots Maximum number of slots to pop | ||
| 66 | /// @returns The number of slots actually popped | ||
| 67 | std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) { | ||
| 68 | const std::size_t read_index = m_read_index.load(); | ||
| 69 | const std::size_t slots_filled = m_write_index.load() - read_index; | ||
| 70 | const std::size_t pop_count = std::min(slots_filled, max_slots); | ||
| 71 | |||
| 72 | const std::size_t pos = read_index % capacity; | ||
| 73 | const std::size_t first_copy = std::min(capacity - pos, pop_count); | ||
| 74 | const std::size_t second_copy = pop_count - first_copy; | ||
| 75 | |||
| 76 | char* out = static_cast<char*>(output); | ||
| 77 | std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size); | ||
| 78 | out += first_copy * slot_size; | ||
| 79 | std::memcpy(out, m_data.data(), second_copy * slot_size); | ||
| 80 | |||
| 81 | m_read_index.store(read_index + pop_count); | ||
| 82 | |||
| 83 | return pop_count; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) { | ||
| 87 | std::vector<T> out(std::min(max_slots, capacity) * granularity); | ||
| 88 | const std::size_t count = Pop(out.data(), out.size() / granularity); | ||
| 89 | out.resize(count * granularity); | ||
| 90 | return out; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// @returns Number of slots used | ||
| 94 | std::size_t Size() const { | ||
| 95 | return m_write_index.load() - m_read_index.load(); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// @returns Maximum size of ring buffer | ||
| 99 | constexpr std::size_t Capacity() const { | ||
| 100 | return capacity; | ||
| 101 | } | ||
| 102 | |||
| 103 | private: | ||
| 104 | // It is important to align the below variables for performance reasons: | ||
| 105 | // Having them on the same cache-line would result in false-sharing between them. | ||
| 106 | // TODO: Remove this ifdef whenever clang and GCC support | ||
| 107 | // std::hardware_destructive_interference_size. | ||
| 108 | #if defined(_MSC_VER) && _MSC_VER >= 1911 | ||
| 109 | alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; | ||
| 110 | alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; | ||
| 111 | #else | ||
| 112 | alignas(128) std::atomic_size_t m_read_index{0}; | ||
| 113 | alignas(128) std::atomic_size_t m_write_index{0}; | ||
| 114 | #endif | ||
| 115 | |||
| 116 | std::array<T, granularity * capacity> m_data; | ||
| 117 | }; | ||
| 118 | |||
| 119 | } // namespace Common | ||
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 4083095d5..2b1727769 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in | |||
| @@ -9,6 +9,8 @@ | |||
| 9 | #define GIT_DESC "@GIT_DESC@" | 9 | #define GIT_DESC "@GIT_DESC@" |
| 10 | #define BUILD_NAME "@REPO_NAME@" | 10 | #define BUILD_NAME "@REPO_NAME@" |
| 11 | #define BUILD_DATE "@BUILD_DATE@" | 11 | #define BUILD_DATE "@BUILD_DATE@" |
| 12 | #define BUILD_FULLNAME "@BUILD_FULLNAME@" | ||
| 13 | #define BUILD_VERSION "@BUILD_VERSION@" | ||
| 12 | 14 | ||
| 13 | namespace Common { | 15 | namespace Common { |
| 14 | 16 | ||
| @@ -17,6 +19,8 @@ const char g_scm_branch[] = GIT_BRANCH; | |||
| 17 | const char g_scm_desc[] = GIT_DESC; | 19 | const char g_scm_desc[] = GIT_DESC; |
| 18 | const char g_build_name[] = BUILD_NAME; | 20 | const char g_build_name[] = BUILD_NAME; |
| 19 | const char g_build_date[] = BUILD_DATE; | 21 | const char g_build_date[] = BUILD_DATE; |
| 22 | const char g_build_fullname[] = BUILD_FULLNAME; | ||
| 23 | const char g_build_version[] = BUILD_VERSION; | ||
| 20 | 24 | ||
| 21 | } // namespace | 25 | } // namespace |
| 22 | 26 | ||
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index db0f4a947..af9a9daed 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h | |||
| @@ -11,5 +11,7 @@ extern const char g_scm_branch[]; | |||
| 11 | extern const char g_scm_desc[]; | 11 | extern const char g_scm_desc[]; |
| 12 | extern const char g_build_name[]; | 12 | extern const char g_build_name[]; |
| 13 | extern const char g_build_date[]; | 13 | extern const char g_build_date[]; |
| 14 | extern const char g_build_fullname[]; | ||
| 15 | extern const char g_build_version[]; | ||
| 14 | 16 | ||
| 15 | } // namespace Common | 17 | } // namespace Common |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 0ca663032..c9a5425a7 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -37,7 +37,7 @@ std::string ToUpper(std::string str) { | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | // For Debugging. Read out an u8 array. | 39 | // For Debugging. Read out an u8 array. |
| 40 | std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) { | 40 | std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) { |
| 41 | std::ostringstream oss; | 41 | std::ostringstream oss; |
| 42 | oss << std::setfill('0') << std::hex; | 42 | oss << std::setfill('0') << std::hex; |
| 43 | 43 | ||
| @@ -60,7 +60,7 @@ std::string StringFromBuffer(const std::vector<u8>& data) { | |||
| 60 | 60 | ||
| 61 | // Turns " hej " into "hej". Also handles tabs. | 61 | // Turns " hej " into "hej". Also handles tabs. |
| 62 | std::string StripSpaces(const std::string& str) { | 62 | std::string StripSpaces(const std::string& str) { |
| 63 | const size_t s = str.find_first_not_of(" \t\r\n"); | 63 | const std::size_t s = str.find_first_not_of(" \t\r\n"); |
| 64 | 64 | ||
| 65 | if (str.npos != s) | 65 | if (str.npos != s) |
| 66 | return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); | 66 | return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); |
| @@ -121,10 +121,10 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 121 | if (full_path.empty()) | 121 | if (full_path.empty()) |
| 122 | return false; | 122 | return false; |
| 123 | 123 | ||
| 124 | size_t dir_end = full_path.find_last_of("/" | 124 | std::size_t dir_end = full_path.find_last_of("/" |
| 125 | // windows needs the : included for something like just "C:" to be considered a directory | 125 | // windows needs the : included for something like just "C:" to be considered a directory |
| 126 | #ifdef _WIN32 | 126 | #ifdef _WIN32 |
| 127 | "\\:" | 127 | "\\:" |
| 128 | #endif | 128 | #endif |
| 129 | ); | 129 | ); |
| 130 | if (std::string::npos == dir_end) | 130 | if (std::string::npos == dir_end) |
| @@ -132,7 +132,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 132 | else | 132 | else |
| 133 | dir_end += 1; | 133 | dir_end += 1; |
| 134 | 134 | ||
| 135 | size_t fname_end = full_path.rfind('.'); | 135 | std::size_t fname_end = full_path.rfind('.'); |
| 136 | if (fname_end < dir_end || std::string::npos == fname_end) | 136 | if (fname_end < dir_end || std::string::npos == fname_end) |
| 137 | fname_end = full_path.size(); | 137 | fname_end = full_path.size(); |
| 138 | 138 | ||
| @@ -172,7 +172,7 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri | |||
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | std::string TabsToSpaces(int tab_size, std::string in) { | 174 | std::string TabsToSpaces(int tab_size, std::string in) { |
| 175 | size_t i = 0; | 175 | std::size_t i = 0; |
| 176 | 176 | ||
| 177 | while ((i = in.find('\t')) != std::string::npos) { | 177 | while ((i = in.find('\t')) != std::string::npos) { |
| 178 | in.replace(i, 1, tab_size, ' '); | 178 | in.replace(i, 1, tab_size, ' '); |
| @@ -182,7 +182,7 @@ std::string TabsToSpaces(int tab_size, std::string in) { | |||
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) { | 184 | std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) { |
| 185 | size_t pos = 0; | 185 | std::size_t pos = 0; |
| 186 | 186 | ||
| 187 | if (src == dest) | 187 | if (src == dest) |
| 188 | return result; | 188 | return result; |
| @@ -280,22 +280,22 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& | |||
| 280 | return {}; | 280 | return {}; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | const size_t in_bytes = sizeof(T) * input.size(); | 283 | const std::size_t in_bytes = sizeof(T) * input.size(); |
| 284 | // Multiply by 4, which is the max number of bytes to encode a codepoint | 284 | // Multiply by 4, which is the max number of bytes to encode a codepoint |
| 285 | const size_t out_buffer_size = 4 * in_bytes; | 285 | const std::size_t out_buffer_size = 4 * in_bytes; |
| 286 | 286 | ||
| 287 | std::string out_buffer(out_buffer_size, '\0'); | 287 | std::string out_buffer(out_buffer_size, '\0'); |
| 288 | 288 | ||
| 289 | auto src_buffer = &input[0]; | 289 | auto src_buffer = &input[0]; |
| 290 | size_t src_bytes = in_bytes; | 290 | std::size_t src_bytes = in_bytes; |
| 291 | auto dst_buffer = &out_buffer[0]; | 291 | auto dst_buffer = &out_buffer[0]; |
| 292 | size_t dst_bytes = out_buffer.size(); | 292 | std::size_t dst_bytes = out_buffer.size(); |
| 293 | 293 | ||
| 294 | while (0 != src_bytes) { | 294 | while (0 != src_bytes) { |
| 295 | size_t const iconv_result = | 295 | std::size_t const iconv_result = |
| 296 | iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); | 296 | iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); |
| 297 | 297 | ||
| 298 | if (static_cast<size_t>(-1) == iconv_result) { | 298 | if (static_cast<std::size_t>(-1) == iconv_result) { |
| 299 | if (EILSEQ == errno || EINVAL == errno) { | 299 | if (EILSEQ == errno || EINVAL == errno) { |
| 300 | // Try to skip the bad character | 300 | // Try to skip the bad character |
| 301 | if (0 != src_bytes) { | 301 | if (0 != src_bytes) { |
| @@ -326,22 +326,22 @@ std::u16string UTF8ToUTF16(const std::string& input) { | |||
| 326 | return {}; | 326 | return {}; |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | const size_t in_bytes = sizeof(char) * input.size(); | 329 | const std::size_t in_bytes = sizeof(char) * input.size(); |
| 330 | // Multiply by 4, which is the max number of bytes to encode a codepoint | 330 | // Multiply by 4, which is the max number of bytes to encode a codepoint |
| 331 | const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; | 331 | const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; |
| 332 | 332 | ||
| 333 | std::u16string out_buffer(out_buffer_size, char16_t{}); | 333 | std::u16string out_buffer(out_buffer_size, char16_t{}); |
| 334 | 334 | ||
| 335 | char* src_buffer = const_cast<char*>(&input[0]); | 335 | char* src_buffer = const_cast<char*>(&input[0]); |
| 336 | size_t src_bytes = in_bytes; | 336 | std::size_t src_bytes = in_bytes; |
| 337 | char* dst_buffer = (char*)(&out_buffer[0]); | 337 | char* dst_buffer = (char*)(&out_buffer[0]); |
| 338 | size_t dst_bytes = out_buffer.size(); | 338 | std::size_t dst_bytes = out_buffer.size(); |
| 339 | 339 | ||
| 340 | while (0 != src_bytes) { | 340 | while (0 != src_bytes) { |
| 341 | size_t const iconv_result = | 341 | std::size_t const iconv_result = |
| 342 | iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); | 342 | iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); |
| 343 | 343 | ||
| 344 | if (static_cast<size_t>(-1) == iconv_result) { | 344 | if (static_cast<std::size_t>(-1) == iconv_result) { |
| 345 | if (EILSEQ == errno || EINVAL == errno) { | 345 | if (EILSEQ == errno || EINVAL == errno) { |
| 346 | // Try to skip the bad character | 346 | // Try to skip the bad character |
| 347 | if (0 != src_bytes) { | 347 | if (0 != src_bytes) { |
| @@ -381,8 +381,8 @@ std::string SHIFTJISToUTF8(const std::string& input) { | |||
| 381 | 381 | ||
| 382 | #endif | 382 | #endif |
| 383 | 383 | ||
| 384 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len) { | 384 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) { |
| 385 | size_t len = 0; | 385 | std::size_t len = 0; |
| 386 | while (len < max_len && buffer[len] != '\0') | 386 | while (len < max_len && buffer[len] != '\0') |
| 387 | ++len; | 387 | ++len; |
| 388 | 388 | ||
diff --git a/src/common/string_util.h b/src/common/string_util.h index 4a2143b59..dcca6bc38 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -19,7 +19,7 @@ std::string ToLower(std::string str); | |||
| 19 | /// Make a string uppercase | 19 | /// Make a string uppercase |
| 20 | std::string ToUpper(std::string str); | 20 | std::string ToUpper(std::string str); |
| 21 | 21 | ||
| 22 | std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true); | 22 | std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true); |
| 23 | 23 | ||
| 24 | std::string StringFromBuffer(const std::vector<u8>& data); | 24 | std::string StringFromBuffer(const std::vector<u8>& data); |
| 25 | 25 | ||
| @@ -118,7 +118,7 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) { | |||
| 118 | * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't | 118 | * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't |
| 119 | * NUL-terminated then the string ends at max_len characters. | 119 | * NUL-terminated then the string ends at max_len characters. |
| 120 | */ | 120 | */ |
| 121 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); | 121 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); |
| 122 | 122 | ||
| 123 | /** | 123 | /** |
| 124 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | 124 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's |
diff --git a/src/common/thread.h b/src/common/thread.h index 9465e1de7..12a1c095c 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -60,12 +60,12 @@ private: | |||
| 60 | 60 | ||
| 61 | class Barrier { | 61 | class Barrier { |
| 62 | public: | 62 | public: |
| 63 | explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {} | 63 | explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {} |
| 64 | 64 | ||
| 65 | /// Blocks until all "count" threads have called Sync() | 65 | /// Blocks until all "count" threads have called Sync() |
| 66 | void Sync() { | 66 | void Sync() { |
| 67 | std::unique_lock<std::mutex> lk(mutex); | 67 | std::unique_lock<std::mutex> lk(mutex); |
| 68 | const size_t current_generation = generation; | 68 | const std::size_t current_generation = generation; |
| 69 | 69 | ||
| 70 | if (++waiting == count) { | 70 | if (++waiting == count) { |
| 71 | generation++; | 71 | generation++; |
| @@ -80,9 +80,9 @@ public: | |||
| 80 | private: | 80 | private: |
| 81 | std::condition_variable condvar; | 81 | std::condition_variable condvar; |
| 82 | std::mutex mutex; | 82 | std::mutex mutex; |
| 83 | const size_t count; | 83 | const std::size_t count; |
| 84 | size_t waiting; | 84 | std::size_t waiting; |
| 85 | size_t generation; // Incremented once each time the barrier is used | 85 | std::size_t generation; // Incremented once each time the barrier is used |
| 86 | }; | 86 | }; |
| 87 | 87 | ||
| 88 | void SleepCurrentThread(int ms); | 88 | void SleepCurrentThread(int ms); |
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index 927da9187..636a5c0f9 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h | |||
| @@ -97,7 +97,7 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ | |||
| 97 | Xbyak::util::xmm15, | 97 | Xbyak::util::xmm15, |
| 98 | }); | 98 | }); |
| 99 | 99 | ||
| 100 | constexpr size_t ABI_SHADOW_SPACE = 0x20; | 100 | constexpr std::size_t ABI_SHADOW_SPACE = 0x20; |
| 101 | 101 | ||
| 102 | #else | 102 | #else |
| 103 | 103 | ||
| @@ -147,22 +147,23 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ | |||
| 147 | Xbyak::util::r15, | 147 | Xbyak::util::r15, |
| 148 | }); | 148 | }); |
| 149 | 149 | ||
| 150 | constexpr size_t ABI_SHADOW_SPACE = 0; | 150 | constexpr std::size_t ABI_SHADOW_SPACE = 0; |
| 151 | 151 | ||
| 152 | #endif | 152 | #endif |
| 153 | 153 | ||
| 154 | inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, | 154 | inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment, |
| 155 | s32* out_subtraction, s32* out_xmm_offset) { | 155 | std::size_t needed_frame_size, s32* out_subtraction, |
| 156 | s32* out_xmm_offset) { | ||
| 156 | int count = (regs & ABI_ALL_GPRS).Count(); | 157 | int count = (regs & ABI_ALL_GPRS).Count(); |
| 157 | rsp_alignment -= count * 8; | 158 | rsp_alignment -= count * 8; |
| 158 | size_t subtraction = 0; | 159 | std::size_t subtraction = 0; |
| 159 | int xmm_count = (regs & ABI_ALL_XMMS).Count(); | 160 | int xmm_count = (regs & ABI_ALL_XMMS).Count(); |
| 160 | if (xmm_count) { | 161 | if (xmm_count) { |
| 161 | // If we have any XMMs to save, we must align the stack here. | 162 | // If we have any XMMs to save, we must align the stack here. |
| 162 | subtraction = rsp_alignment & 0xF; | 163 | subtraction = rsp_alignment & 0xF; |
| 163 | } | 164 | } |
| 164 | subtraction += 0x10 * xmm_count; | 165 | subtraction += 0x10 * xmm_count; |
| 165 | size_t xmm_base_subtraction = subtraction; | 166 | std::size_t xmm_base_subtraction = subtraction; |
| 166 | subtraction += needed_frame_size; | 167 | subtraction += needed_frame_size; |
| 167 | subtraction += ABI_SHADOW_SPACE; | 168 | subtraction += ABI_SHADOW_SPACE; |
| 168 | // Final alignment. | 169 | // Final alignment. |
| @@ -173,8 +174,9 @@ inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t n | |||
| 173 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); | 174 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | 177 | inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, |
| 177 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 178 | std::size_t rsp_alignment, |
| 179 | std::size_t needed_frame_size = 0) { | ||
| 178 | s32 subtraction, xmm_offset; | 180 | s32 subtraction, xmm_offset; |
| 179 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 181 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); |
| 180 | 182 | ||
| @@ -195,7 +197,8 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet | |||
| 195 | } | 197 | } |
| 196 | 198 | ||
| 197 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | 199 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, |
| 198 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 200 | std::size_t rsp_alignment, |
| 201 | std::size_t needed_frame_size = 0) { | ||
| 199 | s32 subtraction, xmm_offset; | 202 | s32 subtraction, xmm_offset; |
| 200 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 203 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); |
| 201 | 204 | ||
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h index 02323a017..5cc8a8c76 100644 --- a/src/common/x64/xbyak_util.h +++ b/src/common/x64/xbyak_util.h | |||
| @@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { | |||
| 34 | template <typename T> | 34 | template <typename T> |
| 35 | inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { | 35 | inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { |
| 36 | static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); | 36 | static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); |
| 37 | size_t addr = reinterpret_cast<size_t>(f); | 37 | std::size_t addr = reinterpret_cast<std::size_t>(f); |
| 38 | if (IsWithin2G(code, addr)) { | 38 | if (IsWithin2G(code, addr)) { |
| 39 | code.call(f); | 39 | code.call(f); |
| 40 | } else { | 40 | } else { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a74270a0f..26f727d96 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -35,8 +35,12 @@ add_library(core STATIC | |||
| 35 | file_sys/mode.h | 35 | file_sys/mode.h |
| 36 | file_sys/nca_metadata.cpp | 36 | file_sys/nca_metadata.cpp |
| 37 | file_sys/nca_metadata.h | 37 | file_sys/nca_metadata.h |
| 38 | file_sys/nca_patch.cpp | ||
| 39 | file_sys/nca_patch.h | ||
| 38 | file_sys/partition_filesystem.cpp | 40 | file_sys/partition_filesystem.cpp |
| 39 | file_sys/partition_filesystem.h | 41 | file_sys/partition_filesystem.h |
| 42 | file_sys/patch_manager.cpp | ||
| 43 | file_sys/patch_manager.h | ||
| 40 | file_sys/program_metadata.cpp | 44 | file_sys/program_metadata.cpp |
| 41 | file_sys/program_metadata.h | 45 | file_sys/program_metadata.h |
| 42 | file_sys/registered_cache.cpp | 46 | file_sys/registered_cache.cpp |
| @@ -49,6 +53,8 @@ add_library(core STATIC | |||
| 49 | file_sys/savedata_factory.h | 53 | file_sys/savedata_factory.h |
| 50 | file_sys/sdmc_factory.cpp | 54 | file_sys/sdmc_factory.cpp |
| 51 | file_sys/sdmc_factory.h | 55 | file_sys/sdmc_factory.h |
| 56 | file_sys/submission_package.cpp | ||
| 57 | file_sys/submission_package.h | ||
| 52 | file_sys/vfs.cpp | 58 | file_sys/vfs.cpp |
| 53 | file_sys/vfs.h | 59 | file_sys/vfs.h |
| 54 | file_sys/vfs_concat.cpp | 60 | file_sys/vfs_concat.cpp |
| @@ -359,6 +365,8 @@ add_library(core STATIC | |||
| 359 | loader/nro.h | 365 | loader/nro.h |
| 360 | loader/nso.cpp | 366 | loader/nso.cpp |
| 361 | loader/nso.h | 367 | loader/nso.h |
| 368 | loader/nsp.cpp | ||
| 369 | loader/nsp.h | ||
| 362 | loader/xci.cpp | 370 | loader/xci.cpp |
| 363 | loader/xci.h | 371 | loader/xci.h |
| 364 | memory.cpp | 372 | memory.cpp |
| @@ -380,7 +388,7 @@ add_library(core STATIC | |||
| 380 | create_target_directory_groups(core) | 388 | create_target_directory_groups(core) |
| 381 | 389 | ||
| 382 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 390 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 383 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn) | 391 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) |
| 384 | 392 | ||
| 385 | if (ARCHITECTURE_x86_64) | 393 | if (ARCHITECTURE_x86_64) |
| 386 | target_sources(core PRIVATE | 394 | target_sources(core PRIVATE |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index c368745b1..16d528994 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -6,11 +6,14 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/kernel/vm_manager.h" | 9 | |
| 10 | namespace Kernel { | ||
| 11 | enum class VMAPermission : u8; | ||
| 12 | } | ||
| 10 | 13 | ||
| 11 | namespace Core { | 14 | namespace Core { |
| 12 | 15 | ||
| 13 | /// Generic ARM11 CPU interface | 16 | /// Generic ARMv8 CPU interface |
| 14 | class ARM_Interface : NonCopyable { | 17 | class ARM_Interface : NonCopyable { |
| 15 | public: | 18 | public: |
| 16 | virtual ~ARM_Interface() {} | 19 | virtual ~ARM_Interface() {} |
| @@ -19,9 +22,9 @@ public: | |||
| 19 | std::array<u64, 31> cpu_registers; | 22 | std::array<u64, 31> cpu_registers; |
| 20 | u64 sp; | 23 | u64 sp; |
| 21 | u64 pc; | 24 | u64 pc; |
| 22 | u64 cpsr; | 25 | u64 pstate; |
| 23 | std::array<u128, 32> fpu_registers; | 26 | std::array<u128, 32> vector_registers; |
| 24 | u64 fpscr; | 27 | u64 fpcr; |
| 25 | }; | 28 | }; |
| 26 | 29 | ||
| 27 | /// Runs the CPU until an event happens | 30 | /// Runs the CPU until an event happens |
| @@ -31,11 +34,11 @@ public: | |||
| 31 | virtual void Step() = 0; | 34 | virtual void Step() = 0; |
| 32 | 35 | ||
| 33 | /// Maps a backing memory region for the CPU | 36 | /// Maps a backing memory region for the CPU |
| 34 | virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, | 37 | virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 35 | Kernel::VMAPermission perms) = 0; | 38 | Kernel::VMAPermission perms) = 0; |
| 36 | 39 | ||
| 37 | /// Unmaps a region of memory that was previously mapped using MapBackingMemory | 40 | /// Unmaps a region of memory that was previously mapped using MapBackingMemory |
| 38 | virtual void UnmapMemory(VAddr address, size_t size) = 0; | 41 | virtual void UnmapMemory(VAddr address, std::size_t size) = 0; |
| 39 | 42 | ||
| 40 | /// Clear all instruction cache | 43 | /// Clear all instruction cache |
| 41 | virtual void ClearInstructionCache() = 0; | 44 | virtual void ClearInstructionCache() = 0; |
| @@ -69,42 +72,50 @@ public: | |||
| 69 | */ | 72 | */ |
| 70 | virtual void SetReg(int index, u64 value) = 0; | 73 | virtual void SetReg(int index, u64 value) = 0; |
| 71 | 74 | ||
| 72 | virtual u128 GetExtReg(int index) const = 0; | ||
| 73 | |||
| 74 | virtual void SetExtReg(int index, u128 value) = 0; | ||
| 75 | |||
| 76 | /** | 75 | /** |
| 77 | * Gets the value of a VFP register | 76 | * Gets the value of a specified vector register. |
| 78 | * @param index Register index (0-31) | 77 | * |
| 79 | * @return Returns the value in the register | 78 | * @param index The index of the vector register. |
| 79 | * @return the value within the vector register. | ||
| 80 | */ | 80 | */ |
| 81 | virtual u32 GetVFPReg(int index) const = 0; | 81 | virtual u128 GetVectorReg(int index) const = 0; |
| 82 | 82 | ||
| 83 | /** | 83 | /** |
| 84 | * Sets a VFP register to the given value | 84 | * Sets a given value into a vector register. |
| 85 | * @param index Register index (0-31) | 85 | * |
| 86 | * @param value Value to set register to | 86 | * @param index The index of the vector register. |
| 87 | * @param value The new value to place in the register. | ||
| 87 | */ | 88 | */ |
| 88 | virtual void SetVFPReg(int index, u32 value) = 0; | 89 | virtual void SetVectorReg(int index, u128 value) = 0; |
| 89 | 90 | ||
| 90 | /** | 91 | /** |
| 91 | * Get the current CPSR register | 92 | * Get the current PSTATE register |
| 92 | * @return Returns the value of the CPSR register | 93 | * @return Returns the value of the PSTATE register |
| 93 | */ | 94 | */ |
| 94 | virtual u32 GetCPSR() const = 0; | 95 | virtual u32 GetPSTATE() const = 0; |
| 95 | 96 | ||
| 96 | /** | 97 | /** |
| 97 | * Set the current CPSR register | 98 | * Set the current PSTATE register |
| 98 | * @param cpsr Value to set CPSR to | 99 | * @param pstate Value to set PSTATE to |
| 99 | */ | 100 | */ |
| 100 | virtual void SetCPSR(u32 cpsr) = 0; | 101 | virtual void SetPSTATE(u32 pstate) = 0; |
| 101 | 102 | ||
| 102 | virtual VAddr GetTlsAddress() const = 0; | 103 | virtual VAddr GetTlsAddress() const = 0; |
| 103 | 104 | ||
| 104 | virtual void SetTlsAddress(VAddr address) = 0; | 105 | virtual void SetTlsAddress(VAddr address) = 0; |
| 105 | 106 | ||
| 107 | /** | ||
| 108 | * Gets the value within the TPIDR_EL0 (read/write software thread ID) register. | ||
| 109 | * | ||
| 110 | * @return the value within the register. | ||
| 111 | */ | ||
| 106 | virtual u64 GetTPIDR_EL0() const = 0; | 112 | virtual u64 GetTPIDR_EL0() const = 0; |
| 107 | 113 | ||
| 114 | /** | ||
| 115 | * Sets a new value within the TPIDR_EL0 (read/write software thread ID) register. | ||
| 116 | * | ||
| 117 | * @param value The new value to place in the register. | ||
| 118 | */ | ||
| 108 | virtual void SetTPIDR_EL0(u64 value) = 0; | 119 | virtual void SetTPIDR_EL0(u64 value) = 0; |
| 109 | 120 | ||
| 110 | /** | 121 | /** |
| @@ -119,6 +130,7 @@ public: | |||
| 119 | */ | 130 | */ |
| 120 | virtual void LoadContext(const ThreadContext& ctx) = 0; | 131 | virtual void LoadContext(const ThreadContext& ctx) = 0; |
| 121 | 132 | ||
| 133 | /// Clears the exclusive monitor's state. | ||
| 122 | virtual void ClearExclusiveState() = 0; | 134 | virtual void ClearExclusiveState() = 0; |
| 123 | 135 | ||
| 124 | /// Prepare core for thread reschedule (if needed to correctly handle state) | 136 | /// Prepare core for thread reschedule (if needed to correctly handle state) |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index de44ccebd..7be5a38de 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -7,12 +7,15 @@ | |||
| 7 | #include <dynarmic/A64/a64.h> | 7 | #include <dynarmic/A64/a64.h> |
| 8 | #include <dynarmic/A64/config.h> | 8 | #include <dynarmic/A64/config.h> |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/microprofile.h" | ||
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | 11 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 11 | #include "core/core.h" | 12 | #include "core/core.h" |
| 12 | #include "core/core_cpu.h" | 13 | #include "core/core_cpu.h" |
| 13 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/gdbstub/gdbstub.h" | ||
| 14 | #include "core/hle/kernel/process.h" | 16 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/kernel/svc.h" | 17 | #include "core/hle/kernel/svc.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | ||
| 16 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 17 | 20 | ||
| 18 | namespace Core { | 21 | namespace Core { |
| @@ -57,7 +60,7 @@ public: | |||
| 57 | Memory::Write64(vaddr + 8, value[1]); | 60 | Memory::Write64(vaddr + 8, value[1]); |
| 58 | } | 61 | } |
| 59 | 62 | ||
| 60 | void InterpreterFallback(u64 pc, size_t num_instructions) override { | 63 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { |
| 61 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 64 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 62 | num_instructions, MemoryReadCode(pc)); | 65 | num_instructions, MemoryReadCode(pc)); |
| 63 | 66 | ||
| @@ -78,9 +81,20 @@ public: | |||
| 78 | case Dynarmic::A64::Exception::SendEventLocal: | 81 | case Dynarmic::A64::Exception::SendEventLocal: |
| 79 | case Dynarmic::A64::Exception::Yield: | 82 | case Dynarmic::A64::Exception::Yield: |
| 80 | return; | 83 | return; |
| 84 | case Dynarmic::A64::Exception::Breakpoint: | ||
| 85 | if (GDBStub::IsServerEnabled()) { | ||
| 86 | parent.jit->HaltExecution(); | ||
| 87 | parent.SetPC(pc); | ||
| 88 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||
| 89 | parent.SaveContext(thread->context); | ||
| 90 | GDBStub::Break(); | ||
| 91 | GDBStub::SendTrap(thread, 5); | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | [[fallthrough]]; | ||
| 81 | default: | 95 | default: |
| 82 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", | 96 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", |
| 83 | static_cast<size_t>(exception), pc); | 97 | static_cast<std::size_t>(exception), pc); |
| 84 | } | 98 | } |
| 85 | } | 99 | } |
| 86 | 100 | ||
| @@ -109,7 +123,7 @@ public: | |||
| 109 | } | 123 | } |
| 110 | 124 | ||
| 111 | ARM_Dynarmic& parent; | 125 | ARM_Dynarmic& parent; |
| 112 | size_t num_interpreted_instructions = 0; | 126 | std::size_t num_interpreted_instructions = 0; |
| 113 | u64 tpidrro_el0 = 0; | 127 | u64 tpidrro_el0 = 0; |
| 114 | u64 tpidr_el0 = 0; | 128 | u64 tpidr_el0 = 0; |
| 115 | }; | 129 | }; |
| @@ -143,7 +157,10 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { | |||
| 143 | return std::make_unique<Dynarmic::A64::Jit>(config); | 157 | return std::make_unique<Dynarmic::A64::Jit>(config); |
| 144 | } | 158 | } |
| 145 | 159 | ||
| 160 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | ||
| 161 | |||
| 146 | void ARM_Dynarmic::Run() { | 162 | void ARM_Dynarmic::Run() { |
| 163 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic); | ||
| 147 | ASSERT(Memory::GetCurrentPageTable() == current_page_table); | 164 | ASSERT(Memory::GetCurrentPageTable() == current_page_table); |
| 148 | 165 | ||
| 149 | jit->Run(); | 166 | jit->Run(); |
| @@ -153,7 +170,8 @@ void ARM_Dynarmic::Step() { | |||
| 153 | cb->InterpreterFallback(jit->GetPC(), 1); | 170 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 154 | } | 171 | } |
| 155 | 172 | ||
| 156 | ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) | 173 | ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, |
| 174 | std::size_t core_index) | ||
| 157 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, | 175 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, |
| 158 | exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { | 176 | exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { |
| 159 | ThreadContext ctx; | 177 | ThreadContext ctx; |
| @@ -164,12 +182,12 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | |||
| 164 | 182 | ||
| 165 | ARM_Dynarmic::~ARM_Dynarmic() = default; | 183 | ARM_Dynarmic::~ARM_Dynarmic() = default; |
| 166 | 184 | ||
| 167 | void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory, | 185 | void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory, |
| 168 | Kernel::VMAPermission perms) { | 186 | Kernel::VMAPermission perms) { |
| 169 | inner_unicorn.MapBackingMemory(address, size, memory, perms); | 187 | inner_unicorn.MapBackingMemory(address, size, memory, perms); |
| 170 | } | 188 | } |
| 171 | 189 | ||
| 172 | void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) { | 190 | void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) { |
| 173 | inner_unicorn.UnmapMemory(address, size); | 191 | inner_unicorn.UnmapMemory(address, size); |
| 174 | } | 192 | } |
| 175 | 193 | ||
| @@ -189,29 +207,20 @@ void ARM_Dynarmic::SetReg(int index, u64 value) { | |||
| 189 | jit->SetRegister(index, value); | 207 | jit->SetRegister(index, value); |
| 190 | } | 208 | } |
| 191 | 209 | ||
| 192 | u128 ARM_Dynarmic::GetExtReg(int index) const { | 210 | u128 ARM_Dynarmic::GetVectorReg(int index) const { |
| 193 | return jit->GetVector(index); | 211 | return jit->GetVector(index); |
| 194 | } | 212 | } |
| 195 | 213 | ||
| 196 | void ARM_Dynarmic::SetExtReg(int index, u128 value) { | 214 | void ARM_Dynarmic::SetVectorReg(int index, u128 value) { |
| 197 | jit->SetVector(index, value); | 215 | jit->SetVector(index, value); |
| 198 | } | 216 | } |
| 199 | 217 | ||
| 200 | u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const { | 218 | u32 ARM_Dynarmic::GetPSTATE() const { |
| 201 | UNIMPLEMENTED(); | ||
| 202 | return {}; | ||
| 203 | } | ||
| 204 | |||
| 205 | void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) { | ||
| 206 | UNIMPLEMENTED(); | ||
| 207 | } | ||
| 208 | |||
| 209 | u32 ARM_Dynarmic::GetCPSR() const { | ||
| 210 | return jit->GetPstate(); | 219 | return jit->GetPstate(); |
| 211 | } | 220 | } |
| 212 | 221 | ||
| 213 | void ARM_Dynarmic::SetCPSR(u32 cpsr) { | 222 | void ARM_Dynarmic::SetPSTATE(u32 pstate) { |
| 214 | jit->SetPstate(cpsr); | 223 | jit->SetPstate(pstate); |
| 215 | } | 224 | } |
| 216 | 225 | ||
| 217 | u64 ARM_Dynarmic::GetTlsAddress() const { | 226 | u64 ARM_Dynarmic::GetTlsAddress() const { |
| @@ -234,18 +243,18 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { | |||
| 234 | ctx.cpu_registers = jit->GetRegisters(); | 243 | ctx.cpu_registers = jit->GetRegisters(); |
| 235 | ctx.sp = jit->GetSP(); | 244 | ctx.sp = jit->GetSP(); |
| 236 | ctx.pc = jit->GetPC(); | 245 | ctx.pc = jit->GetPC(); |
| 237 | ctx.cpsr = jit->GetPstate(); | 246 | ctx.pstate = jit->GetPstate(); |
| 238 | ctx.fpu_registers = jit->GetVectors(); | 247 | ctx.vector_registers = jit->GetVectors(); |
| 239 | ctx.fpscr = jit->GetFpcr(); | 248 | ctx.fpcr = jit->GetFpcr(); |
| 240 | } | 249 | } |
| 241 | 250 | ||
| 242 | void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { | 251 | void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { |
| 243 | jit->SetRegisters(ctx.cpu_registers); | 252 | jit->SetRegisters(ctx.cpu_registers); |
| 244 | jit->SetSP(ctx.sp); | 253 | jit->SetSP(ctx.sp); |
| 245 | jit->SetPC(ctx.pc); | 254 | jit->SetPC(ctx.pc); |
| 246 | jit->SetPstate(static_cast<u32>(ctx.cpsr)); | 255 | jit->SetPstate(static_cast<u32>(ctx.pstate)); |
| 247 | jit->SetVectors(ctx.fpu_registers); | 256 | jit->SetVectors(ctx.vector_registers); |
| 248 | jit->SetFpcr(static_cast<u32>(ctx.fpscr)); | 257 | jit->SetFpcr(static_cast<u32>(ctx.fpcr)); |
| 249 | } | 258 | } |
| 250 | 259 | ||
| 251 | void ARM_Dynarmic::PrepareReschedule() { | 260 | void ARM_Dynarmic::PrepareReschedule() { |
| @@ -265,10 +274,10 @@ void ARM_Dynarmic::PageTableChanged() { | |||
| 265 | current_page_table = Memory::GetCurrentPageTable(); | 274 | current_page_table = Memory::GetCurrentPageTable(); |
| 266 | } | 275 | } |
| 267 | 276 | ||
| 268 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {} | 277 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} |
| 269 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | 278 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; |
| 270 | 279 | ||
| 271 | void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) { | 280 | void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { |
| 272 | // Size doesn't actually matter. | 281 | // Size doesn't actually matter. |
| 273 | monitor.Mark(core_index, addr, 16); | 282 | monitor.Mark(core_index, addr, 16); |
| 274 | } | 283 | } |
| @@ -277,30 +286,30 @@ void DynarmicExclusiveMonitor::ClearExclusive() { | |||
| 277 | monitor.Clear(); | 286 | monitor.Clear(); |
| 278 | } | 287 | } |
| 279 | 288 | ||
| 280 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) { | 289 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { |
| 281 | return monitor.DoExclusiveOperation(core_index, vaddr, 1, | 290 | return monitor.DoExclusiveOperation(core_index, vaddr, 1, |
| 282 | [&] { Memory::Write8(vaddr, value); }); | 291 | [&] { Memory::Write8(vaddr, value); }); |
| 283 | } | 292 | } |
| 284 | 293 | ||
| 285 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) { | 294 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { |
| 286 | return monitor.DoExclusiveOperation(core_index, vaddr, 2, | 295 | return monitor.DoExclusiveOperation(core_index, vaddr, 2, |
| 287 | [&] { Memory::Write16(vaddr, value); }); | 296 | [&] { Memory::Write16(vaddr, value); }); |
| 288 | } | 297 | } |
| 289 | 298 | ||
| 290 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) { | 299 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { |
| 291 | return monitor.DoExclusiveOperation(core_index, vaddr, 4, | 300 | return monitor.DoExclusiveOperation(core_index, vaddr, 4, |
| 292 | [&] { Memory::Write32(vaddr, value); }); | 301 | [&] { Memory::Write32(vaddr, value); }); |
| 293 | } | 302 | } |
| 294 | 303 | ||
| 295 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) { | 304 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { |
| 296 | return monitor.DoExclusiveOperation(core_index, vaddr, 8, | 305 | return monitor.DoExclusiveOperation(core_index, vaddr, 8, |
| 297 | [&] { Memory::Write64(vaddr, value); }); | 306 | [&] { Memory::Write64(vaddr, value); }); |
| 298 | } | 307 | } |
| 299 | 308 | ||
| 300 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) { | 309 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { |
| 301 | return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { | 310 | return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { |
| 302 | Memory::Write64(vaddr, value[0]); | 311 | Memory::Write64(vaddr + 0, value[0]); |
| 303 | Memory::Write64(vaddr, value[1]); | 312 | Memory::Write64(vaddr + 8, value[1]); |
| 304 | }); | 313 | }); |
| 305 | } | 314 | } |
| 306 | 315 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 3bdfd8cd9..4ee92ee27 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | 13 | #include "core/arm/unicorn/arm_unicorn.h" |
| 14 | 14 | ||
| 15 | namespace Memory { | ||
| 16 | struct PageTable; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Core { | 19 | namespace Core { |
| 16 | 20 | ||
| 17 | class ARM_Dynarmic_Callbacks; | 21 | class ARM_Dynarmic_Callbacks; |
| @@ -19,24 +23,22 @@ class DynarmicExclusiveMonitor; | |||
| 19 | 23 | ||
| 20 | class ARM_Dynarmic final : public ARM_Interface { | 24 | class ARM_Dynarmic final : public ARM_Interface { |
| 21 | public: | 25 | public: |
| 22 | ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index); | 26 | ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); |
| 23 | ~ARM_Dynarmic(); | 27 | ~ARM_Dynarmic(); |
| 24 | 28 | ||
| 25 | void MapBackingMemory(VAddr address, size_t size, u8* memory, | 29 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 26 | Kernel::VMAPermission perms) override; | 30 | Kernel::VMAPermission perms) override; |
| 27 | void UnmapMemory(u64 address, size_t size) override; | 31 | void UnmapMemory(u64 address, std::size_t size) override; |
| 28 | void SetPC(u64 pc) override; | 32 | void SetPC(u64 pc) override; |
| 29 | u64 GetPC() const override; | 33 | u64 GetPC() const override; |
| 30 | u64 GetReg(int index) const override; | 34 | u64 GetReg(int index) const override; |
| 31 | void SetReg(int index, u64 value) override; | 35 | void SetReg(int index, u64 value) override; |
| 32 | u128 GetExtReg(int index) const override; | 36 | u128 GetVectorReg(int index) const override; |
| 33 | void SetExtReg(int index, u128 value) override; | 37 | void SetVectorReg(int index, u128 value) override; |
| 34 | u32 GetVFPReg(int index) const override; | 38 | u32 GetPSTATE() const override; |
| 35 | void SetVFPReg(int index, u32 value) override; | 39 | void SetPSTATE(u32 pstate) override; |
| 36 | u32 GetCPSR() const override; | ||
| 37 | void Run() override; | 40 | void Run() override; |
| 38 | void Step() override; | 41 | void Step() override; |
| 39 | void SetCPSR(u32 cpsr) override; | ||
| 40 | VAddr GetTlsAddress() const override; | 42 | VAddr GetTlsAddress() const override; |
| 41 | void SetTlsAddress(VAddr address) override; | 43 | void SetTlsAddress(VAddr address) override; |
| 42 | void SetTPIDR_EL0(u64 value) override; | 44 | void SetTPIDR_EL0(u64 value) override; |
| @@ -59,7 +61,7 @@ private: | |||
| 59 | std::unique_ptr<Dynarmic::A64::Jit> jit; | 61 | std::unique_ptr<Dynarmic::A64::Jit> jit; |
| 60 | ARM_Unicorn inner_unicorn; | 62 | ARM_Unicorn inner_unicorn; |
| 61 | 63 | ||
| 62 | size_t core_index; | 64 | std::size_t core_index; |
| 63 | std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; | 65 | std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; |
| 64 | 66 | ||
| 65 | Memory::PageTable* current_page_table = nullptr; | 67 | Memory::PageTable* current_page_table = nullptr; |
| @@ -67,17 +69,17 @@ private: | |||
| 67 | 69 | ||
| 68 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | 70 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { |
| 69 | public: | 71 | public: |
| 70 | explicit DynarmicExclusiveMonitor(size_t core_count); | 72 | explicit DynarmicExclusiveMonitor(std::size_t core_count); |
| 71 | ~DynarmicExclusiveMonitor(); | 73 | ~DynarmicExclusiveMonitor(); |
| 72 | 74 | ||
| 73 | void SetExclusive(size_t core_index, VAddr addr) override; | 75 | void SetExclusive(std::size_t core_index, VAddr addr) override; |
| 74 | void ClearExclusive() override; | 76 | void ClearExclusive() override; |
| 75 | 77 | ||
| 76 | bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override; | 78 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; |
| 77 | bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override; | 79 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; |
| 78 | bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override; | 80 | bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; |
| 79 | bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override; | 81 | bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; |
| 80 | bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override; | 82 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; |
| 81 | 83 | ||
| 82 | private: | 84 | private: |
| 83 | friend class ARM_Dynarmic; | 85 | friend class ARM_Dynarmic; |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index 6f9b51573..f59aca667 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -12,14 +12,14 @@ class ExclusiveMonitor { | |||
| 12 | public: | 12 | public: |
| 13 | virtual ~ExclusiveMonitor(); | 13 | virtual ~ExclusiveMonitor(); |
| 14 | 14 | ||
| 15 | virtual void SetExclusive(size_t core_index, VAddr addr) = 0; | 15 | virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0; |
| 16 | virtual void ClearExclusive() = 0; | 16 | virtual void ClearExclusive() = 0; |
| 17 | 17 | ||
| 18 | virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0; | 18 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; |
| 19 | virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0; | 19 | virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0; |
| 20 | virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0; | 20 | virtual bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) = 0; |
| 21 | virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0; | 21 | virtual bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) = 0; |
| 22 | virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0; | 22 | virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | } // namespace Core | 25 | } // namespace Core |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 307f12198..e218a0b15 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -90,12 +90,12 @@ ARM_Unicorn::~ARM_Unicorn() { | |||
| 90 | CHECKED(uc_close(uc)); | 90 | CHECKED(uc_close(uc)); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory, | 93 | void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 94 | Kernel::VMAPermission perms) { | 94 | Kernel::VMAPermission perms) { |
| 95 | CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); | 95 | CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) { | 98 | void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) { |
| 99 | CHECKED(uc_mem_unmap(uc, address, size)); | 99 | CHECKED(uc_mem_unmap(uc, address, size)); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| @@ -131,33 +131,24 @@ void ARM_Unicorn::SetReg(int regn, u64 val) { | |||
| 131 | CHECKED(uc_reg_write(uc, treg, &val)); | 131 | CHECKED(uc_reg_write(uc, treg, &val)); |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | u128 ARM_Unicorn::GetExtReg(int /*index*/) const { | 134 | u128 ARM_Unicorn::GetVectorReg(int /*index*/) const { |
| 135 | UNIMPLEMENTED(); | 135 | UNIMPLEMENTED(); |
| 136 | static constexpr u128 res{}; | 136 | static constexpr u128 res{}; |
| 137 | return res; | 137 | return res; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) { | 140 | void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) { |
| 141 | UNIMPLEMENTED(); | 141 | UNIMPLEMENTED(); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | u32 ARM_Unicorn::GetVFPReg(int /*index*/) const { | 144 | u32 ARM_Unicorn::GetPSTATE() const { |
| 145 | UNIMPLEMENTED(); | ||
| 146 | return {}; | ||
| 147 | } | ||
| 148 | |||
| 149 | void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) { | ||
| 150 | UNIMPLEMENTED(); | ||
| 151 | } | ||
| 152 | |||
| 153 | u32 ARM_Unicorn::GetCPSR() const { | ||
| 154 | u64 nzcv{}; | 145 | u64 nzcv{}; |
| 155 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv)); | 146 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv)); |
| 156 | return static_cast<u32>(nzcv); | 147 | return static_cast<u32>(nzcv); |
| 157 | } | 148 | } |
| 158 | 149 | ||
| 159 | void ARM_Unicorn::SetCPSR(u32 cpsr) { | 150 | void ARM_Unicorn::SetPSTATE(u32 pstate) { |
| 160 | u64 nzcv = cpsr; | 151 | u64 nzcv = pstate; |
| 161 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv)); | 152 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv)); |
| 162 | } | 153 | } |
| 163 | 154 | ||
| @@ -193,10 +184,10 @@ void ARM_Unicorn::Step() { | |||
| 193 | ExecuteInstructions(1); | 184 | ExecuteInstructions(1); |
| 194 | } | 185 | } |
| 195 | 186 | ||
| 196 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); | 187 | MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); |
| 197 | 188 | ||
| 198 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | 189 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { |
| 199 | MICROPROFILE_SCOPE(ARM_Jit); | 190 | MICROPROFILE_SCOPE(ARM_Jit_Unicorn); |
| 200 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | 191 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); |
| 201 | CoreTiming::AddTicks(num_instructions); | 192 | CoreTiming::AddTicks(num_instructions); |
| 202 | if (GDBStub::IsServerEnabled()) { | 193 | if (GDBStub::IsServerEnabled()) { |
| @@ -219,7 +210,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { | |||
| 219 | 210 | ||
| 220 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp)); | 211 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp)); |
| 221 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc)); | 212 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc)); |
| 222 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); | 213 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate)); |
| 223 | 214 | ||
| 224 | for (auto i = 0; i < 29; ++i) { | 215 | for (auto i = 0; i < 29; ++i) { |
| 225 | uregs[i] = UC_ARM64_REG_X0 + i; | 216 | uregs[i] = UC_ARM64_REG_X0 + i; |
| @@ -234,7 +225,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { | |||
| 234 | 225 | ||
| 235 | for (int i = 0; i < 32; ++i) { | 226 | for (int i = 0; i < 32; ++i) { |
| 236 | uregs[i] = UC_ARM64_REG_Q0 + i; | 227 | uregs[i] = UC_ARM64_REG_Q0 + i; |
| 237 | tregs[i] = &ctx.fpu_registers[i]; | 228 | tregs[i] = &ctx.vector_registers[i]; |
| 238 | } | 229 | } |
| 239 | 230 | ||
| 240 | CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); | 231 | CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); |
| @@ -246,7 +237,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { | |||
| 246 | 237 | ||
| 247 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp)); | 238 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp)); |
| 248 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc)); | 239 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc)); |
| 249 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); | 240 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate)); |
| 250 | 241 | ||
| 251 | for (int i = 0; i < 29; ++i) { | 242 | for (int i = 0; i < 29; ++i) { |
| 252 | uregs[i] = UC_ARM64_REG_X0 + i; | 243 | uregs[i] = UC_ARM64_REG_X0 + i; |
| @@ -261,7 +252,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { | |||
| 261 | 252 | ||
| 262 | for (auto i = 0; i < 32; ++i) { | 253 | for (auto i = 0; i < 32; ++i) { |
| 263 | uregs[i] = UC_ARM64_REG_Q0 + i; | 254 | uregs[i] = UC_ARM64_REG_Q0 + i; |
| 264 | tregs[i] = (void*)&ctx.fpu_registers[i]; | 255 | tregs[i] = (void*)&ctx.vector_registers[i]; |
| 265 | } | 256 | } |
| 266 | 257 | ||
| 267 | CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32)); | 258 | CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32)); |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index bd6b2f723..75761950b 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -15,19 +15,17 @@ class ARM_Unicorn final : public ARM_Interface { | |||
| 15 | public: | 15 | public: |
| 16 | ARM_Unicorn(); | 16 | ARM_Unicorn(); |
| 17 | ~ARM_Unicorn(); | 17 | ~ARM_Unicorn(); |
| 18 | void MapBackingMemory(VAddr address, size_t size, u8* memory, | 18 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 19 | Kernel::VMAPermission perms) override; | 19 | Kernel::VMAPermission perms) override; |
| 20 | void UnmapMemory(VAddr address, size_t size) override; | 20 | void UnmapMemory(VAddr address, std::size_t size) override; |
| 21 | void SetPC(u64 pc) override; | 21 | void SetPC(u64 pc) override; |
| 22 | u64 GetPC() const override; | 22 | u64 GetPC() const override; |
| 23 | u64 GetReg(int index) const override; | 23 | u64 GetReg(int index) const override; |
| 24 | void SetReg(int index, u64 value) override; | 24 | void SetReg(int index, u64 value) override; |
| 25 | u128 GetExtReg(int index) const override; | 25 | u128 GetVectorReg(int index) const override; |
| 26 | void SetExtReg(int index, u128 value) override; | 26 | void SetVectorReg(int index, u128 value) override; |
| 27 | u32 GetVFPReg(int index) const override; | 27 | u32 GetPSTATE() const override; |
| 28 | void SetVFPReg(int index, u32 value) override; | 28 | void SetPSTATE(u32 pstate) override; |
| 29 | u32 GetCPSR() const override; | ||
| 30 | void SetCPSR(u32 cpsr) override; | ||
| 31 | VAddr GetTlsAddress() const override; | 29 | VAddr GetTlsAddress() const override; |
| 32 | void SetTlsAddress(VAddr address) override; | 30 | void SetTlsAddress(VAddr address) override; |
| 33 | void SetTPIDR_EL0(u64 value) override; | 31 | void SetTPIDR_EL0(u64 value) override; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 2cfae18df..50f0a42fb 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -14,6 +14,9 @@ | |||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_cpu.h" | 15 | #include "core/core_cpu.h" |
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/file_sys/mode.h" | ||
| 18 | #include "core/file_sys/vfs_concat.h" | ||
| 19 | #include "core/file_sys/vfs_real.h" | ||
| 17 | #include "core/gdbstub/gdbstub.h" | 20 | #include "core/gdbstub/gdbstub.h" |
| 18 | #include "core/hle/kernel/client_port.h" | 21 | #include "core/hle/kernel/client_port.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 22 | #include "core/hle/kernel/kernel.h" |
| @@ -21,14 +24,11 @@ | |||
| 21 | #include "core/hle/kernel/scheduler.h" | 24 | #include "core/hle/kernel/scheduler.h" |
| 22 | #include "core/hle/kernel/thread.h" | 25 | #include "core/hle/kernel/thread.h" |
| 23 | #include "core/hle/service/service.h" | 26 | #include "core/hle/service/service.h" |
| 24 | #include "core/hle/service/sm/controller.h" | ||
| 25 | #include "core/hle/service/sm/sm.h" | 27 | #include "core/hle/service/sm/sm.h" |
| 26 | #include "core/loader/loader.h" | 28 | #include "core/loader/loader.h" |
| 27 | #include "core/perf_stats.h" | 29 | #include "core/perf_stats.h" |
| 28 | #include "core/settings.h" | 30 | #include "core/settings.h" |
| 29 | #include "core/telemetry_session.h" | 31 | #include "core/telemetry_session.h" |
| 30 | #include "file_sys/vfs_concat.h" | ||
| 31 | #include "file_sys/vfs_real.h" | ||
| 32 | #include "video_core/debug_utils/debug_utils.h" | 32 | #include "video_core/debug_utils/debug_utils.h" |
| 33 | #include "video_core/gpu.h" | 33 | #include "video_core/gpu.h" |
| 34 | #include "video_core/renderer_base.h" | 34 | #include "video_core/renderer_base.h" |
| @@ -136,11 +136,11 @@ struct System::Impl { | |||
| 136 | if (virtual_filesystem == nullptr) | 136 | if (virtual_filesystem == nullptr) |
| 137 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); | 137 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| 138 | 138 | ||
| 139 | current_process = Kernel::Process::Create(kernel, "main"); | 139 | kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main")); |
| 140 | 140 | ||
| 141 | cpu_barrier = std::make_shared<CpuBarrier>(); | 141 | cpu_barrier = std::make_shared<CpuBarrier>(); |
| 142 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); | 142 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); |
| 143 | for (size_t index = 0; index < cpu_cores.size(); ++index) { | 143 | for (std::size_t index = 0; index < cpu_cores.size(); ++index) { |
| 144 | cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); | 144 | cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| @@ -161,7 +161,7 @@ struct System::Impl { | |||
| 161 | // CPU core 0 is run on the main thread | 161 | // CPU core 0 is run on the main thread |
| 162 | thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; | 162 | thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; |
| 163 | if (Settings::values.use_multi_core) { | 163 | if (Settings::values.use_multi_core) { |
| 164 | for (size_t index = 0; index < cpu_core_threads.size(); ++index) { | 164 | for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { |
| 165 | cpu_core_threads[index] = | 165 | cpu_core_threads[index] = |
| 166 | std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); | 166 | std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); |
| 167 | thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; | 167 | thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; |
| @@ -202,7 +202,7 @@ struct System::Impl { | |||
| 202 | return init_result; | 202 | return init_result; |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | const Loader::ResultStatus load_result{app_loader->Load(current_process)}; | 205 | const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())}; |
| 206 | if (load_result != Loader::ResultStatus::Success) { | 206 | if (load_result != Loader::ResultStatus::Success) { |
| 207 | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); | 207 | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); |
| 208 | Shutdown(); | 208 | Shutdown(); |
| @@ -281,12 +281,11 @@ struct System::Impl { | |||
| 281 | std::unique_ptr<VideoCore::RendererBase> renderer; | 281 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| 282 | std::unique_ptr<Tegra::GPU> gpu_core; | 282 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 283 | std::shared_ptr<Tegra::DebugContext> debug_context; | 283 | std::shared_ptr<Tegra::DebugContext> debug_context; |
| 284 | Kernel::SharedPtr<Kernel::Process> current_process; | ||
| 285 | std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; | 284 | std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; |
| 286 | std::shared_ptr<CpuBarrier> cpu_barrier; | 285 | std::shared_ptr<CpuBarrier> cpu_barrier; |
| 287 | std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; | 286 | std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; |
| 288 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; | 287 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; |
| 289 | size_t active_core{}; ///< Active core, only used in single thread mode | 288 | std::size_t active_core{}; ///< Active core, only used in single thread mode |
| 290 | 289 | ||
| 291 | /// Service manager | 290 | /// Service manager |
| 292 | std::shared_ptr<Service::SM::ServiceManager> service_manager; | 291 | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| @@ -349,7 +348,7 @@ ARM_Interface& System::CurrentArmInterface() { | |||
| 349 | return CurrentCpuCore().ArmInterface(); | 348 | return CurrentCpuCore().ArmInterface(); |
| 350 | } | 349 | } |
| 351 | 350 | ||
| 352 | size_t System::CurrentCoreIndex() { | 351 | std::size_t System::CurrentCoreIndex() { |
| 353 | return CurrentCpuCore().CoreIndex(); | 352 | return CurrentCpuCore().CoreIndex(); |
| 354 | } | 353 | } |
| 355 | 354 | ||
| @@ -357,21 +356,25 @@ Kernel::Scheduler& System::CurrentScheduler() { | |||
| 357 | return *CurrentCpuCore().Scheduler(); | 356 | return *CurrentCpuCore().Scheduler(); |
| 358 | } | 357 | } |
| 359 | 358 | ||
| 360 | const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { | 359 | const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { |
| 361 | ASSERT(core_index < NUM_CPU_CORES); | 360 | ASSERT(core_index < NUM_CPU_CORES); |
| 362 | return impl->cpu_cores[core_index]->Scheduler(); | 361 | return impl->cpu_cores[core_index]->Scheduler(); |
| 363 | } | 362 | } |
| 364 | 363 | ||
| 365 | Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { | 364 | Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { |
| 366 | return impl->current_process; | 365 | return impl->kernel.CurrentProcess(); |
| 367 | } | 366 | } |
| 368 | 367 | ||
| 369 | ARM_Interface& System::ArmInterface(size_t core_index) { | 368 | const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const { |
| 369 | return impl->kernel.CurrentProcess(); | ||
| 370 | } | ||
| 371 | |||
| 372 | ARM_Interface& System::ArmInterface(std::size_t core_index) { | ||
| 370 | ASSERT(core_index < NUM_CPU_CORES); | 373 | ASSERT(core_index < NUM_CPU_CORES); |
| 371 | return impl->cpu_cores[core_index]->ArmInterface(); | 374 | return impl->cpu_cores[core_index]->ArmInterface(); |
| 372 | } | 375 | } |
| 373 | 376 | ||
| 374 | Cpu& System::CpuCore(size_t core_index) { | 377 | Cpu& System::CpuCore(std::size_t core_index) { |
| 375 | ASSERT(core_index < NUM_CPU_CORES); | 378 | ASSERT(core_index < NUM_CPU_CORES); |
| 376 | return *impl->cpu_cores[core_index]; | 379 | return *impl->cpu_cores[core_index]; |
| 377 | } | 380 | } |
| @@ -440,8 +443,8 @@ void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { | |||
| 440 | impl->debug_context = std::move(context); | 443 | impl->debug_context = std::move(context); |
| 441 | } | 444 | } |
| 442 | 445 | ||
| 443 | std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const { | 446 | Tegra::DebugContext* System::GetGPUDebugContext() const { |
| 444 | return impl->debug_context; | 447 | return impl->debug_context.get(); |
| 445 | } | 448 | } |
| 446 | 449 | ||
| 447 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { | 450 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { |
diff --git a/src/core/core.h b/src/core/core.h index eee1fecc1..f9a3e97e3 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -145,16 +145,16 @@ public: | |||
| 145 | ARM_Interface& CurrentArmInterface(); | 145 | ARM_Interface& CurrentArmInterface(); |
| 146 | 146 | ||
| 147 | /// Gets the index of the currently running CPU core | 147 | /// Gets the index of the currently running CPU core |
| 148 | size_t CurrentCoreIndex(); | 148 | std::size_t CurrentCoreIndex(); |
| 149 | 149 | ||
| 150 | /// Gets the scheduler for the CPU core that is currently running | 150 | /// Gets the scheduler for the CPU core that is currently running |
| 151 | Kernel::Scheduler& CurrentScheduler(); | 151 | Kernel::Scheduler& CurrentScheduler(); |
| 152 | 152 | ||
| 153 | /// Gets an ARM interface to the CPU core with the specified index | 153 | /// Gets an ARM interface to the CPU core with the specified index |
| 154 | ARM_Interface& ArmInterface(size_t core_index); | 154 | ARM_Interface& ArmInterface(std::size_t core_index); |
| 155 | 155 | ||
| 156 | /// Gets a CPU interface to the CPU core with the specified index | 156 | /// Gets a CPU interface to the CPU core with the specified index |
| 157 | Cpu& CpuCore(size_t core_index); | 157 | Cpu& CpuCore(std::size_t core_index); |
| 158 | 158 | ||
| 159 | /// Gets the exclusive monitor | 159 | /// Gets the exclusive monitor |
| 160 | ExclusiveMonitor& Monitor(); | 160 | ExclusiveMonitor& Monitor(); |
| @@ -172,11 +172,14 @@ public: | |||
| 172 | const VideoCore::RendererBase& Renderer() const; | 172 | const VideoCore::RendererBase& Renderer() const; |
| 173 | 173 | ||
| 174 | /// Gets the scheduler for the CPU core with the specified index | 174 | /// Gets the scheduler for the CPU core with the specified index |
| 175 | const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index); | 175 | const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); |
| 176 | 176 | ||
| 177 | /// Gets the current process | 177 | /// Provides a reference to the current process |
| 178 | Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); | 178 | Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); |
| 179 | 179 | ||
| 180 | /// Provides a constant reference to the current process. | ||
| 181 | const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const; | ||
| 182 | |||
| 180 | /// Provides a reference to the kernel instance. | 183 | /// Provides a reference to the kernel instance. |
| 181 | Kernel::KernelCore& Kernel(); | 184 | Kernel::KernelCore& Kernel(); |
| 182 | 185 | ||
| @@ -209,7 +212,7 @@ public: | |||
| 209 | 212 | ||
| 210 | void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context); | 213 | void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context); |
| 211 | 214 | ||
| 212 | std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const; | 215 | Tegra::DebugContext* GetGPUDebugContext() const; |
| 213 | 216 | ||
| 214 | void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); | 217 | void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); |
| 215 | 218 | ||
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index b042ee02b..21568ad50 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #ifdef ARCHITECTURE_x86_64 | 9 | #ifdef ARCHITECTURE_x86_64 |
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | 10 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 11 | #endif | 11 | #endif |
| 12 | #include "core/arm/exclusive_monitor.h" | ||
| 12 | #include "core/arm/unicorn/arm_unicorn.h" | 13 | #include "core/arm/unicorn/arm_unicorn.h" |
| 13 | #include "core/core_cpu.h" | 14 | #include "core/core_cpu.h" |
| 14 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| @@ -49,7 +50,7 @@ bool CpuBarrier::Rendezvous() { | |||
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | 52 | Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, |
| 52 | std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index) | 53 | std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) |
| 53 | : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { | 54 | : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { |
| 54 | 55 | ||
| 55 | if (Settings::values.use_cpu_jit) { | 56 | if (Settings::values.use_cpu_jit) { |
| @@ -66,7 +67,9 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | |||
| 66 | scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); | 67 | scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) { | 70 | Cpu::~Cpu() = default; |
| 71 | |||
| 72 | std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { | ||
| 70 | if (Settings::values.use_cpu_jit) { | 73 | if (Settings::values.use_cpu_jit) { |
| 71 | #ifdef ARCHITECTURE_x86_64 | 74 | #ifdef ARCHITECTURE_x86_64 |
| 72 | return std::make_shared<DynarmicExclusiveMonitor>(num_cores); | 75 | return std::make_shared<DynarmicExclusiveMonitor>(num_cores); |
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 40ed34b47..685532965 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h | |||
| @@ -6,11 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <cstddef> | ||
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| 11 | #include <string> | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "core/arm/exclusive_monitor.h" | ||
| 14 | 13 | ||
| 15 | namespace Kernel { | 14 | namespace Kernel { |
| 16 | class Scheduler; | 15 | class Scheduler; |
| @@ -19,6 +18,7 @@ class Scheduler; | |||
| 19 | namespace Core { | 18 | namespace Core { |
| 20 | 19 | ||
| 21 | class ARM_Interface; | 20 | class ARM_Interface; |
| 21 | class ExclusiveMonitor; | ||
| 22 | 22 | ||
| 23 | constexpr unsigned NUM_CPU_CORES{4}; | 23 | constexpr unsigned NUM_CPU_CORES{4}; |
| 24 | 24 | ||
| @@ -42,7 +42,8 @@ private: | |||
| 42 | class Cpu { | 42 | class Cpu { |
| 43 | public: | 43 | public: |
| 44 | Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | 44 | Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, |
| 45 | std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index); | 45 | std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index); |
| 46 | ~Cpu(); | ||
| 46 | 47 | ||
| 47 | void RunLoop(bool tight_loop = true); | 48 | void RunLoop(bool tight_loop = true); |
| 48 | 49 | ||
| @@ -66,11 +67,11 @@ public: | |||
| 66 | return core_index == 0; | 67 | return core_index == 0; |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | size_t CoreIndex() const { | 70 | std::size_t CoreIndex() const { |
| 70 | return core_index; | 71 | return core_index; |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores); | 74 | static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); |
| 74 | 75 | ||
| 75 | private: | 76 | private: |
| 76 | void Reschedule(); | 77 | void Reschedule(); |
| @@ -80,7 +81,7 @@ private: | |||
| 80 | std::shared_ptr<Kernel::Scheduler> scheduler; | 81 | std::shared_ptr<Kernel::Scheduler> scheduler; |
| 81 | 82 | ||
| 82 | std::atomic<bool> reschedule_pending = false; | 83 | std::atomic<bool> reschedule_pending = false; |
| 83 | size_t core_index; | 84 | std::size_t core_index; |
| 84 | }; | 85 | }; |
| 85 | 86 | ||
| 86 | } // namespace Core | 87 | } // namespace Core |
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index 72e4bed67..4be76bb43 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp | |||
| @@ -10,9 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | namespace Core::Crypto { | 11 | namespace Core::Crypto { |
| 12 | namespace { | 12 | namespace { |
| 13 | std::vector<u8> CalculateNintendoTweak(size_t sector_id) { | 13 | std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) { |
| 14 | std::vector<u8> out(0x10); | 14 | std::vector<u8> out(0x10); |
| 15 | for (size_t i = 0xF; i <= 0xF; --i) { | 15 | for (std::size_t i = 0xF; i <= 0xF; --i) { |
| 16 | out[i] = sector_id & 0xFF; | 16 | out[i] = sector_id & 0xFF; |
| 17 | sector_id >>= 8; | 17 | sector_id >>= 8; |
| 18 | } | 18 | } |
| @@ -20,11 +20,14 @@ std::vector<u8> CalculateNintendoTweak(size_t sector_id) { | |||
| 20 | } | 20 | } |
| 21 | } // Anonymous namespace | 21 | } // Anonymous namespace |
| 22 | 22 | ||
| 23 | static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), | 23 | static_assert(static_cast<std::size_t>(Mode::CTR) == |
| 24 | static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_CTR), | ||
| 24 | "CTR has incorrect value."); | 25 | "CTR has incorrect value."); |
| 25 | static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), | 26 | static_assert(static_cast<std::size_t>(Mode::ECB) == |
| 27 | static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_ECB), | ||
| 26 | "ECB has incorrect value."); | 28 | "ECB has incorrect value."); |
| 27 | static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), | 29 | static_assert(static_cast<std::size_t>(Mode::XTS) == |
| 30 | static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_XTS), | ||
| 28 | "XTS has incorrect value."); | 31 | "XTS has incorrect value."); |
| 29 | 32 | ||
| 30 | // Structure to hide mbedtls types from header file | 33 | // Structure to hide mbedtls types from header file |
| @@ -33,7 +36,7 @@ struct CipherContext { | |||
| 33 | mbedtls_cipher_context_t decryption_context; | 36 | mbedtls_cipher_context_t decryption_context; |
| 34 | }; | 37 | }; |
| 35 | 38 | ||
| 36 | template <typename Key, size_t KeySize> | 39 | template <typename Key, std::size_t KeySize> |
| 37 | Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) | 40 | Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) |
| 38 | : ctx(std::make_unique<CipherContext>()) { | 41 | : ctx(std::make_unique<CipherContext>()) { |
| 39 | mbedtls_cipher_init(&ctx->encryption_context); | 42 | mbedtls_cipher_init(&ctx->encryption_context); |
| @@ -54,26 +57,26 @@ Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) | |||
| 54 | //"Failed to set key on mbedtls ciphers."); | 57 | //"Failed to set key on mbedtls ciphers."); |
| 55 | } | 58 | } |
| 56 | 59 | ||
| 57 | template <typename Key, size_t KeySize> | 60 | template <typename Key, std::size_t KeySize> |
| 58 | AESCipher<Key, KeySize>::~AESCipher() { | 61 | AESCipher<Key, KeySize>::~AESCipher() { |
| 59 | mbedtls_cipher_free(&ctx->encryption_context); | 62 | mbedtls_cipher_free(&ctx->encryption_context); |
| 60 | mbedtls_cipher_free(&ctx->decryption_context); | 63 | mbedtls_cipher_free(&ctx->decryption_context); |
| 61 | } | 64 | } |
| 62 | 65 | ||
| 63 | template <typename Key, size_t KeySize> | 66 | template <typename Key, std::size_t KeySize> |
| 64 | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { | 67 | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { |
| 65 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || | 68 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || |
| 66 | mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, | 69 | mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, |
| 67 | "Failed to set IV on mbedtls ciphers."); | 70 | "Failed to set IV on mbedtls ciphers."); |
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | template <typename Key, size_t KeySize> | 73 | template <typename Key, std::size_t KeySize> |
| 71 | void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const { | 74 | void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { |
| 72 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; | 75 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; |
| 73 | 76 | ||
| 74 | mbedtls_cipher_reset(context); | 77 | mbedtls_cipher_reset(context); |
| 75 | 78 | ||
| 76 | size_t written = 0; | 79 | std::size_t written = 0; |
| 77 | if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { | 80 | if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { |
| 78 | mbedtls_cipher_update(context, src, size, dest, &written); | 81 | mbedtls_cipher_update(context, src, size, dest, &written); |
| 79 | if (written != size) { | 82 | if (written != size) { |
| @@ -82,11 +85,25 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op | |||
| 82 | } | 85 | } |
| 83 | } else { | 86 | } else { |
| 84 | const auto block_size = mbedtls_cipher_get_block_size(context); | 87 | const auto block_size = mbedtls_cipher_get_block_size(context); |
| 88 | if (size < block_size) { | ||
| 89 | std::vector<u8> block(block_size); | ||
| 90 | std::memcpy(block.data(), src, size); | ||
| 91 | Transcode(block.data(), block.size(), block.data(), op); | ||
| 92 | std::memcpy(dest, block.data(), size); | ||
| 93 | return; | ||
| 94 | } | ||
| 85 | 95 | ||
| 86 | for (size_t offset = 0; offset < size; offset += block_size) { | 96 | for (std::size_t offset = 0; offset < size; offset += block_size) { |
| 87 | auto length = std::min<size_t>(block_size, size - offset); | 97 | auto length = std::min<std::size_t>(block_size, size - offset); |
| 88 | mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); | 98 | mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); |
| 89 | if (written != length) { | 99 | if (written != length) { |
| 100 | if (length < block_size) { | ||
| 101 | std::vector<u8> block(block_size); | ||
| 102 | std::memcpy(block.data(), src + offset, length); | ||
| 103 | Transcode(block.data(), block.size(), block.data(), op); | ||
| 104 | std::memcpy(dest + offset, block.data(), length); | ||
| 105 | return; | ||
| 106 | } | ||
| 90 | LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", | 107 | LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", |
| 91 | length, written); | 108 | length, written); |
| 92 | } | 109 | } |
| @@ -96,12 +113,12 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op | |||
| 96 | mbedtls_cipher_finish(context, nullptr, nullptr); | 113 | mbedtls_cipher_finish(context, nullptr, nullptr); |
| 97 | } | 114 | } |
| 98 | 115 | ||
| 99 | template <typename Key, size_t KeySize> | 116 | template <typename Key, std::size_t KeySize> |
| 100 | void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, | 117 | void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* dest, |
| 101 | size_t sector_size, Op op) { | 118 | std::size_t sector_id, std::size_t sector_size, Op op) { |
| 102 | ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size."); | 119 | ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size."); |
| 103 | 120 | ||
| 104 | for (size_t i = 0; i < size; i += sector_size) { | 121 | for (std::size_t i = 0; i < size; i += sector_size) { |
| 105 | SetIV(CalculateNintendoTweak(sector_id++)); | 122 | SetIV(CalculateNintendoTweak(sector_id++)); |
| 106 | Transcode<u8, u8>(src + i, sector_size, dest + i, op); | 123 | Transcode<u8, u8>(src + i, sector_size, dest + i, op); |
| 107 | } | 124 | } |
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index 8ce9d6612..edc4ab910 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h | |||
| @@ -25,7 +25,7 @@ enum class Op { | |||
| 25 | Decrypt, | 25 | Decrypt, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | template <typename Key, size_t KeySize = sizeof(Key)> | 28 | template <typename Key, std::size_t KeySize = sizeof(Key)> |
| 29 | class AESCipher { | 29 | class AESCipher { |
| 30 | static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); | 30 | static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); |
| 31 | static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); | 31 | static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); |
| @@ -38,25 +38,25 @@ public: | |||
| 38 | void SetIV(std::vector<u8> iv); | 38 | void SetIV(std::vector<u8> iv); |
| 39 | 39 | ||
| 40 | template <typename Source, typename Dest> | 40 | template <typename Source, typename Dest> |
| 41 | void Transcode(const Source* src, size_t size, Dest* dest, Op op) const { | 41 | void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { |
| 42 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, | 42 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, |
| 43 | "Transcode source and destination types must be trivially copyable."); | 43 | "Transcode source and destination types must be trivially copyable."); |
| 44 | Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); | 44 | Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | void Transcode(const u8* src, size_t size, u8* dest, Op op) const; | 47 | void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const; |
| 48 | 48 | ||
| 49 | template <typename Source, typename Dest> | 49 | template <typename Source, typename Dest> |
| 50 | void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, | 50 | void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id, |
| 51 | size_t sector_size, Op op) { | 51 | std::size_t sector_size, Op op) { |
| 52 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, | 52 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, |
| 53 | "XTSTranscode source and destination types must be trivially copyable."); | 53 | "XTSTranscode source and destination types must be trivially copyable."); |
| 54 | XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, | 54 | XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, |
| 55 | sector_size, op); | 55 | sector_size, op); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, | 58 | void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id, |
| 59 | Op op); | 59 | std::size_t sector_size, Op op); |
| 60 | 60 | ||
| 61 | private: | 61 | private: |
| 62 | std::unique_ptr<CipherContext> ctx; | 62 | std::unique_ptr<CipherContext> ctx; |
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index 3ea60dbd0..902841c77 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | 8 | ||
| 9 | namespace Core::Crypto { | 9 | namespace Core::Crypto { |
| 10 | 10 | ||
| 11 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) | 11 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, |
| 12 | std::size_t base_offset) | ||
| 12 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), | 13 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), |
| 13 | iv(16, 0) {} | 14 | iv(16, 0) {} |
| 14 | 15 | ||
| 15 | size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | 16 | std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 16 | if (length == 0) | 17 | if (length == 0) |
| 17 | return 0; | 18 | return 0; |
| 18 | 19 | ||
| @@ -21,14 +22,14 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | |||
| 21 | UpdateIV(base_offset + offset); | 22 | UpdateIV(base_offset + offset); |
| 22 | std::vector<u8> raw = base->ReadBytes(length, offset); | 23 | std::vector<u8> raw = base->ReadBytes(length, offset); |
| 23 | cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt); | 24 | cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt); |
| 24 | return raw.size(); | 25 | return length; |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | // offset does not fall on block boundary (0x10) | 28 | // offset does not fall on block boundary (0x10) |
| 28 | std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); | 29 | std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); |
| 29 | UpdateIV(base_offset + offset - sector_offset); | 30 | UpdateIV(base_offset + offset - sector_offset); |
| 30 | cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); | 31 | cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); |
| 31 | size_t read = 0x10 - sector_offset; | 32 | std::size_t read = 0x10 - sector_offset; |
| 32 | 33 | ||
| 33 | if (length + sector_offset < 0x10) { | 34 | if (length + sector_offset < 0x10) { |
| 34 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); | 35 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); |
| @@ -43,9 +44,9 @@ void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { | |||
| 43 | iv.assign(iv_.cbegin(), iv_.cbegin() + length); | 44 | iv.assign(iv_.cbegin(), iv_.cbegin() + length); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | void CTREncryptionLayer::UpdateIV(size_t offset) const { | 47 | void CTREncryptionLayer::UpdateIV(std::size_t offset) const { |
| 47 | offset >>= 4; | 48 | offset >>= 4; |
| 48 | for (size_t i = 0; i < 8; ++i) { | 49 | for (std::size_t i = 0; i < 8; ++i) { |
| 49 | iv[16 - i - 1] = offset & 0xFF; | 50 | iv[16 - i - 1] = offset & 0xFF; |
| 50 | offset >>= 8; | 51 | offset >>= 8; |
| 51 | } | 52 | } |
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h index 11b8683c7..a7bf810f4 100644 --- a/src/core/crypto/ctr_encryption_layer.h +++ b/src/core/crypto/ctr_encryption_layer.h | |||
| @@ -14,20 +14,20 @@ namespace Core::Crypto { | |||
| 14 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. | 14 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. |
| 15 | class CTREncryptionLayer : public EncryptionLayer { | 15 | class CTREncryptionLayer : public EncryptionLayer { |
| 16 | public: | 16 | public: |
| 17 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); | 17 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); |
| 18 | 18 | ||
| 19 | size_t Read(u8* data, size_t length, size_t offset) const override; | 19 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 20 | 20 | ||
| 21 | void SetIV(const std::vector<u8>& iv); | 21 | void SetIV(const std::vector<u8>& iv); |
| 22 | 22 | ||
| 23 | private: | 23 | private: |
| 24 | size_t base_offset; | 24 | std::size_t base_offset; |
| 25 | 25 | ||
| 26 | // Must be mutable as operations modify cipher contexts. | 26 | // Must be mutable as operations modify cipher contexts. |
| 27 | mutable AESCipher<Key128> cipher; | 27 | mutable AESCipher<Key128> cipher; |
| 28 | mutable std::vector<u8> iv; | 28 | mutable std::vector<u8> iv; |
| 29 | 29 | ||
| 30 | void UpdateIV(size_t offset) const; | 30 | void UpdateIV(std::size_t offset) const; |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | } // namespace Core::Crypto | 33 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp index 4204527e3..4c377d7d4 100644 --- a/src/core/crypto/encryption_layer.cpp +++ b/src/core/crypto/encryption_layer.cpp | |||
| @@ -12,11 +12,11 @@ std::string EncryptionLayer::GetName() const { | |||
| 12 | return base->GetName(); | 12 | return base->GetName(); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | size_t EncryptionLayer::GetSize() const { | 15 | std::size_t EncryptionLayer::GetSize() const { |
| 16 | return base->GetSize(); | 16 | return base->GetSize(); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | bool EncryptionLayer::Resize(size_t new_size) { | 19 | bool EncryptionLayer::Resize(std::size_t new_size) { |
| 20 | return false; | 20 | return false; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| @@ -32,7 +32,7 @@ bool EncryptionLayer::IsReadable() const { | |||
| 32 | return true; | 32 | return true; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { | 35 | std::size_t EncryptionLayer::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 36 | return 0; | 36 | return 0; |
| 37 | } | 37 | } |
| 38 | 38 | ||
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h index 7f05af9b4..53619cb38 100644 --- a/src/core/crypto/encryption_layer.h +++ b/src/core/crypto/encryption_layer.h | |||
| @@ -15,15 +15,15 @@ class EncryptionLayer : public FileSys::VfsFile { | |||
| 15 | public: | 15 | public: |
| 16 | explicit EncryptionLayer(FileSys::VirtualFile base); | 16 | explicit EncryptionLayer(FileSys::VirtualFile base); |
| 17 | 17 | ||
| 18 | size_t Read(u8* data, size_t length, size_t offset) const override = 0; | 18 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0; |
| 19 | 19 | ||
| 20 | std::string GetName() const override; | 20 | std::string GetName() const override; |
| 21 | size_t GetSize() const override; | 21 | std::size_t GetSize() const override; |
| 22 | bool Resize(size_t new_size) override; | 22 | bool Resize(std::size_t new_size) override; |
| 23 | std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; | 23 | std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; |
| 24 | bool IsWritable() const override; | 24 | bool IsWritable() const override; |
| 25 | bool IsReadable() const override; | 25 | bool IsReadable() const override; |
| 26 | size_t Write(const u8* data, size_t length, size_t offset) override; | 26 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 27 | bool Rename(std::string_view name) override; | 27 | bool Rename(std::string_view name) override; |
| 28 | 28 | ||
| 29 | protected: | 29 | protected: |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 0b6c07de8..bf3a70944 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -8,12 +8,15 @@ | |||
| 8 | #include <locale> | 8 | #include <locale> |
| 9 | #include <sstream> | 9 | #include <sstream> |
| 10 | #include <string_view> | 10 | #include <string_view> |
| 11 | #include <tuple> | ||
| 12 | #include <vector> | ||
| 11 | #include "common/common_paths.h" | 13 | #include "common/common_paths.h" |
| 12 | #include "common/file_util.h" | 14 | #include "common/file_util.h" |
| 13 | #include "common/hex_util.h" | 15 | #include "common/hex_util.h" |
| 14 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 15 | #include "core/crypto/aes_util.h" | 17 | #include "core/crypto/aes_util.h" |
| 16 | #include "core/crypto/key_manager.h" | 18 | #include "core/crypto/key_manager.h" |
| 19 | #include "core/loader/loader.h" | ||
| 17 | #include "core/settings.h" | 20 | #include "core/settings.h" |
| 18 | 21 | ||
| 19 | namespace Core::Crypto { | 22 | namespace Core::Crypto { |
| @@ -51,7 +54,7 @@ boost::optional<Key128> DeriveSDSeed() { | |||
| 51 | return boost::none; | 54 | return boost::none; |
| 52 | 55 | ||
| 53 | std::array<u8, 0x10> buffer{}; | 56 | std::array<u8, 0x10> buffer{}; |
| 54 | size_t offset = 0; | 57 | std::size_t offset = 0; |
| 55 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { | 58 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { |
| 56 | save_43.Seek(offset, SEEK_SET); | 59 | save_43.Seek(offset, SEEK_SET); |
| 57 | save_43.ReadBytes(buffer.data(), buffer.size()); | 60 | save_43.ReadBytes(buffer.data(), buffer.size()); |
| @@ -102,7 +105,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag | |||
| 102 | 105 | ||
| 103 | // Combine sources and seed | 106 | // Combine sources and seed |
| 104 | for (auto& source : sd_key_sources) { | 107 | for (auto& source : sd_key_sources) { |
| 105 | for (size_t i = 0; i < source.size(); ++i) | 108 | for (std::size_t i = 0; i < source.size(); ++i) |
| 106 | source[i] ^= sd_seed[i & 0xF]; | 109 | source[i] ^= sd_seed[i & 0xF]; |
| 107 | } | 110 | } |
| 108 | 111 | ||
| @@ -204,7 +207,7 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { | |||
| 204 | return s256_keys.at({id, field1, field2}); | 207 | return s256_keys.at({id, field1, field2}); |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 207 | template <size_t Size> | 210 | template <std::size_t Size> |
| 208 | void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, | 211 | void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, |
| 209 | const std::array<u8, Size>& key) { | 212 | const std::array<u8, Size>& key) { |
| 210 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); | 213 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); |
| @@ -228,18 +231,28 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, | |||
| 228 | } | 231 | } |
| 229 | 232 | ||
| 230 | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | 233 | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { |
| 231 | const auto iter = std::find_if( | 234 | if (s128_keys.find({id, field1, field2}) != s128_keys.end()) |
| 235 | return; | ||
| 236 | if (id == S128KeyType::Titlekey) { | ||
| 237 | Key128 rights_id; | ||
| 238 | std::memcpy(rights_id.data(), &field2, sizeof(u64)); | ||
| 239 | std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64)); | ||
| 240 | WriteKeyToFile(true, Common::HexArrayToString(rights_id), key); | ||
| 241 | } | ||
| 242 | const auto iter2 = std::find_if( | ||
| 232 | s128_file_id.begin(), s128_file_id.end(), | 243 | s128_file_id.begin(), s128_file_id.end(), |
| 233 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { | 244 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { |
| 234 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == | 245 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == |
| 235 | std::tie(id, field1, field2); | 246 | std::tie(id, field1, field2); |
| 236 | }); | 247 | }); |
| 237 | if (iter != s128_file_id.end()) | 248 | if (iter2 != s128_file_id.end()) |
| 238 | WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key); | 249 | WriteKeyToFile(false, iter2->first, key); |
| 239 | s128_keys[{id, field1, field2}] = key; | 250 | s128_keys[{id, field1, field2}] = key; |
| 240 | } | 251 | } |
| 241 | 252 | ||
| 242 | void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { | 253 | void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { |
| 254 | if (s256_keys.find({id, field1, field2}) != s256_keys.end()) | ||
| 255 | return; | ||
| 243 | const auto iter = std::find_if( | 256 | const auto iter = std::find_if( |
| 244 | s256_file_id.begin(), s256_file_id.end(), | 257 | s256_file_id.begin(), s256_file_id.end(), |
| 245 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { | 258 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { |
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 7ca3e6cbc..978eec8dc 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -6,16 +6,19 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <string_view> | ||
| 10 | #include <type_traits> | ||
| 11 | #include <vector> | ||
| 12 | #include <boost/container/flat_map.hpp> | 9 | #include <boost/container/flat_map.hpp> |
| 10 | #include <boost/optional.hpp> | ||
| 13 | #include <fmt/format.h> | 11 | #include <fmt/format.h> |
| 14 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 15 | #include "core/loader/loader.h" | 13 | |
| 14 | namespace Loader { | ||
| 15 | enum class ResultStatus : u16; | ||
| 16 | } | ||
| 16 | 17 | ||
| 17 | namespace Core::Crypto { | 18 | namespace Core::Crypto { |
| 18 | 19 | ||
| 20 | constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180; | ||
| 21 | |||
| 19 | using Key128 = std::array<u8, 0x10>; | 22 | using Key128 = std::array<u8, 0x10>; |
| 20 | using Key256 = std::array<u8, 0x20>; | 23 | using Key256 = std::array<u8, 0x20>; |
| 21 | using SHA256Hash = std::array<u8, 0x20>; | 24 | using SHA256Hash = std::array<u8, 0x20>; |
| @@ -105,7 +108,7 @@ private: | |||
| 105 | void LoadFromFile(const std::string& filename, bool is_title_keys); | 108 | void LoadFromFile(const std::string& filename, bool is_title_keys); |
| 106 | void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, | 109 | void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, |
| 107 | const std::string& filename, bool title); | 110 | const std::string& filename, bool title); |
| 108 | template <size_t Size> | 111 | template <std::size_t Size> |
| 109 | void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); | 112 | void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); |
| 110 | 113 | ||
| 111 | static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; | 114 | static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; |
diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp index c10832cfe..8f0ba4ee7 100644 --- a/src/core/crypto/xts_encryption_layer.cpp +++ b/src/core/crypto/xts_encryption_layer.cpp | |||
| @@ -14,7 +14,7 @@ constexpr u64 XTS_SECTOR_SIZE = 0x4000; | |||
| 14 | XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) | 14 | XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) |
| 15 | : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} | 15 | : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} |
| 16 | 16 | ||
| 17 | size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | 17 | std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 18 | if (length == 0) | 18 | if (length == 0) |
| 19 | return 0; | 19 | return 0; |
| 20 | 20 | ||
| @@ -46,7 +46,7 @@ size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | |||
| 46 | block.resize(XTS_SECTOR_SIZE); | 46 | block.resize(XTS_SECTOR_SIZE); |
| 47 | cipher.XTSTranscode(block.data(), block.size(), block.data(), | 47 | cipher.XTSTranscode(block.data(), block.size(), block.data(), |
| 48 | (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); | 48 | (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); |
| 49 | const size_t read = XTS_SECTOR_SIZE - sector_offset; | 49 | const std::size_t read = XTS_SECTOR_SIZE - sector_offset; |
| 50 | 50 | ||
| 51 | if (length + sector_offset < XTS_SECTOR_SIZE) { | 51 | if (length + sector_offset < XTS_SECTOR_SIZE) { |
| 52 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); | 52 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); |
diff --git a/src/core/crypto/xts_encryption_layer.h b/src/core/crypto/xts_encryption_layer.h index 7a1f1dc64..5f8f00fe7 100644 --- a/src/core/crypto/xts_encryption_layer.h +++ b/src/core/crypto/xts_encryption_layer.h | |||
| @@ -15,7 +15,7 @@ class XTSEncryptionLayer : public EncryptionLayer { | |||
| 15 | public: | 15 | public: |
| 16 | XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key); | 16 | XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key); |
| 17 | 17 | ||
| 18 | size_t Read(u8* data, size_t length, size_t offset) const override; | 18 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 19 | 19 | ||
| 20 | private: | 20 | private: |
| 21 | // Must be mutable as operations modify cipher contexts. | 21 | // Must be mutable as operations modify cipher contexts. |
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 08a7cea5a..205492897 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/file_sys/bis_factory.h" | 5 | #include "core/file_sys/bis_factory.h" |
| 6 | #include "core/file_sys/registered_cache.h" | ||
| 6 | 7 | ||
| 7 | namespace FileSys { | 8 | namespace FileSys { |
| 8 | 9 | ||
| @@ -13,6 +14,8 @@ BISFactory::BISFactory(VirtualDir nand_root_) | |||
| 13 | usrnand_cache(std::make_shared<RegisteredCache>( | 14 | usrnand_cache(std::make_shared<RegisteredCache>( |
| 14 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} | 15 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} |
| 15 | 16 | ||
| 17 | BISFactory::~BISFactory() = default; | ||
| 18 | |||
| 16 | std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { | 19 | std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { |
| 17 | return sysnand_cache; | 20 | return sysnand_cache; |
| 18 | } | 21 | } |
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index a970a5e2e..9523dd864 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h | |||
| @@ -5,17 +5,20 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include "core/loader/loader.h" | 8 | |
| 9 | #include "registered_cache.h" | 9 | #include "core/file_sys/vfs.h" |
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | class RegisteredCache; | ||
| 14 | |||
| 13 | /// File system interface to the Built-In Storage | 15 | /// File system interface to the Built-In Storage |
| 14 | /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND | 16 | /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND |
| 15 | /// registered caches. | 17 | /// registered caches. |
| 16 | class BISFactory { | 18 | class BISFactory { |
| 17 | public: | 19 | public: |
| 18 | explicit BISFactory(VirtualDir nand_root); | 20 | explicit BISFactory(VirtualDir nand_root); |
| 21 | ~BISFactory(); | ||
| 19 | 22 | ||
| 20 | std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; | 23 | std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; |
| 21 | std::shared_ptr<RegisteredCache> GetUserNANDContents() const; | 24 | std::shared_ptr<RegisteredCache> GetUserNANDContents() const; |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index d61a2ebe1..edfc1bbd4 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -9,7 +9,10 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/file_sys/card_image.h" | 11 | #include "core/file_sys/card_image.h" |
| 12 | #include "core/file_sys/content_archive.h" | ||
| 13 | #include "core/file_sys/nca_metadata.h" | ||
| 12 | #include "core/file_sys/partition_filesystem.h" | 14 | #include "core/file_sys/partition_filesystem.h" |
| 15 | #include "core/file_sys/submission_package.h" | ||
| 13 | #include "core/file_sys/vfs_offset.h" | 16 | #include "core/file_sys/vfs_offset.h" |
| 14 | #include "core/loader/loader.h" | 17 | #include "core/loader/loader.h" |
| 15 | 18 | ||
| @@ -38,20 +41,25 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | |||
| 38 | 41 | ||
| 39 | for (XCIPartition partition : | 42 | for (XCIPartition partition : |
| 40 | {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { | 43 | {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { |
| 41 | auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); | 44 | auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]); |
| 42 | if (raw != nullptr) | 45 | if (raw != nullptr) |
| 43 | partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); | 46 | partitions[static_cast<std::size_t>(partition)] = |
| 47 | std::make_shared<PartitionFilesystem>(raw); | ||
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; | 50 | secure_partition = std::make_shared<NSP>( |
| 51 | main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)])); | ||
| 47 | 52 | ||
| 48 | auto result = AddNCAFromPartition(XCIPartition::Secure); | 53 | const auto secure_ncas = secure_partition->GetNCAsCollapsed(); |
| 49 | if (result != Loader::ResultStatus::Success) { | 54 | std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas)); |
| 50 | status = result; | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | 55 | ||
| 54 | result = AddNCAFromPartition(XCIPartition::Update); | 56 | program = |
| 57 | secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); | ||
| 58 | program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID()); | ||
| 59 | if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) | ||
| 60 | program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; | ||
| 61 | |||
| 62 | auto result = AddNCAFromPartition(XCIPartition::Update); | ||
| 55 | if (result != Loader::ResultStatus::Success) { | 63 | if (result != Loader::ResultStatus::Success) { |
| 56 | status = result; | 64 | status = result; |
| 57 | return; | 65 | return; |
| @@ -74,6 +82,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | |||
| 74 | status = Loader::ResultStatus::Success; | 82 | status = Loader::ResultStatus::Success; |
| 75 | } | 83 | } |
| 76 | 84 | ||
| 85 | XCI::~XCI() = default; | ||
| 86 | |||
| 77 | Loader::ResultStatus XCI::GetStatus() const { | 87 | Loader::ResultStatus XCI::GetStatus() const { |
| 78 | return status; | 88 | return status; |
| 79 | } | 89 | } |
| @@ -83,7 +93,11 @@ Loader::ResultStatus XCI::GetProgramNCAStatus() const { | |||
| 83 | } | 93 | } |
| 84 | 94 | ||
| 85 | VirtualDir XCI::GetPartition(XCIPartition partition) const { | 95 | VirtualDir XCI::GetPartition(XCIPartition partition) const { |
| 86 | return partitions[static_cast<size_t>(partition)]; | 96 | return partitions[static_cast<std::size_t>(partition)]; |
| 97 | } | ||
| 98 | |||
| 99 | std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const { | ||
| 100 | return secure_partition; | ||
| 87 | } | 101 | } |
| 88 | 102 | ||
| 89 | VirtualDir XCI::GetSecurePartition() const { | 103 | VirtualDir XCI::GetSecurePartition() const { |
| @@ -102,6 +116,20 @@ VirtualDir XCI::GetLogoPartition() const { | |||
| 102 | return GetPartition(XCIPartition::Logo); | 116 | return GetPartition(XCIPartition::Logo); |
| 103 | } | 117 | } |
| 104 | 118 | ||
| 119 | u64 XCI::GetProgramTitleID() const { | ||
| 120 | return secure_partition->GetProgramTitleID(); | ||
| 121 | } | ||
| 122 | |||
| 123 | std::shared_ptr<NCA> XCI::GetProgramNCA() const { | ||
| 124 | return program; | ||
| 125 | } | ||
| 126 | |||
| 127 | VirtualFile XCI::GetProgramNCAFile() const { | ||
| 128 | if (GetProgramNCA() == nullptr) | ||
| 129 | return nullptr; | ||
| 130 | return GetProgramNCA()->GetBaseFile(); | ||
| 131 | } | ||
| 132 | |||
| 105 | const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { | 133 | const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { |
| 106 | return ncas; | 134 | return ncas; |
| 107 | } | 135 | } |
| @@ -141,11 +169,11 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | |||
| 141 | } | 169 | } |
| 142 | 170 | ||
| 143 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | 171 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { |
| 144 | if (partitions[static_cast<size_t>(part)] == nullptr) { | 172 | if (partitions[static_cast<std::size_t>(part)] == nullptr) { |
| 145 | return Loader::ResultStatus::ErrorXCIMissingPartition; | 173 | return Loader::ResultStatus::ErrorXCIMissingPartition; |
| 146 | } | 174 | } |
| 147 | 175 | ||
| 148 | for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { | 176 | for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) { |
| 149 | if (file->GetExtension() != "nca") | 177 | if (file->GetExtension() != "nca") |
| 150 | continue; | 178 | continue; |
| 151 | auto nca = std::make_shared<NCA>(file); | 179 | auto nca = std::make_shared<NCA>(file); |
| @@ -160,7 +188,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | |||
| 160 | } else { | 188 | } else { |
| 161 | const u16 error_id = static_cast<u16>(nca->GetStatus()); | 189 | const u16 error_id = static_cast<u16>(nca->GetStatus()); |
| 162 | LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", | 190 | LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", |
| 163 | partition_names[static_cast<size_t>(part)], nca->GetName(), error_id, | 191 | partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id, |
| 164 | nca->GetStatus()); | 192 | nca->GetStatus()); |
| 165 | } | 193 | } |
| 166 | } | 194 | } |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 54ab828d1..ce514dfa0 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -5,15 +5,22 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 11 | #include "core/file_sys/content_archive.h" | ||
| 12 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 13 | #include "core/loader/loader.h" | 13 | |
| 14 | namespace Loader { | ||
| 15 | enum class ResultStatus : u16; | ||
| 16 | } | ||
| 14 | 17 | ||
| 15 | namespace FileSys { | 18 | namespace FileSys { |
| 16 | 19 | ||
| 20 | class NCA; | ||
| 21 | enum class NCAContentType : u8; | ||
| 22 | class NSP; | ||
| 23 | |||
| 17 | enum class GamecardSize : u8 { | 24 | enum class GamecardSize : u8 { |
| 18 | S_1GB = 0xFA, | 25 | S_1GB = 0xFA, |
| 19 | S_2GB = 0xF8, | 26 | S_2GB = 0xF8, |
| @@ -57,6 +64,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo }; | |||
| 57 | class XCI : public ReadOnlyVfsDirectory { | 64 | class XCI : public ReadOnlyVfsDirectory { |
| 58 | public: | 65 | public: |
| 59 | explicit XCI(VirtualFile file); | 66 | explicit XCI(VirtualFile file); |
| 67 | ~XCI() override; | ||
| 60 | 68 | ||
| 61 | Loader::ResultStatus GetStatus() const; | 69 | Loader::ResultStatus GetStatus() const; |
| 62 | Loader::ResultStatus GetProgramNCAStatus() const; | 70 | Loader::ResultStatus GetProgramNCAStatus() const; |
| @@ -64,11 +72,16 @@ public: | |||
| 64 | u8 GetFormatVersion() const; | 72 | u8 GetFormatVersion() const; |
| 65 | 73 | ||
| 66 | VirtualDir GetPartition(XCIPartition partition) const; | 74 | VirtualDir GetPartition(XCIPartition partition) const; |
| 75 | std::shared_ptr<NSP> GetSecurePartitionNSP() const; | ||
| 67 | VirtualDir GetSecurePartition() const; | 76 | VirtualDir GetSecurePartition() const; |
| 68 | VirtualDir GetNormalPartition() const; | 77 | VirtualDir GetNormalPartition() const; |
| 69 | VirtualDir GetUpdatePartition() const; | 78 | VirtualDir GetUpdatePartition() const; |
| 70 | VirtualDir GetLogoPartition() const; | 79 | VirtualDir GetLogoPartition() const; |
| 71 | 80 | ||
| 81 | u64 GetProgramTitleID() const; | ||
| 82 | |||
| 83 | std::shared_ptr<NCA> GetProgramNCA() const; | ||
| 84 | VirtualFile GetProgramNCAFile() const; | ||
| 72 | const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; | 85 | const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; |
| 73 | std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; | 86 | std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; |
| 74 | VirtualFile GetNCAFileByType(NCAContentType type) const; | 87 | VirtualFile GetNCAFileByType(NCAContentType type) const; |
| @@ -94,6 +107,8 @@ private: | |||
| 94 | Loader::ResultStatus program_nca_status; | 107 | Loader::ResultStatus program_nca_status; |
| 95 | 108 | ||
| 96 | std::vector<VirtualDir> partitions; | 109 | std::vector<VirtualDir> partitions; |
| 110 | std::shared_ptr<NSP> secure_partition; | ||
| 111 | std::shared_ptr<NCA> program; | ||
| 97 | std::vector<std::shared_ptr<NCA>> ncas; | 112 | std::vector<std::shared_ptr<NCA>> ncas; |
| 98 | }; | 113 | }; |
| 99 | } // namespace FileSys | 114 | } // namespace FileSys |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index e8b5d6ece..aa1b3c17d 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -3,12 +3,17 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstring> | ||
| 6 | #include <utility> | 7 | #include <utility> |
| 8 | |||
| 7 | #include <boost/optional.hpp> | 9 | #include <boost/optional.hpp> |
| 10 | |||
| 8 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 9 | #include "core/crypto/aes_util.h" | 12 | #include "core/crypto/aes_util.h" |
| 10 | #include "core/crypto/ctr_encryption_layer.h" | 13 | #include "core/crypto/ctr_encryption_layer.h" |
| 11 | #include "core/file_sys/content_archive.h" | 14 | #include "core/file_sys/content_archive.h" |
| 15 | #include "core/file_sys/nca_patch.h" | ||
| 16 | #include "core/file_sys/partition_filesystem.h" | ||
| 12 | #include "core/file_sys/romfs.h" | 17 | #include "core/file_sys/romfs.h" |
| 13 | #include "core/file_sys/vfs_offset.h" | 18 | #include "core/file_sys/vfs_offset.h" |
| 14 | #include "core/loader/loader.h" | 19 | #include "core/loader/loader.h" |
| @@ -64,10 +69,31 @@ struct RomFSSuperblock { | |||
| 64 | }; | 69 | }; |
| 65 | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); | 70 | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); |
| 66 | 71 | ||
| 72 | struct BKTRHeader { | ||
| 73 | u64_le offset; | ||
| 74 | u64_le size; | ||
| 75 | u32_le magic; | ||
| 76 | INSERT_PADDING_BYTES(0x4); | ||
| 77 | u32_le number_entries; | ||
| 78 | INSERT_PADDING_BYTES(0x4); | ||
| 79 | }; | ||
| 80 | static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); | ||
| 81 | |||
| 82 | struct BKTRSuperblock { | ||
| 83 | NCASectionHeaderBlock header_block; | ||
| 84 | IVFCHeader ivfc; | ||
| 85 | INSERT_PADDING_BYTES(0x18); | ||
| 86 | BKTRHeader relocation; | ||
| 87 | BKTRHeader subsection; | ||
| 88 | INSERT_PADDING_BYTES(0xC0); | ||
| 89 | }; | ||
| 90 | static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); | ||
| 91 | |||
| 67 | union NCASectionHeader { | 92 | union NCASectionHeader { |
| 68 | NCASectionRaw raw; | 93 | NCASectionRaw raw; |
| 69 | PFS0Superblock pfs0; | 94 | PFS0Superblock pfs0; |
| 70 | RomFSSuperblock romfs; | 95 | RomFSSuperblock romfs; |
| 96 | BKTRSuperblock bktr; | ||
| 71 | }; | 97 | }; |
| 72 | static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); | 98 | static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); |
| 73 | 99 | ||
| @@ -100,7 +126,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty | |||
| 100 | Core::Crypto::Key128 out; | 126 | Core::Crypto::Key128 out; |
| 101 | if (type == NCASectionCryptoType::XTS) | 127 | if (type == NCASectionCryptoType::XTS) |
| 102 | std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); | 128 | std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); |
| 103 | else if (type == NCASectionCryptoType::CTR) | 129 | else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) |
| 104 | std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); | 130 | std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); |
| 105 | else | 131 | else |
| 106 | LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", | 132 | LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", |
| @@ -150,6 +176,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting | |||
| 150 | LOG_DEBUG(Crypto, "called with mode=NONE"); | 176 | LOG_DEBUG(Crypto, "called with mode=NONE"); |
| 151 | return in; | 177 | return in; |
| 152 | case NCASectionCryptoType::CTR: | 178 | case NCASectionCryptoType::CTR: |
| 179 | // During normal BKTR decryption, this entire function is skipped. This is for the metadata, | ||
| 180 | // which uses the same CTR as usual. | ||
| 181 | case NCASectionCryptoType::BKTR: | ||
| 153 | LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); | 182 | LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); |
| 154 | { | 183 | { |
| 155 | boost::optional<Core::Crypto::Key128> key = boost::none; | 184 | boost::optional<Core::Crypto::Key128> key = boost::none; |
| @@ -186,7 +215,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting | |||
| 186 | } | 215 | } |
| 187 | } | 216 | } |
| 188 | 217 | ||
| 189 | NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | 218 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) |
| 219 | : file(std::move(file_)), | ||
| 220 | bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) { | ||
| 190 | status = Loader::ResultStatus::Success; | 221 | status = Loader::ResultStatus::Success; |
| 191 | 222 | ||
| 192 | if (file == nullptr) { | 223 | if (file == nullptr) { |
| @@ -261,22 +292,21 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||
| 261 | is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) { | 292 | is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) { |
| 262 | return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; | 293 | return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; |
| 263 | }) != sections.end(); | 294 | }) != sections.end(); |
| 295 | ivfc_offset = 0; | ||
| 264 | 296 | ||
| 265 | for (std::ptrdiff_t i = 0; i < number_sections; ++i) { | 297 | for (std::ptrdiff_t i = 0; i < number_sections; ++i) { |
| 266 | auto section = sections[i]; | 298 | auto section = sections[i]; |
| 267 | 299 | ||
| 268 | if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { | 300 | if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { |
| 269 | const size_t romfs_offset = | 301 | const std::size_t base_offset = |
| 270 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + | 302 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; |
| 271 | section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; | 303 | ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; |
| 272 | const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; | 304 | const std::size_t romfs_offset = base_offset + ivfc_offset; |
| 273 | auto dec = | 305 | const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; |
| 274 | Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset), | 306 | auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset); |
| 275 | romfs_offset); | 307 | auto dec = Decrypt(section, raw, romfs_offset); |
| 276 | if (dec != nullptr) { | 308 | |
| 277 | files.push_back(std::move(dec)); | 309 | if (dec == nullptr) { |
| 278 | romfs = files.back(); | ||
| 279 | } else { | ||
| 280 | if (status != Loader::ResultStatus::Success) | 310 | if (status != Loader::ResultStatus::Success) |
| 281 | return; | 311 | return; |
| 282 | if (has_rights_id) | 312 | if (has_rights_id) |
| @@ -285,6 +315,117 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||
| 285 | status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; | 315 | status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; |
| 286 | return; | 316 | return; |
| 287 | } | 317 | } |
| 318 | |||
| 319 | if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) { | ||
| 320 | if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') || | ||
| 321 | section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) { | ||
| 322 | status = Loader::ResultStatus::ErrorBadBKTRHeader; | ||
| 323 | return; | ||
| 324 | } | ||
| 325 | |||
| 326 | if (section.bktr.relocation.offset + section.bktr.relocation.size != | ||
| 327 | section.bktr.subsection.offset) { | ||
| 328 | status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation; | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | |||
| 332 | const u64 size = | ||
| 333 | MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - | ||
| 334 | header.section_tables[i].media_offset); | ||
| 335 | if (section.bktr.subsection.offset + section.bktr.subsection.size != size) { | ||
| 336 | status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd; | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | |||
| 340 | const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; | ||
| 341 | RelocationBlock relocation_block{}; | ||
| 342 | if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) != | ||
| 343 | sizeof(RelocationBlock)) { | ||
| 344 | status = Loader::ResultStatus::ErrorBadRelocationBlock; | ||
| 345 | return; | ||
| 346 | } | ||
| 347 | SubsectionBlock subsection_block{}; | ||
| 348 | if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) != | ||
| 349 | sizeof(RelocationBlock)) { | ||
| 350 | status = Loader::ResultStatus::ErrorBadSubsectionBlock; | ||
| 351 | return; | ||
| 352 | } | ||
| 353 | |||
| 354 | std::vector<RelocationBucketRaw> relocation_buckets_raw( | ||
| 355 | (section.bktr.relocation.size - sizeof(RelocationBlock)) / | ||
| 356 | sizeof(RelocationBucketRaw)); | ||
| 357 | if (dec->ReadBytes(relocation_buckets_raw.data(), | ||
| 358 | section.bktr.relocation.size - sizeof(RelocationBlock), | ||
| 359 | section.bktr.relocation.offset + sizeof(RelocationBlock) - | ||
| 360 | offset) != | ||
| 361 | section.bktr.relocation.size - sizeof(RelocationBlock)) { | ||
| 362 | status = Loader::ResultStatus::ErrorBadRelocationBuckets; | ||
| 363 | return; | ||
| 364 | } | ||
| 365 | |||
| 366 | std::vector<SubsectionBucketRaw> subsection_buckets_raw( | ||
| 367 | (section.bktr.subsection.size - sizeof(SubsectionBlock)) / | ||
| 368 | sizeof(SubsectionBucketRaw)); | ||
| 369 | if (dec->ReadBytes(subsection_buckets_raw.data(), | ||
| 370 | section.bktr.subsection.size - sizeof(SubsectionBlock), | ||
| 371 | section.bktr.subsection.offset + sizeof(SubsectionBlock) - | ||
| 372 | offset) != | ||
| 373 | section.bktr.subsection.size - sizeof(SubsectionBlock)) { | ||
| 374 | status = Loader::ResultStatus::ErrorBadSubsectionBuckets; | ||
| 375 | return; | ||
| 376 | } | ||
| 377 | |||
| 378 | std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size()); | ||
| 379 | std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(), | ||
| 380 | relocation_buckets.begin(), &ConvertRelocationBucketRaw); | ||
| 381 | std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size()); | ||
| 382 | std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(), | ||
| 383 | subsection_buckets.begin(), &ConvertSubsectionBucketRaw); | ||
| 384 | |||
| 385 | u32 ctr_low; | ||
| 386 | std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low)); | ||
| 387 | subsection_buckets.back().entries.push_back( | ||
| 388 | {section.bktr.relocation.offset, {0}, ctr_low}); | ||
| 389 | subsection_buckets.back().entries.push_back({size, {0}, 0}); | ||
| 390 | |||
| 391 | boost::optional<Core::Crypto::Key128> key = boost::none; | ||
| 392 | if (encrypted) { | ||
| 393 | if (has_rights_id) { | ||
| 394 | status = Loader::ResultStatus::Success; | ||
| 395 | key = GetTitlekey(); | ||
| 396 | if (key == boost::none) { | ||
| 397 | status = Loader::ResultStatus::ErrorMissingTitlekey; | ||
| 398 | return; | ||
| 399 | } | ||
| 400 | } else { | ||
| 401 | key = GetKeyAreaKey(NCASectionCryptoType::BKTR); | ||
| 402 | if (key == boost::none) { | ||
| 403 | status = Loader::ResultStatus::ErrorMissingKeyAreaKey; | ||
| 404 | return; | ||
| 405 | } | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | if (bktr_base_romfs == nullptr) { | ||
| 410 | status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; | ||
| 411 | return; | ||
| 412 | } | ||
| 413 | |||
| 414 | auto bktr = std::make_shared<BKTR>( | ||
| 415 | bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset), | ||
| 416 | relocation_block, relocation_buckets, subsection_block, subsection_buckets, | ||
| 417 | encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, | ||
| 418 | bktr_base_ivfc_offset, section.raw.section_ctr); | ||
| 419 | |||
| 420 | // BKTR applies to entire IVFC, so make an offset version to level 6 | ||
| 421 | |||
| 422 | files.push_back(std::make_shared<OffsetVfsFile>( | ||
| 423 | bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset)); | ||
| 424 | romfs = files.back(); | ||
| 425 | } else { | ||
| 426 | files.push_back(std::move(dec)); | ||
| 427 | romfs = files.back(); | ||
| 428 | } | ||
| 288 | } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { | 429 | } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { |
| 289 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | 430 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * |
| 290 | MEDIA_OFFSET_MULTIPLIER) + | 431 | MEDIA_OFFSET_MULTIPLIER) + |
| @@ -300,6 +441,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||
| 300 | dirs.push_back(std::move(npfs)); | 441 | dirs.push_back(std::move(npfs)); |
| 301 | if (IsDirectoryExeFS(dirs.back())) | 442 | if (IsDirectoryExeFS(dirs.back())) |
| 302 | exefs = dirs.back(); | 443 | exefs = dirs.back(); |
| 444 | } else { | ||
| 445 | if (has_rights_id) | ||
| 446 | status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; | ||
| 447 | else | ||
| 448 | status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; | ||
| 449 | return; | ||
| 303 | } | 450 | } |
| 304 | } else { | 451 | } else { |
| 305 | if (status != Loader::ResultStatus::Success) | 452 | if (status != Loader::ResultStatus::Success) |
| @@ -316,6 +463,8 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||
| 316 | status = Loader::ResultStatus::Success; | 463 | status = Loader::ResultStatus::Success; |
| 317 | } | 464 | } |
| 318 | 465 | ||
| 466 | NCA::~NCA() = default; | ||
| 467 | |||
| 319 | Loader::ResultStatus NCA::GetStatus() const { | 468 | Loader::ResultStatus NCA::GetStatus() const { |
| 320 | return status; | 469 | return status; |
| 321 | } | 470 | } |
| @@ -345,11 +494,15 @@ NCAContentType NCA::GetType() const { | |||
| 345 | } | 494 | } |
| 346 | 495 | ||
| 347 | u64 NCA::GetTitleId() const { | 496 | u64 NCA::GetTitleId() const { |
| 348 | if (status != Loader::ResultStatus::Success) | 497 | if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) |
| 349 | return {}; | 498 | return header.title_id | 0x800; |
| 350 | return header.title_id; | 499 | return header.title_id; |
| 351 | } | 500 | } |
| 352 | 501 | ||
| 502 | bool NCA::IsUpdate() const { | ||
| 503 | return is_update; | ||
| 504 | } | ||
| 505 | |||
| 353 | VirtualFile NCA::GetRomFS() const { | 506 | VirtualFile NCA::GetRomFS() const { |
| 354 | return romfs; | 507 | return romfs; |
| 355 | } | 508 | } |
| @@ -362,8 +515,8 @@ VirtualFile NCA::GetBaseFile() const { | |||
| 362 | return file; | 515 | return file; |
| 363 | } | 516 | } |
| 364 | 517 | ||
| 365 | bool NCA::IsUpdate() const { | 518 | u64 NCA::GetBaseIVFCOffset() const { |
| 366 | return is_update; | 519 | return ivfc_offset; |
| 367 | } | 520 | } |
| 368 | 521 | ||
| 369 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 522 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index b961cfde7..f9f66cae9 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -12,10 +12,12 @@ | |||
| 12 | #include "common/common_funcs.h" | 12 | #include "common/common_funcs.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 15 | #include "control_metadata.h" | ||
| 16 | #include "core/crypto/key_manager.h" | 15 | #include "core/crypto/key_manager.h" |
| 17 | #include "core/file_sys/partition_filesystem.h" | 16 | #include "core/file_sys/vfs.h" |
| 18 | #include "core/loader/loader.h" | 17 | |
| 18 | namespace Loader { | ||
| 19 | enum class ResultStatus : u16; | ||
| 20 | } | ||
| 19 | 21 | ||
| 20 | namespace FileSys { | 22 | namespace FileSys { |
| 21 | 23 | ||
| @@ -77,7 +79,10 @@ bool IsValidNCA(const NCAHeader& header); | |||
| 77 | // After construction, use GetStatus to determine if the file is valid and ready to be used. | 79 | // After construction, use GetStatus to determine if the file is valid and ready to be used. |
| 78 | class NCA : public ReadOnlyVfsDirectory { | 80 | class NCA : public ReadOnlyVfsDirectory { |
| 79 | public: | 81 | public: |
| 80 | explicit NCA(VirtualFile file); | 82 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, |
| 83 | u64 bktr_base_ivfc_offset = 0); | ||
| 84 | ~NCA() override; | ||
| 85 | |||
| 81 | Loader::ResultStatus GetStatus() const; | 86 | Loader::ResultStatus GetStatus() const; |
| 82 | 87 | ||
| 83 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 88 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| @@ -87,13 +92,15 @@ public: | |||
| 87 | 92 | ||
| 88 | NCAContentType GetType() const; | 93 | NCAContentType GetType() const; |
| 89 | u64 GetTitleId() const; | 94 | u64 GetTitleId() const; |
| 95 | bool IsUpdate() const; | ||
| 90 | 96 | ||
| 91 | VirtualFile GetRomFS() const; | 97 | VirtualFile GetRomFS() const; |
| 92 | VirtualDir GetExeFS() const; | 98 | VirtualDir GetExeFS() const; |
| 93 | 99 | ||
| 94 | VirtualFile GetBaseFile() const; | 100 | VirtualFile GetBaseFile() const; |
| 95 | 101 | ||
| 96 | bool IsUpdate() const; | 102 | // Returns the base ivfc offset used in BKTR patching. |
| 103 | u64 GetBaseIVFCOffset() const; | ||
| 97 | 104 | ||
| 98 | protected: | 105 | protected: |
| 99 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 106 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
| @@ -110,14 +117,16 @@ private: | |||
| 110 | VirtualFile romfs = nullptr; | 117 | VirtualFile romfs = nullptr; |
| 111 | VirtualDir exefs = nullptr; | 118 | VirtualDir exefs = nullptr; |
| 112 | VirtualFile file; | 119 | VirtualFile file; |
| 120 | VirtualFile bktr_base_romfs; | ||
| 121 | u64 ivfc_offset; | ||
| 113 | 122 | ||
| 114 | NCAHeader header{}; | 123 | NCAHeader header{}; |
| 115 | bool has_rights_id{}; | 124 | bool has_rights_id{}; |
| 116 | bool is_update{}; | ||
| 117 | 125 | ||
| 118 | Loader::ResultStatus status{}; | 126 | Loader::ResultStatus status{}; |
| 119 | 127 | ||
| 120 | bool encrypted; | 128 | bool encrypted; |
| 129 | bool is_update; | ||
| 121 | 130 | ||
| 122 | Core::Crypto::KeyManager keys; | 131 | Core::Crypto::KeyManager keys; |
| 123 | }; | 132 | }; |
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index ae21ad5b9..5b1177a03 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp | |||
| @@ -8,6 +8,14 @@ | |||
| 8 | 8 | ||
| 9 | namespace FileSys { | 9 | namespace FileSys { |
| 10 | 10 | ||
| 11 | const std::array<const char*, 15> LANGUAGE_NAMES = { | ||
| 12 | "AmericanEnglish", "BritishEnglish", "Japanese", | ||
| 13 | "French", "German", "LatinAmericanSpanish", | ||
| 14 | "Spanish", "Italian", "Dutch", | ||
| 15 | "CanadianFrench", "Portugese", "Russian", | ||
| 16 | "Korean", "Taiwanese", "Chinese", | ||
| 17 | }; | ||
| 18 | |||
| 11 | std::string LanguageEntry::GetApplicationName() const { | 19 | std::string LanguageEntry::GetApplicationName() const { |
| 12 | return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); | 20 | return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); |
| 13 | } | 21 | } |
| @@ -20,8 +28,20 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { | |||
| 20 | file->ReadObject(raw.get()); | 28 | file->ReadObject(raw.get()); |
| 21 | } | 29 | } |
| 22 | 30 | ||
| 31 | NACP::~NACP() = default; | ||
| 32 | |||
| 23 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | 33 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { |
| 24 | return raw->language_entries.at(static_cast<u8>(language)); | 34 | if (language != Language::Default) { |
| 35 | return raw->language_entries.at(static_cast<u8>(language)); | ||
| 36 | } | ||
| 37 | |||
| 38 | for (const auto& language_entry : raw->language_entries) { | ||
| 39 | if (!language_entry.GetApplicationName().empty()) | ||
| 40 | return language_entry; | ||
| 41 | } | ||
| 42 | |||
| 43 | // Fallback to English | ||
| 44 | return GetLanguageEntry(Language::AmericanEnglish); | ||
| 25 | } | 45 | } |
| 26 | 46 | ||
| 27 | std::string NACP::GetApplicationName(Language language) const { | 47 | std::string NACP::GetApplicationName(Language language) const { |
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 8c2cc1a2a..43d6f0719 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/swap.h" | ||
| 11 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs.h" |
| 12 | 14 | ||
| 13 | namespace FileSys { | 15 | namespace FileSys { |
| @@ -60,23 +62,22 @@ enum class Language : u8 { | |||
| 60 | Korean = 12, | 62 | Korean = 12, |
| 61 | Taiwanese = 13, | 63 | Taiwanese = 13, |
| 62 | Chinese = 14, | 64 | Chinese = 14, |
| 65 | |||
| 66 | Default = 255, | ||
| 63 | }; | 67 | }; |
| 64 | 68 | ||
| 65 | static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { | 69 | extern const std::array<const char*, 15> LANGUAGE_NAMES; |
| 66 | "AmericanEnglish", "BritishEnglish", "Japanese", | ||
| 67 | "French", "German", "LatinAmericanSpanish", | ||
| 68 | "Spanish", "Italian", "Dutch", | ||
| 69 | "CanadianFrench", "Portugese", "Russian", | ||
| 70 | "Korean", "Taiwanese", "Chinese"}; | ||
| 71 | 70 | ||
| 72 | // A class representing the format used by NX metadata files, typically named Control.nacp. | 71 | // A class representing the format used by NX metadata files, typically named Control.nacp. |
| 73 | // These store application name, dev name, title id, and other miscellaneous data. | 72 | // These store application name, dev name, title id, and other miscellaneous data. |
| 74 | class NACP { | 73 | class NACP { |
| 75 | public: | 74 | public: |
| 76 | explicit NACP(VirtualFile file); | 75 | explicit NACP(VirtualFile file); |
| 77 | const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const; | 76 | ~NACP(); |
| 78 | std::string GetApplicationName(Language language = Language::AmericanEnglish) const; | 77 | |
| 79 | std::string GetDeveloperName(Language language = Language::AmericanEnglish) const; | 78 | const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; |
| 79 | std::string GetApplicationName(Language language = Language::Default) const; | ||
| 80 | std::string GetDeveloperName(Language language = Language::Default) const; | ||
| 80 | u64 GetTitleId() const; | 81 | u64 GetTitleId() const; |
| 81 | std::string GetVersionString() const; | 82 | std::string GetVersionString() const; |
| 82 | 83 | ||
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index 3759e743a..12bb90ec8 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h | |||
| @@ -25,7 +25,7 @@ enum EntryType : u8 { | |||
| 25 | struct Entry { | 25 | struct Entry { |
| 26 | Entry(std::string_view view, EntryType entry_type, u64 entry_size) | 26 | Entry(std::string_view view, EntryType entry_type, u64 entry_size) |
| 27 | : type{entry_type}, file_size{entry_size} { | 27 | : type{entry_type}, file_size{entry_size} { |
| 28 | const size_t copy_size = view.copy(filename, std::size(filename) - 1); | 28 | const std::size_t copy_size = view.copy(filename, std::size(filename) - 1); |
| 29 | filename[copy_size] = '\0'; | 29 | filename[copy_size] = '\0'; |
| 30 | } | 30 | } |
| 31 | 31 | ||
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 449244444..6f34b7836 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp | |||
| @@ -3,20 +3,19 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_funcs.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 9 | #include "content_archive.h" | ||
| 10 | #include "core/file_sys/nca_metadata.h" | 9 | #include "core/file_sys/nca_metadata.h" |
| 11 | 10 | ||
| 12 | namespace FileSys { | 11 | namespace FileSys { |
| 13 | 12 | ||
| 14 | bool operator>=(TitleType lhs, TitleType rhs) { | 13 | bool operator>=(TitleType lhs, TitleType rhs) { |
| 15 | return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs); | 14 | return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs); |
| 16 | } | 15 | } |
| 17 | 16 | ||
| 18 | bool operator<=(TitleType lhs, TitleType rhs) { | 17 | bool operator<=(TitleType lhs, TitleType rhs) { |
| 19 | return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs); | 18 | return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs); |
| 20 | } | 19 | } |
| 21 | 20 | ||
| 22 | CNMT::CNMT(VirtualFile file) { | 21 | CNMT::CNMT(VirtualFile file) { |
| @@ -52,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco | |||
| 52 | : header(std::move(header)), opt_header(std::move(opt_header)), | 51 | : header(std::move(header)), opt_header(std::move(opt_header)), |
| 53 | content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} | 52 | content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} |
| 54 | 53 | ||
| 54 | CNMT::~CNMT() = default; | ||
| 55 | |||
| 55 | u64 CNMT::GetTitleID() const { | 56 | u64 CNMT::GetTitleID() const { |
| 56 | return header.title_id; | 57 | return header.title_id; |
| 57 | } | 58 | } |
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index ce05b4c1d..a05d155f4 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstring> | ||
| 8 | #include <memory> | 7 | #include <memory> |
| 9 | #include <vector> | 8 | #include <vector> |
| 10 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| @@ -88,6 +87,7 @@ public: | |||
| 88 | explicit CNMT(VirtualFile file); | 87 | explicit CNMT(VirtualFile file); |
| 89 | CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, | 88 | CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, |
| 90 | std::vector<MetaRecord> meta_records); | 89 | std::vector<MetaRecord> meta_records); |
| 90 | ~CNMT(); | ||
| 91 | 91 | ||
| 92 | u64 GetTitleID() const; | 92 | u64 GetTitleID() const; |
| 93 | u32 GetTitleVersion() const; | 93 | u32 GetTitleVersion() const; |
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp new file mode 100644 index 000000000..0090cc6c4 --- /dev/null +++ b/src/core/file_sys/nca_patch.cpp | |||
| @@ -0,0 +1,210 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <cstddef> | ||
| 7 | #include <cstring> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "core/crypto/aes_util.h" | ||
| 11 | #include "core/file_sys/nca_patch.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, | ||
| 16 | std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_, | ||
| 17 | std::vector<SubsectionBucket> subsection_buckets_, bool is_encrypted_, | ||
| 18 | Core::Crypto::Key128 key_, u64 base_offset_, u64 ivfc_offset_, | ||
| 19 | std::array<u8, 8> section_ctr_) | ||
| 20 | : relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)), | ||
| 21 | subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)), | ||
| 22 | base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)), | ||
| 23 | encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_), | ||
| 24 | section_ctr(section_ctr_) { | ||
| 25 | for (std::size_t i = 0; i < relocation.number_buckets - 1; ++i) { | ||
| 26 | relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0}); | ||
| 27 | } | ||
| 28 | |||
| 29 | for (std::size_t i = 0; i < subsection.number_buckets - 1; ++i) { | ||
| 30 | subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch, | ||
| 31 | {0}, | ||
| 32 | subsection_buckets[i + 1].entries[0].ctr}); | ||
| 33 | } | ||
| 34 | |||
| 35 | relocation_buckets.back().entries.push_back({relocation.size, 0, 0}); | ||
| 36 | } | ||
| 37 | |||
| 38 | BKTR::~BKTR() = default; | ||
| 39 | |||
| 40 | std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const { | ||
| 41 | // Read out of bounds. | ||
| 42 | if (offset >= relocation.size) | ||
| 43 | return 0; | ||
| 44 | const auto relocation = GetRelocationEntry(offset); | ||
| 45 | const auto section_offset = offset - relocation.address_patch + relocation.address_source; | ||
| 46 | const auto bktr_read = relocation.from_patch; | ||
| 47 | |||
| 48 | const auto next_relocation = GetNextRelocationEntry(offset); | ||
| 49 | |||
| 50 | if (offset + length > next_relocation.address_patch) { | ||
| 51 | const u64 partition = next_relocation.address_patch - offset; | ||
| 52 | return Read(data, partition, offset) + | ||
| 53 | Read(data + partition, length - partition, offset + partition); | ||
| 54 | } | ||
| 55 | |||
| 56 | if (!bktr_read) { | ||
| 57 | ASSERT_MSG(section_offset >= ivfc_offset, "Offset calculation negative."); | ||
| 58 | return base_romfs->Read(data, length, section_offset - ivfc_offset); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (!encrypted) { | ||
| 62 | return bktr_romfs->Read(data, length, section_offset); | ||
| 63 | } | ||
| 64 | |||
| 65 | const auto subsection = GetSubsectionEntry(section_offset); | ||
| 66 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); | ||
| 67 | |||
| 68 | // Calculate AES IV | ||
| 69 | std::vector<u8> iv(16); | ||
| 70 | auto subsection_ctr = subsection.ctr; | ||
| 71 | auto offset_iv = section_offset + base_offset; | ||
| 72 | for (std::size_t i = 0; i < section_ctr.size(); ++i) | ||
| 73 | iv[i] = section_ctr[0x8 - i - 1]; | ||
| 74 | offset_iv >>= 4; | ||
| 75 | for (std::size_t i = 0; i < sizeof(u64); ++i) { | ||
| 76 | iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF); | ||
| 77 | offset_iv >>= 8; | ||
| 78 | } | ||
| 79 | for (std::size_t i = 0; i < sizeof(u32); ++i) { | ||
| 80 | iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF); | ||
| 81 | subsection_ctr >>= 8; | ||
| 82 | } | ||
| 83 | cipher.SetIV(iv); | ||
| 84 | |||
| 85 | const auto next_subsection = GetNextSubsectionEntry(section_offset); | ||
| 86 | |||
| 87 | if (section_offset + length > next_subsection.address_patch) { | ||
| 88 | const u64 partition = next_subsection.address_patch - section_offset; | ||
| 89 | return Read(data, partition, offset) + | ||
| 90 | Read(data + partition, length - partition, offset + partition); | ||
| 91 | } | ||
| 92 | |||
| 93 | const auto block_offset = section_offset & 0xF; | ||
| 94 | if (block_offset != 0) { | ||
| 95 | auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF); | ||
| 96 | cipher.Transcode(block.data(), block.size(), block.data(), Core::Crypto::Op::Decrypt); | ||
| 97 | if (length + block_offset < 0x10) { | ||
| 98 | std::memcpy(data, block.data() + block_offset, std::min(length, block.size())); | ||
| 99 | return std::min(length, block.size()); | ||
| 100 | } | ||
| 101 | |||
| 102 | const auto read = 0x10 - block_offset; | ||
| 103 | std::memcpy(data, block.data() + block_offset, read); | ||
| 104 | return read + Read(data + read, length - read, offset + read); | ||
| 105 | } | ||
| 106 | |||
| 107 | const auto raw_read = bktr_romfs->Read(data, length, section_offset); | ||
| 108 | cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt); | ||
| 109 | return raw_read; | ||
| 110 | } | ||
| 111 | |||
| 112 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 113 | std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, | ||
| 114 | BucketType buckets) const { | ||
| 115 | if constexpr (Subsection) { | ||
| 116 | const auto last_bucket = buckets[block.number_buckets - 1]; | ||
| 117 | if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) | ||
| 118 | return {block.number_buckets - 1, last_bucket.number_entries}; | ||
| 119 | } else { | ||
| 120 | ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); | ||
| 121 | } | ||
| 122 | |||
| 123 | std::size_t bucket_id = std::count_if( | ||
| 124 | block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, | ||
| 125 | [&offset](u64 base_offset) { return base_offset <= offset; }); | ||
| 126 | |||
| 127 | const auto bucket = buckets[bucket_id]; | ||
| 128 | |||
| 129 | if (bucket.number_entries == 1) | ||
| 130 | return {bucket_id, 0}; | ||
| 131 | |||
| 132 | std::size_t low = 0; | ||
| 133 | std::size_t mid = 0; | ||
| 134 | std::size_t high = bucket.number_entries - 1; | ||
| 135 | while (low <= high) { | ||
| 136 | mid = (low + high) / 2; | ||
| 137 | if (bucket.entries[mid].address_patch > offset) { | ||
| 138 | high = mid - 1; | ||
| 139 | } else { | ||
| 140 | if (mid == bucket.number_entries - 1 || | ||
| 141 | bucket.entries[mid + 1].address_patch > offset) { | ||
| 142 | return {bucket_id, mid}; | ||
| 143 | } | ||
| 144 | |||
| 145 | low = mid + 1; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | UNREACHABLE_MSG("Offset could not be found in BKTR block."); | ||
| 150 | } | ||
| 151 | |||
| 152 | RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { | ||
| 153 | const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); | ||
| 154 | return relocation_buckets[res.first].entries[res.second]; | ||
| 155 | } | ||
| 156 | |||
| 157 | RelocationEntry BKTR::GetNextRelocationEntry(u64 offset) const { | ||
| 158 | const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); | ||
| 159 | const auto bucket = relocation_buckets[res.first]; | ||
| 160 | if (res.second + 1 < bucket.entries.size()) | ||
| 161 | return bucket.entries[res.second + 1]; | ||
| 162 | return relocation_buckets[res.first + 1].entries[0]; | ||
| 163 | } | ||
| 164 | |||
| 165 | SubsectionEntry BKTR::GetSubsectionEntry(u64 offset) const { | ||
| 166 | const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets); | ||
| 167 | return subsection_buckets[res.first].entries[res.second]; | ||
| 168 | } | ||
| 169 | |||
| 170 | SubsectionEntry BKTR::GetNextSubsectionEntry(u64 offset) const { | ||
| 171 | const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets); | ||
| 172 | const auto bucket = subsection_buckets[res.first]; | ||
| 173 | if (res.second + 1 < bucket.entries.size()) | ||
| 174 | return bucket.entries[res.second + 1]; | ||
| 175 | return subsection_buckets[res.first + 1].entries[0]; | ||
| 176 | } | ||
| 177 | |||
| 178 | std::string BKTR::GetName() const { | ||
| 179 | return base_romfs->GetName(); | ||
| 180 | } | ||
| 181 | |||
| 182 | std::size_t BKTR::GetSize() const { | ||
| 183 | return relocation.size; | ||
| 184 | } | ||
| 185 | |||
| 186 | bool BKTR::Resize(std::size_t new_size) { | ||
| 187 | return false; | ||
| 188 | } | ||
| 189 | |||
| 190 | std::shared_ptr<VfsDirectory> BKTR::GetContainingDirectory() const { | ||
| 191 | return base_romfs->GetContainingDirectory(); | ||
| 192 | } | ||
| 193 | |||
| 194 | bool BKTR::IsWritable() const { | ||
| 195 | return false; | ||
| 196 | } | ||
| 197 | |||
| 198 | bool BKTR::IsReadable() const { | ||
| 199 | return true; | ||
| 200 | } | ||
| 201 | |||
| 202 | std::size_t BKTR::Write(const u8* data, std::size_t length, std::size_t offset) { | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | bool BKTR::Rename(std::string_view name) { | ||
| 207 | return base_romfs->Rename(name); | ||
| 208 | } | ||
| 209 | |||
| 210 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h new file mode 100644 index 000000000..8e64e8378 --- /dev/null +++ b/src/core/file_sys/nca_patch.h | |||
| @@ -0,0 +1,150 @@ | |||
| 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 <array> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_funcs.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/swap.h" | ||
| 14 | #include "core/crypto/key_manager.h" | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | #pragma pack(push, 1) | ||
| 19 | struct RelocationEntry { | ||
| 20 | u64_le address_patch; | ||
| 21 | u64_le address_source; | ||
| 22 | u32 from_patch; | ||
| 23 | }; | ||
| 24 | #pragma pack(pop) | ||
| 25 | static_assert(sizeof(RelocationEntry) == 0x14, "RelocationEntry has incorrect size."); | ||
| 26 | |||
| 27 | struct RelocationBucketRaw { | ||
| 28 | INSERT_PADDING_BYTES(4); | ||
| 29 | u32_le number_entries; | ||
| 30 | u64_le end_offset; | ||
| 31 | std::array<RelocationEntry, 0x332> relocation_entries; | ||
| 32 | INSERT_PADDING_BYTES(8); | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(RelocationBucketRaw) == 0x4000, "RelocationBucketRaw has incorrect size."); | ||
| 35 | |||
| 36 | // Vector version of RelocationBucketRaw | ||
| 37 | struct RelocationBucket { | ||
| 38 | u32 number_entries; | ||
| 39 | u64 end_offset; | ||
| 40 | std::vector<RelocationEntry> entries; | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct RelocationBlock { | ||
| 44 | INSERT_PADDING_BYTES(4); | ||
| 45 | u32_le number_buckets; | ||
| 46 | u64_le size; | ||
| 47 | std::array<u64, 0x7FE> base_offsets; | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(RelocationBlock) == 0x4000, "RelocationBlock has incorrect size."); | ||
| 50 | |||
| 51 | struct SubsectionEntry { | ||
| 52 | u64_le address_patch; | ||
| 53 | INSERT_PADDING_BYTES(0x4); | ||
| 54 | u32_le ctr; | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(SubsectionEntry) == 0x10, "SubsectionEntry has incorrect size."); | ||
| 57 | |||
| 58 | struct SubsectionBucketRaw { | ||
| 59 | INSERT_PADDING_BYTES(4); | ||
| 60 | u32_le number_entries; | ||
| 61 | u64_le end_offset; | ||
| 62 | std::array<SubsectionEntry, 0x3FF> subsection_entries; | ||
| 63 | }; | ||
| 64 | static_assert(sizeof(SubsectionBucketRaw) == 0x4000, "SubsectionBucketRaw has incorrect size."); | ||
| 65 | |||
| 66 | // Vector version of SubsectionBucketRaw | ||
| 67 | struct SubsectionBucket { | ||
| 68 | u32 number_entries; | ||
| 69 | u64 end_offset; | ||
| 70 | std::vector<SubsectionEntry> entries; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct SubsectionBlock { | ||
| 74 | INSERT_PADDING_BYTES(4); | ||
| 75 | u32_le number_buckets; | ||
| 76 | u64_le size; | ||
| 77 | std::array<u64, 0x7FE> base_offsets; | ||
| 78 | }; | ||
| 79 | static_assert(sizeof(SubsectionBlock) == 0x4000, "SubsectionBlock has incorrect size."); | ||
| 80 | |||
| 81 | inline RelocationBucket ConvertRelocationBucketRaw(RelocationBucketRaw raw) { | ||
| 82 | return {raw.number_entries, | ||
| 83 | raw.end_offset, | ||
| 84 | {raw.relocation_entries.begin(), raw.relocation_entries.begin() + raw.number_entries}}; | ||
| 85 | } | ||
| 86 | |||
| 87 | inline SubsectionBucket ConvertSubsectionBucketRaw(SubsectionBucketRaw raw) { | ||
| 88 | return {raw.number_entries, | ||
| 89 | raw.end_offset, | ||
| 90 | {raw.subsection_entries.begin(), raw.subsection_entries.begin() + raw.number_entries}}; | ||
| 91 | } | ||
| 92 | |||
| 93 | class BKTR : public VfsFile { | ||
| 94 | public: | ||
| 95 | BKTR(VirtualFile base_romfs, VirtualFile bktr_romfs, RelocationBlock relocation, | ||
| 96 | std::vector<RelocationBucket> relocation_buckets, SubsectionBlock subsection, | ||
| 97 | std::vector<SubsectionBucket> subsection_buckets, bool is_encrypted, | ||
| 98 | Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr); | ||
| 99 | ~BKTR() override; | ||
| 100 | |||
| 101 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | ||
| 102 | |||
| 103 | std::string GetName() const override; | ||
| 104 | |||
| 105 | std::size_t GetSize() const override; | ||
| 106 | |||
| 107 | bool Resize(std::size_t new_size) override; | ||
| 108 | |||
| 109 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 110 | |||
| 111 | bool IsWritable() const override; | ||
| 112 | |||
| 113 | bool IsReadable() const override; | ||
| 114 | |||
| 115 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; | ||
| 116 | |||
| 117 | bool Rename(std::string_view name) override; | ||
| 118 | |||
| 119 | private: | ||
| 120 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 121 | std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block, | ||
| 122 | BucketType buckets) const; | ||
| 123 | |||
| 124 | RelocationEntry GetRelocationEntry(u64 offset) const; | ||
| 125 | RelocationEntry GetNextRelocationEntry(u64 offset) const; | ||
| 126 | |||
| 127 | SubsectionEntry GetSubsectionEntry(u64 offset) const; | ||
| 128 | SubsectionEntry GetNextSubsectionEntry(u64 offset) const; | ||
| 129 | |||
| 130 | RelocationBlock relocation; | ||
| 131 | std::vector<RelocationBucket> relocation_buckets; | ||
| 132 | SubsectionBlock subsection; | ||
| 133 | std::vector<SubsectionBucket> subsection_buckets; | ||
| 134 | |||
| 135 | // Should be the raw base romfs, decrypted. | ||
| 136 | VirtualFile base_romfs; | ||
| 137 | // Should be the raw BKTR romfs, (located at media_offset with size media_size). | ||
| 138 | VirtualFile bktr_romfs; | ||
| 139 | |||
| 140 | bool encrypted; | ||
| 141 | Core::Crypto::Key128 key; | ||
| 142 | |||
| 143 | // Base offset into NCA, used for IV calculation. | ||
| 144 | u64 base_offset; | ||
| 145 | // Distance between IVFC start and RomFS start, used for base reads | ||
| 146 | u64 ivfc_offset; | ||
| 147 | std::array<u8, 8> section_ctr; | ||
| 148 | }; | ||
| 149 | |||
| 150 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index c377edc9c..5791c76ff 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -42,21 +42,21 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | |||
| 42 | 42 | ||
| 43 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 43 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| 44 | 44 | ||
| 45 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | 45 | std::size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); |
| 46 | size_t metadata_size = | 46 | std::size_t metadata_size = |
| 47 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; | 47 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; |
| 48 | 48 | ||
| 49 | // Actually read in now... | 49 | // Actually read in now... |
| 50 | std::vector<u8> file_data = file->ReadBytes(metadata_size); | 50 | std::vector<u8> file_data = file->ReadBytes(metadata_size); |
| 51 | const size_t total_size = file_data.size(); | 51 | const std::size_t total_size = file_data.size(); |
| 52 | 52 | ||
| 53 | if (total_size != metadata_size) { | 53 | if (total_size != metadata_size) { |
| 54 | status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; | 54 | status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; |
| 55 | return; | 55 | return; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | size_t entries_offset = sizeof(Header); | 58 | std::size_t entries_offset = sizeof(Header); |
| 59 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); | 59 | std::size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); |
| 60 | content_offset = strtab_offset + pfs_header.strtab_size; | 60 | content_offset = strtab_offset + pfs_header.strtab_size; |
| 61 | for (u16 i = 0; i < pfs_header.num_entries; i++) { | 61 | for (u16 i = 0; i < pfs_header.num_entries; i++) { |
| 62 | FSEntry entry; | 62 | FSEntry entry; |
| @@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | |||
| 72 | status = Loader::ResultStatus::Success; | 72 | status = Loader::ResultStatus::Success; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | PartitionFilesystem::~PartitionFilesystem() = default; | ||
| 76 | |||
| 75 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { | 77 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { |
| 76 | return status; | 78 | return status; |
| 77 | } | 79 | } |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index be7bc32a8..739c63a7f 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -25,6 +25,8 @@ namespace FileSys { | |||
| 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { | 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { |
| 26 | public: | 26 | public: |
| 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); | 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); |
| 28 | ~PartitionFilesystem() override; | ||
| 29 | |||
| 28 | Loader::ResultStatus GetStatus() const; | 30 | Loader::ResultStatus GetStatus() const; |
| 29 | 31 | ||
| 30 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 32 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| @@ -79,7 +81,7 @@ private: | |||
| 79 | 81 | ||
| 80 | Header pfs_header{}; | 82 | Header pfs_header{}; |
| 81 | bool is_hfs = false; | 83 | bool is_hfs = false; |
| 82 | size_t content_offset = 0; | 84 | std::size_t content_offset = 0; |
| 83 | 85 | ||
| 84 | std::vector<VirtualFile> pfs_files; | 86 | std::vector<VirtualFile> pfs_files; |
| 85 | std::vector<VirtualDir> pfs_dirs; | 87 | std::vector<VirtualDir> pfs_dirs; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp new file mode 100644 index 000000000..aebc69d52 --- /dev/null +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -0,0 +1,159 @@ | |||
| 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 <array> | ||
| 6 | #include <cstddef> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/content_archive.h" | ||
| 10 | #include "core/file_sys/control_metadata.h" | ||
| 11 | #include "core/file_sys/patch_manager.h" | ||
| 12 | #include "core/file_sys/registered_cache.h" | ||
| 13 | #include "core/file_sys/romfs.h" | ||
| 14 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 15 | #include "core/loader/loader.h" | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | ||
| 20 | |||
| 21 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | ||
| 22 | std::array<u8, sizeof(u32)> bytes{}; | ||
| 23 | bytes[0] = version % SINGLE_BYTE_MODULUS; | ||
| 24 | for (std::size_t i = 1; i < bytes.size(); ++i) { | ||
| 25 | version /= SINGLE_BYTE_MODULUS; | ||
| 26 | bytes[i] = version % SINGLE_BYTE_MODULUS; | ||
| 27 | } | ||
| 28 | |||
| 29 | if (format == TitleVersionFormat::FourElements) | ||
| 30 | return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); | ||
| 31 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); | ||
| 32 | } | ||
| 33 | |||
| 34 | constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{ | ||
| 35 | "Update", | ||
| 36 | }; | ||
| 37 | |||
| 38 | std::string FormatPatchTypeName(PatchType type) { | ||
| 39 | return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type)); | ||
| 40 | } | ||
| 41 | |||
| 42 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | ||
| 43 | |||
| 44 | PatchManager::~PatchManager() = default; | ||
| 45 | |||
| 46 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | ||
| 47 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); | ||
| 48 | |||
| 49 | if (exefs == nullptr) | ||
| 50 | return exefs; | ||
| 51 | |||
| 52 | const auto installed = Service::FileSystem::GetUnionContents(); | ||
| 53 | |||
| 54 | // Game Updates | ||
| 55 | const auto update_tid = GetUpdateTitleID(title_id); | ||
| 56 | const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); | ||
| 57 | if (update != nullptr) { | ||
| 58 | if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && | ||
| 59 | update->GetExeFS() != nullptr) { | ||
| 60 | LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", | ||
| 61 | FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); | ||
| 62 | exefs = update->GetExeFS(); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | return exefs; | ||
| 67 | } | ||
| 68 | |||
| 69 | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, | ||
| 70 | ContentRecordType type) const { | ||
| 71 | LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, | ||
| 72 | static_cast<u8>(type)); | ||
| 73 | |||
| 74 | if (romfs == nullptr) | ||
| 75 | return romfs; | ||
| 76 | |||
| 77 | const auto installed = Service::FileSystem::GetUnionContents(); | ||
| 78 | |||
| 79 | // Game Updates | ||
| 80 | const auto update_tid = GetUpdateTitleID(title_id); | ||
| 81 | const auto update = installed->GetEntryRaw(update_tid, type); | ||
| 82 | if (update != nullptr) { | ||
| 83 | const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); | ||
| 84 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && | ||
| 85 | new_nca->GetRomFS() != nullptr) { | ||
| 86 | LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", | ||
| 87 | FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); | ||
| 88 | romfs = new_nca->GetRomFS(); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | return romfs; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { | ||
| 96 | std::map<PatchType, std::string> out; | ||
| 97 | const auto installed = Service::FileSystem::GetUnionContents(); | ||
| 98 | |||
| 99 | const auto update_tid = GetUpdateTitleID(title_id); | ||
| 100 | PatchManager update{update_tid}; | ||
| 101 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); | ||
| 102 | |||
| 103 | if (nacp != nullptr) { | ||
| 104 | out[PatchType::Update] = nacp->GetVersionString(); | ||
| 105 | } else { | ||
| 106 | if (installed->HasEntry(update_tid, ContentRecordType::Program)) { | ||
| 107 | const auto meta_ver = installed->GetEntryVersion(update_tid); | ||
| 108 | if (meta_ver == boost::none || meta_ver.get() == 0) { | ||
| 109 | out[PatchType::Update] = ""; | ||
| 110 | } else { | ||
| 111 | out[PatchType::Update] = | ||
| 112 | FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | return out; | ||
| 118 | } | ||
| 119 | |||
| 120 | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | ||
| 121 | const auto& installed{Service::FileSystem::GetUnionContents()}; | ||
| 122 | |||
| 123 | const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); | ||
| 124 | if (base_control_nca == nullptr) | ||
| 125 | return {}; | ||
| 126 | |||
| 127 | return ParseControlNCA(base_control_nca); | ||
| 128 | } | ||
| 129 | |||
| 130 | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | ||
| 131 | const std::shared_ptr<NCA>& nca) const { | ||
| 132 | const auto base_romfs = nca->GetRomFS(); | ||
| 133 | if (base_romfs == nullptr) | ||
| 134 | return {}; | ||
| 135 | |||
| 136 | const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); | ||
| 137 | if (romfs == nullptr) | ||
| 138 | return {}; | ||
| 139 | |||
| 140 | const auto extracted = ExtractRomFS(romfs); | ||
| 141 | if (extracted == nullptr) | ||
| 142 | return {}; | ||
| 143 | |||
| 144 | auto nacp_file = extracted->GetFile("control.nacp"); | ||
| 145 | if (nacp_file == nullptr) | ||
| 146 | nacp_file = extracted->GetFile("Control.nacp"); | ||
| 147 | |||
| 148 | const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); | ||
| 149 | |||
| 150 | VirtualFile icon_file; | ||
| 151 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||
| 152 | icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); | ||
| 153 | if (icon_file != nullptr) | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | |||
| 157 | return {nacp, icon_file}; | ||
| 158 | } | ||
| 159 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h new file mode 100644 index 000000000..209cab1dc --- /dev/null +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -0,0 +1,64 @@ | |||
| 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 <map> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/file_sys/nca_metadata.h" | ||
| 12 | #include "core/file_sys/vfs.h" | ||
| 13 | |||
| 14 | namespace FileSys { | ||
| 15 | |||
| 16 | class NCA; | ||
| 17 | class NACP; | ||
| 18 | |||
| 19 | enum class TitleVersionFormat : u8 { | ||
| 20 | ThreeElements, ///< vX.Y.Z | ||
| 21 | FourElements, ///< vX.Y.Z.W | ||
| 22 | }; | ||
| 23 | |||
| 24 | std::string FormatTitleVersion(u32 version, | ||
| 25 | TitleVersionFormat format = TitleVersionFormat::ThreeElements); | ||
| 26 | |||
| 27 | enum class PatchType { | ||
| 28 | Update, | ||
| 29 | }; | ||
| 30 | |||
| 31 | std::string FormatPatchTypeName(PatchType type); | ||
| 32 | |||
| 33 | // A centralized class to manage patches to games. | ||
| 34 | class PatchManager { | ||
| 35 | public: | ||
| 36 | explicit PatchManager(u64 title_id); | ||
| 37 | ~PatchManager(); | ||
| 38 | |||
| 39 | // Currently tracked ExeFS patches: | ||
| 40 | // - Game Updates | ||
| 41 | VirtualDir PatchExeFS(VirtualDir exefs) const; | ||
| 42 | |||
| 43 | // Currently tracked RomFS patches: | ||
| 44 | // - Game Updates | ||
| 45 | VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, | ||
| 46 | ContentRecordType type = ContentRecordType::Program) const; | ||
| 47 | |||
| 48 | // Returns a vector of pairs between patch names and patch versions. | ||
| 49 | // i.e. Update v80 will return {Update, 80} | ||
| 50 | std::map<PatchType, std::string> GetPatchVersionNames() const; | ||
| 51 | |||
| 52 | // Given title_id of the program, attempts to get the control data of the update and parse it, | ||
| 53 | // falling back to the base control data. | ||
| 54 | std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const; | ||
| 55 | |||
| 56 | // Version of GetControlMetadata that takes an arbitrary NCA | ||
| 57 | std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA( | ||
| 58 | const std::shared_ptr<NCA>& nca) const; | ||
| 59 | |||
| 60 | private: | ||
| 61 | u64 title_id; | ||
| 62 | }; | ||
| 63 | |||
| 64 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 279f987d4..02319ce0f 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -2,15 +2,22 @@ | |||
| 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 "common/file_util.h" | 5 | #include <cstddef> |
| 6 | #include <cstring> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 6 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 7 | #include "core/file_sys/program_metadata.h" | 10 | #include "core/file_sys/program_metadata.h" |
| 8 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| 9 | 12 | ||
| 10 | namespace FileSys { | 13 | namespace FileSys { |
| 11 | 14 | ||
| 15 | ProgramMetadata::ProgramMetadata() = default; | ||
| 16 | |||
| 17 | ProgramMetadata::~ProgramMetadata() = default; | ||
| 18 | |||
| 12 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | 19 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { |
| 13 | size_t total_size = static_cast<size_t>(file->GetSize()); | 20 | std::size_t total_size = static_cast<std::size_t>(file->GetSize()); |
| 14 | if (total_size < sizeof(Header)) | 21 | if (total_size < sizeof(Header)) |
| 15 | return Loader::ResultStatus::ErrorBadNPDMHeader; | 22 | return Loader::ResultStatus::ErrorBadNPDMHeader; |
| 16 | 23 | ||
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 74a91052b..1143e36c4 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -5,12 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 13 | #include "partition_filesystem.h" | 11 | #include "core/file_sys/vfs.h" |
| 14 | 12 | ||
| 15 | namespace Loader { | 13 | namespace Loader { |
| 16 | enum class ResultStatus : u16; | 14 | enum class ResultStatus : u16; |
| @@ -38,6 +36,9 @@ enum class ProgramFilePermission : u64 { | |||
| 38 | */ | 36 | */ |
| 39 | class ProgramMetadata { | 37 | class ProgramMetadata { |
| 40 | public: | 38 | public: |
| 39 | ProgramMetadata(); | ||
| 40 | ~ProgramMetadata(); | ||
| 41 | |||
| 41 | Loader::ResultStatus Load(VirtualFile file); | 42 | Loader::ResultStatus Load(VirtualFile file); |
| 42 | 43 | ||
| 43 | bool Is64BitProgram() const; | 44 | bool Is64BitProgram() const; |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index dacf8568b..dad7ae10b 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -5,13 +5,17 @@ | |||
| 5 | #include <regex> | 5 | #include <regex> |
| 6 | #include <mbedtls/sha256.h> | 6 | #include <mbedtls/sha256.h> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/file_util.h" | ||
| 8 | #include "common/hex_util.h" | 9 | #include "common/hex_util.h" |
| 9 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 10 | #include "core/crypto/encryption_layer.h" | 11 | #include "core/crypto/key_manager.h" |
| 11 | #include "core/file_sys/card_image.h" | 12 | #include "core/file_sys/card_image.h" |
| 13 | #include "core/file_sys/content_archive.h" | ||
| 12 | #include "core/file_sys/nca_metadata.h" | 14 | #include "core/file_sys/nca_metadata.h" |
| 13 | #include "core/file_sys/registered_cache.h" | 15 | #include "core/file_sys/registered_cache.h" |
| 16 | #include "core/file_sys/submission_package.h" | ||
| 14 | #include "core/file_sys/vfs_concat.h" | 17 | #include "core/file_sys/vfs_concat.h" |
| 18 | #include "core/loader/loader.h" | ||
| 15 | 19 | ||
| 16 | namespace FileSys { | 20 | namespace FileSys { |
| 17 | std::string RegisteredCacheEntry::DebugInfo() const { | 21 | std::string RegisteredCacheEntry::DebugInfo() const { |
| @@ -58,11 +62,11 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { | |||
| 58 | "" ///< Currently unknown 'DeltaTitle' | 62 | "" ///< Currently unknown 'DeltaTitle' |
| 59 | }; | 63 | }; |
| 60 | 64 | ||
| 61 | auto index = static_cast<size_t>(type); | 65 | auto index = static_cast<std::size_t>(type); |
| 62 | // If the index is after the jump in TitleType, subtract it out. | 66 | // If the index is after the jump in TitleType, subtract it out. |
| 63 | if (index >= static_cast<size_t>(TitleType::Application)) { | 67 | if (index >= static_cast<std::size_t>(TitleType::Application)) { |
| 64 | index -= static_cast<size_t>(TitleType::Application) - | 68 | index -= static_cast<std::size_t>(TitleType::Application) - |
| 65 | static_cast<size_t>(TitleType::FirmwarePackageB); | 69 | static_cast<std::size_t>(TitleType::FirmwarePackageB); |
| 66 | } | 70 | } |
| 67 | return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); | 71 | return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); |
| 68 | } | 72 | } |
| @@ -101,7 +105,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | |||
| 101 | } else { | 105 | } else { |
| 102 | std::vector<VirtualFile> concat; | 106 | std::vector<VirtualFile> concat; |
| 103 | // Since the files are a two-digit hex number, max is FF. | 107 | // Since the files are a two-digit hex number, max is FF. |
| 104 | for (size_t i = 0; i < 0x100; ++i) { | 108 | for (std::size_t i = 0; i < 0x100; ++i) { |
| 105 | auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); | 109 | auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); |
| 106 | if (next != nullptr) { | 110 | if (next != nullptr) { |
| 107 | concat.push_back(std::move(next)); | 111 | concat.push_back(std::move(next)); |
| @@ -276,6 +280,18 @@ VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const | |||
| 276 | return GetEntryUnparsed(entry.title_id, entry.type); | 280 | return GetEntryUnparsed(entry.title_id, entry.type); |
| 277 | } | 281 | } |
| 278 | 282 | ||
| 283 | boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { | ||
| 284 | const auto meta_iter = meta.find(title_id); | ||
| 285 | if (meta_iter != meta.end()) | ||
| 286 | return meta_iter->second.GetTitleVersion(); | ||
| 287 | |||
| 288 | const auto yuzu_meta_iter = yuzu_meta.find(title_id); | ||
| 289 | if (yuzu_meta_iter != yuzu_meta.end()) | ||
| 290 | return yuzu_meta_iter->second.GetTitleVersion(); | ||
| 291 | |||
| 292 | return boost::none; | ||
| 293 | } | ||
| 294 | |||
| 279 | VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { | 295 | VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { |
| 280 | const auto id = GetNcaIDFromMetadata(title_id, type); | 296 | const auto id = GetNcaIDFromMetadata(title_id, type); |
| 281 | if (id == boost::none) | 297 | if (id == boost::none) |
| @@ -355,17 +371,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( | |||
| 355 | return out; | 371 | return out; |
| 356 | } | 372 | } |
| 357 | 373 | ||
| 358 | static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) { | 374 | static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) { |
| 359 | const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false)); | 375 | const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false))); |
| 360 | const auto iter = | 376 | if (file == nullptr) |
| 361 | std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(), | 377 | return nullptr; |
| 362 | [&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; }); | 378 | return std::make_shared<NCA>(file); |
| 363 | return iter == xci->GetNCAs().end() ? nullptr : *iter; | ||
| 364 | } | 379 | } |
| 365 | 380 | ||
| 366 | InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists, | 381 | InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists, |
| 367 | const VfsCopyFunction& copy) { | 382 | const VfsCopyFunction& copy) { |
| 368 | const auto& ncas = xci->GetNCAs(); | 383 | return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy); |
| 384 | } | ||
| 385 | |||
| 386 | InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists, | ||
| 387 | const VfsCopyFunction& copy) { | ||
| 388 | const auto& ncas = nsp->GetNCAsCollapsed(); | ||
| 369 | const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { | 389 | const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { |
| 370 | return nca->GetType() == NCAContentType::Meta; | 390 | return nca->GetType() == NCAContentType::Meta; |
| 371 | }); | 391 | }); |
| @@ -389,7 +409,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overw | |||
| 389 | const auto cnmt_file = section0->GetFiles()[0]; | 409 | const auto cnmt_file = section0->GetFiles()[0]; |
| 390 | const CNMT cnmt(cnmt_file); | 410 | const CNMT cnmt(cnmt_file); |
| 391 | for (const auto& record : cnmt.GetContentRecords()) { | 411 | for (const auto& record : cnmt.GetContentRecords()) { |
| 392 | const auto nca = GetNCAFromXCIForID(xci, record.nca_id); | 412 | const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); |
| 393 | if (nca == nullptr) | 413 | if (nca == nullptr) |
| 394 | return InstallResult::ErrorCopyFailed; | 414 | return InstallResult::ErrorCopyFailed; |
| 395 | const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); | 415 | const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); |
| @@ -490,4 +510,107 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { | |||
| 490 | kv.second.GetTitleID() == cnmt.GetTitleID(); | 510 | kv.second.GetTitleID() == cnmt.GetTitleID(); |
| 491 | }) != yuzu_meta.end(); | 511 | }) != yuzu_meta.end(); |
| 492 | } | 512 | } |
| 513 | |||
| 514 | RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) | ||
| 515 | : caches(std::move(caches)) {} | ||
| 516 | |||
| 517 | void RegisteredCacheUnion::Refresh() { | ||
| 518 | for (const auto& c : caches) | ||
| 519 | c->Refresh(); | ||
| 520 | } | ||
| 521 | |||
| 522 | bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const { | ||
| 523 | return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) { | ||
| 524 | return cache->HasEntry(title_id, type); | ||
| 525 | }); | ||
| 526 | } | ||
| 527 | |||
| 528 | bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { | ||
| 529 | return HasEntry(entry.title_id, entry.type); | ||
| 530 | } | ||
| 531 | |||
| 532 | boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { | ||
| 533 | for (const auto& c : caches) { | ||
| 534 | const auto res = c->GetEntryVersion(title_id); | ||
| 535 | if (res != boost::none) | ||
| 536 | return res; | ||
| 537 | } | ||
| 538 | |||
| 539 | return boost::none; | ||
| 540 | } | ||
| 541 | |||
| 542 | VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { | ||
| 543 | for (const auto& c : caches) { | ||
| 544 | const auto res = c->GetEntryUnparsed(title_id, type); | ||
| 545 | if (res != nullptr) | ||
| 546 | return res; | ||
| 547 | } | ||
| 548 | |||
| 549 | return nullptr; | ||
| 550 | } | ||
| 551 | |||
| 552 | VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const { | ||
| 553 | return GetEntryUnparsed(entry.title_id, entry.type); | ||
| 554 | } | ||
| 555 | |||
| 556 | VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { | ||
| 557 | for (const auto& c : caches) { | ||
| 558 | const auto res = c->GetEntryRaw(title_id, type); | ||
| 559 | if (res != nullptr) | ||
| 560 | return res; | ||
| 561 | } | ||
| 562 | |||
| 563 | return nullptr; | ||
| 564 | } | ||
| 565 | |||
| 566 | VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const { | ||
| 567 | return GetEntryRaw(entry.title_id, entry.type); | ||
| 568 | } | ||
| 569 | |||
| 570 | std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { | ||
| 571 | const auto raw = GetEntryRaw(title_id, type); | ||
| 572 | if (raw == nullptr) | ||
| 573 | return nullptr; | ||
| 574 | return std::make_shared<NCA>(raw); | ||
| 575 | } | ||
| 576 | |||
| 577 | std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { | ||
| 578 | return GetEntry(entry.title_id, entry.type); | ||
| 579 | } | ||
| 580 | |||
| 581 | std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { | ||
| 582 | std::vector<RegisteredCacheEntry> out; | ||
| 583 | for (const auto& c : caches) { | ||
| 584 | c->IterateAllMetadata<RegisteredCacheEntry>( | ||
| 585 | out, | ||
| 586 | [](const CNMT& c, const ContentRecord& r) { | ||
| 587 | return RegisteredCacheEntry{c.GetTitleID(), r.type}; | ||
| 588 | }, | ||
| 589 | [](const CNMT& c, const ContentRecord& r) { return true; }); | ||
| 590 | } | ||
| 591 | return out; | ||
| 592 | } | ||
| 593 | |||
| 594 | std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( | ||
| 595 | boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, | ||
| 596 | boost::optional<u64> title_id) const { | ||
| 597 | std::vector<RegisteredCacheEntry> out; | ||
| 598 | for (const auto& c : caches) { | ||
| 599 | c->IterateAllMetadata<RegisteredCacheEntry>( | ||
| 600 | out, | ||
| 601 | [](const CNMT& c, const ContentRecord& r) { | ||
| 602 | return RegisteredCacheEntry{c.GetTitleID(), r.type}; | ||
| 603 | }, | ||
| 604 | [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { | ||
| 605 | if (title_type != boost::none && title_type.get() != c.GetType()) | ||
| 606 | return false; | ||
| 607 | if (record_type != boost::none && record_type.get() != r.type) | ||
| 608 | return false; | ||
| 609 | if (title_id != boost::none && title_id.get() != c.GetTitleID()) | ||
| 610 | return false; | ||
| 611 | return true; | ||
| 612 | }); | ||
| 613 | } | ||
| 614 | return out; | ||
| 615 | } | ||
| 493 | } // namespace FileSys | 616 | } // namespace FileSys |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 7b8955dfa..f487b0cf0 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -11,15 +11,19 @@ | |||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | #include <boost/container/flat_map.hpp> | 13 | #include <boost/container/flat_map.hpp> |
| 14 | #include "common/common_funcs.h" | ||
| 15 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 16 | #include "content_archive.h" | ||
| 17 | #include "core/file_sys/nca_metadata.h" | ||
| 18 | #include "core/file_sys/vfs.h" | 15 | #include "core/file_sys/vfs.h" |
| 19 | 16 | ||
| 20 | namespace FileSys { | 17 | namespace FileSys { |
| 21 | class XCI; | ||
| 22 | class CNMT; | 18 | class CNMT; |
| 19 | class NCA; | ||
| 20 | class NSP; | ||
| 21 | class XCI; | ||
| 22 | |||
| 23 | enum class ContentRecordType : u8; | ||
| 24 | enum class TitleType : u8; | ||
| 25 | |||
| 26 | struct ContentRecord; | ||
| 23 | 27 | ||
| 24 | using NcaID = std::array<u8, 0x10>; | 28 | using NcaID = std::array<u8, 0x10>; |
| 25 | using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; | 29 | using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; |
| @@ -39,6 +43,10 @@ struct RegisteredCacheEntry { | |||
| 39 | std::string DebugInfo() const; | 43 | std::string DebugInfo() const; |
| 40 | }; | 44 | }; |
| 41 | 45 | ||
| 46 | constexpr u64 GetUpdateTitleID(u64 base_title_id) { | ||
| 47 | return base_title_id | 0x800; | ||
| 48 | } | ||
| 49 | |||
| 42 | // boost flat_map requires operator< for O(log(n)) lookups. | 50 | // boost flat_map requires operator< for O(log(n)) lookups. |
| 43 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | 51 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); |
| 44 | 52 | ||
| @@ -56,6 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) | |||
| 56 | * 4GB splitting can be ignored.) | 64 | * 4GB splitting can be ignored.) |
| 57 | */ | 65 | */ |
| 58 | class RegisteredCache { | 66 | class RegisteredCache { |
| 67 | friend class RegisteredCacheUnion; | ||
| 68 | |||
| 59 | public: | 69 | public: |
| 60 | // Parsing function defines the conversion from raw file to NCA. If there are other steps | 70 | // Parsing function defines the conversion from raw file to NCA. If there are other steps |
| 61 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | 71 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom |
| @@ -70,6 +80,8 @@ public: | |||
| 70 | bool HasEntry(u64 title_id, ContentRecordType type) const; | 80 | bool HasEntry(u64 title_id, ContentRecordType type) const; |
| 71 | bool HasEntry(RegisteredCacheEntry entry) const; | 81 | bool HasEntry(RegisteredCacheEntry entry) const; |
| 72 | 82 | ||
| 83 | boost::optional<u32> GetEntryVersion(u64 title_id) const; | ||
| 84 | |||
| 73 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; | 85 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; |
| 74 | VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; | 86 | VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; |
| 75 | 87 | ||
| @@ -86,10 +98,12 @@ public: | |||
| 86 | boost::optional<ContentRecordType> record_type = boost::none, | 98 | boost::optional<ContentRecordType> record_type = boost::none, |
| 87 | boost::optional<u64> title_id = boost::none) const; | 99 | boost::optional<u64> title_id = boost::none) const; |
| 88 | 100 | ||
| 89 | // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there | 101 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure |
| 90 | // is a meta NCA and all of them are accessible. | 102 | // there is a meta NCA and all of them are accessible. |
| 91 | InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false, | 103 | InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false, |
| 92 | const VfsCopyFunction& copy = &VfsRawCopy); | 104 | const VfsCopyFunction& copy = &VfsRawCopy); |
| 105 | InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false, | ||
| 106 | const VfsCopyFunction& copy = &VfsRawCopy); | ||
| 93 | 107 | ||
| 94 | // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this | 108 | // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this |
| 95 | // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a | 109 | // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a |
| @@ -125,4 +139,36 @@ private: | |||
| 125 | boost::container::flat_map<u64, CNMT> yuzu_meta; | 139 | boost::container::flat_map<u64, CNMT> yuzu_meta; |
| 126 | }; | 140 | }; |
| 127 | 141 | ||
| 142 | // Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. | ||
| 143 | class RegisteredCacheUnion { | ||
| 144 | public: | ||
| 145 | explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); | ||
| 146 | |||
| 147 | void Refresh(); | ||
| 148 | |||
| 149 | bool HasEntry(u64 title_id, ContentRecordType type) const; | ||
| 150 | bool HasEntry(RegisteredCacheEntry entry) const; | ||
| 151 | |||
| 152 | boost::optional<u32> GetEntryVersion(u64 title_id) const; | ||
| 153 | |||
| 154 | VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; | ||
| 155 | VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; | ||
| 156 | |||
| 157 | VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; | ||
| 158 | VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; | ||
| 159 | |||
| 160 | std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; | ||
| 161 | std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; | ||
| 162 | |||
| 163 | std::vector<RegisteredCacheEntry> ListEntries() const; | ||
| 164 | // If a parameter is not boost::none, it will be filtered for from all entries. | ||
| 165 | std::vector<RegisteredCacheEntry> ListEntriesFilter( | ||
| 166 | boost::optional<TitleType> title_type = boost::none, | ||
| 167 | boost::optional<ContentRecordType> record_type = boost::none, | ||
| 168 | boost::optional<u64> title_id = boost::none) const; | ||
| 169 | |||
| 170 | private: | ||
| 171 | std::vector<std::shared_ptr<RegisteredCache>> caches; | ||
| 172 | }; | ||
| 173 | |||
| 128 | } // namespace FileSys | 174 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index e490c8ace..9f6e41cdf 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -49,7 +49,7 @@ struct FileEntry { | |||
| 49 | static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); | 49 | static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); |
| 50 | 50 | ||
| 51 | template <typename Entry> | 51 | template <typename Entry> |
| 52 | static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { | 52 | static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) { |
| 53 | Entry entry{}; | 53 | Entry entry{}; |
| 54 | if (file->ReadObject(&entry, offset) != sizeof(Entry)) | 54 | if (file->ReadObject(&entry, offset) != sizeof(Entry)) |
| 55 | return {}; | 55 | return {}; |
| @@ -59,8 +59,8 @@ static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t of | |||
| 59 | return {entry, string}; | 59 | return {entry, string}; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, | 62 | void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, |
| 63 | std::shared_ptr<VectorVfsDirectory> parent) { | 63 | u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) { |
| 64 | while (true) { | 64 | while (true) { |
| 65 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); | 65 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); |
| 66 | 66 | ||
| @@ -74,8 +74,9 @@ void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 t | |||
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, | 77 | void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, |
| 78 | u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) { | 78 | std::size_t data_offset, u32 this_dir_offset, |
| 79 | std::shared_ptr<VectorVfsDirectory> parent) { | ||
| 79 | while (true) { | 80 | while (true) { |
| 80 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); | 81 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); |
| 81 | auto current = std::make_shared<VectorVfsDirectory>( | 82 | auto current = std::make_shared<VectorVfsDirectory>( |
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 03a876d22..e54a7d7a9 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 10 | #include "core/file_sys/vfs.h" | 11 | #include "core/file_sys/vfs.h" |
| 11 | 12 | ||
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index eb4e6c865..3d1a3685e 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -2,11 +2,14 @@ | |||
| 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 <memory> | 5 | #include <memory> |
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/content_archive.h" | ||
| 9 | #include "core/file_sys/nca_metadata.h" | 11 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | ||
| 10 | #include "core/file_sys/registered_cache.h" | 13 | #include "core/file_sys/registered_cache.h" |
| 11 | #include "core/file_sys/romfs_factory.h" | 14 | #include "core/file_sys/romfs_factory.h" |
| 12 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| @@ -20,10 +23,19 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { | |||
| 20 | if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { | 23 | if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { |
| 21 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 24 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| 22 | } | 25 | } |
| 26 | |||
| 27 | updatable = app_loader.IsRomFSUpdatable(); | ||
| 28 | ivfc_offset = app_loader.ReadRomFSIVFCOffset(); | ||
| 23 | } | 29 | } |
| 24 | 30 | ||
| 31 | RomFSFactory::~RomFSFactory() = default; | ||
| 32 | |||
| 25 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | 33 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { |
| 26 | return MakeResult<VirtualFile>(file); | 34 | if (!updatable) |
| 35 | return MakeResult<VirtualFile>(file); | ||
| 36 | |||
| 37 | const PatchManager patch_manager(Core::CurrentProcess()->program_id); | ||
| 38 | return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); | ||
| 27 | } | 39 | } |
| 28 | 40 | ||
| 29 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { | 41 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index f38ddc4f7..2cace8180 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -30,12 +30,15 @@ enum class StorageId : u8 { | |||
| 30 | class RomFSFactory { | 30 | class RomFSFactory { |
| 31 | public: | 31 | public: |
| 32 | explicit RomFSFactory(Loader::AppLoader& app_loader); | 32 | explicit RomFSFactory(Loader::AppLoader& app_loader); |
| 33 | ~RomFSFactory(); | ||
| 33 | 34 | ||
| 34 | ResultVal<VirtualFile> OpenCurrentProcess(); | 35 | ResultVal<VirtualFile> OpenCurrentProcess(); |
| 35 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); | 36 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); |
| 36 | 37 | ||
| 37 | private: | 38 | private: |
| 38 | VirtualFile file; | 39 | VirtualFile file; |
| 40 | bool updatable; | ||
| 41 | u64 ivfc_offset; | ||
| 39 | }; | 42 | }; |
| 40 | 43 | ||
| 41 | } // namespace FileSys | 44 | } // namespace FileSys |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 952bd74b3..9b2c51bbd 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 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 "common/assert.h" | ||
| 6 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| @@ -19,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const { | |||
| 19 | 20 | ||
| 20 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} | 21 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} |
| 21 | 22 | ||
| 23 | SaveDataFactory::~SaveDataFactory() = default; | ||
| 24 | |||
| 22 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { | 25 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { |
| 23 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 26 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 24 | if (meta.zero_1 != 0) { | 27 | if (meta.zero_1 != 0) { |
| @@ -84,10 +87,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 84 | 87 | ||
| 85 | switch (space) { | 88 | switch (space) { |
| 86 | case SaveDataSpaceId::NandSystem: | 89 | case SaveDataSpaceId::NandSystem: |
| 87 | out = "/system/save/"; | 90 | out = "/system/"; |
| 88 | break; | 91 | break; |
| 89 | case SaveDataSpaceId::NandUser: | 92 | case SaveDataSpaceId::NandUser: |
| 90 | out = "/user/save/"; | 93 | out = "/user/"; |
| 91 | break; | 94 | break; |
| 92 | default: | 95 | default: |
| 93 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); | 96 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); |
| @@ -95,9 +98,12 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 95 | 98 | ||
| 96 | switch (type) { | 99 | switch (type) { |
| 97 | case SaveDataType::SystemSaveData: | 100 | case SaveDataType::SystemSaveData: |
| 98 | return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); | 101 | return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); |
| 99 | case SaveDataType::SaveData: | 102 | case SaveDataType::SaveData: |
| 100 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], | 103 | return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], |
| 104 | title_id); | ||
| 105 | case SaveDataType::TemporaryStorage: | ||
| 106 | return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], | ||
| 101 | title_id); | 107 | title_id); |
| 102 | default: | 108 | default: |
| 103 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); | 109 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index c6f9549f0..d69ef6741 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 11 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| @@ -47,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr | |||
| 47 | class SaveDataFactory { | 48 | class SaveDataFactory { |
| 48 | public: | 49 | public: |
| 49 | explicit SaveDataFactory(VirtualDir dir); | 50 | explicit SaveDataFactory(VirtualDir dir); |
| 51 | ~SaveDataFactory(); | ||
| 50 | 52 | ||
| 51 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); | 53 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); |
| 52 | 54 | ||
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp new file mode 100644 index 000000000..11264878d --- /dev/null +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -0,0 +1,245 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <string_view> | ||
| 8 | |||
| 9 | #include <fmt/ostream.h> | ||
| 10 | |||
| 11 | #include "common/hex_util.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "core/crypto/key_manager.h" | ||
| 14 | #include "core/file_sys/content_archive.h" | ||
| 15 | #include "core/file_sys/nca_metadata.h" | ||
| 16 | #include "core/file_sys/partition_filesystem.h" | ||
| 17 | #include "core/file_sys/submission_package.h" | ||
| 18 | #include "core/loader/loader.h" | ||
| 19 | |||
| 20 | namespace FileSys { | ||
| 21 | NSP::NSP(VirtualFile file_) | ||
| 22 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, | ||
| 23 | pfs(std::make_shared<PartitionFilesystem>(file)) { | ||
| 24 | if (pfs->GetStatus() != Loader::ResultStatus::Success) { | ||
| 25 | status = pfs->GetStatus(); | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | |||
| 29 | if (IsDirectoryExeFS(pfs)) { | ||
| 30 | extracted = true; | ||
| 31 | exefs = pfs; | ||
| 32 | |||
| 33 | const auto& files = pfs->GetFiles(); | ||
| 34 | const auto romfs_iter = | ||
| 35 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||
| 36 | return file->GetName().find(".romfs") != std::string::npos; | ||
| 37 | }); | ||
| 38 | if (romfs_iter != files.end()) | ||
| 39 | romfs = *romfs_iter; | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | |||
| 43 | extracted = false; | ||
| 44 | const auto files = pfs->GetFiles(); | ||
| 45 | |||
| 46 | Core::Crypto::KeyManager keys; | ||
| 47 | for (const auto& ticket_file : files) { | ||
| 48 | if (ticket_file->GetExtension() == "tik") { | ||
| 49 | if (ticket_file == nullptr || | ||
| 50 | ticket_file->GetSize() < | ||
| 51 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 52 | continue; | ||
| 53 | } | ||
| 54 | |||
| 55 | Core::Crypto::Key128 key{}; | ||
| 56 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 57 | std::string_view name_only(ticket_file->GetName()); | ||
| 58 | name_only.remove_suffix(4); | ||
| 59 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 60 | u128 rights_id; | ||
| 61 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 62 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | for (const auto& outer_file : files) { | ||
| 67 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { | ||
| 68 | const auto nca = std::make_shared<NCA>(outer_file); | ||
| 69 | if (nca->GetStatus() != Loader::ResultStatus::Success) { | ||
| 70 | program_status[nca->GetTitleId()] = nca->GetStatus(); | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | |||
| 74 | const auto section0 = nca->GetSubdirectories()[0]; | ||
| 75 | |||
| 76 | for (const auto& inner_file : section0->GetFiles()) { | ||
| 77 | if (inner_file->GetExtension() != "cnmt") | ||
| 78 | continue; | ||
| 79 | |||
| 80 | const CNMT cnmt(inner_file); | ||
| 81 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 82 | |||
| 83 | ncas_title[ContentRecordType::Meta] = nca; | ||
| 84 | for (const auto& rec : cnmt.GetContentRecords()) { | ||
| 85 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | ||
| 86 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | ||
| 87 | if (next_file == nullptr) { | ||
| 88 | LOG_WARNING(Service_FS, | ||
| 89 | "NCA with ID {}.nca is listed in content metadata, but cannot " | ||
| 90 | "be found in PFS. NSP appears to be corrupted.", | ||
| 91 | id_string); | ||
| 92 | continue; | ||
| 93 | } | ||
| 94 | |||
| 95 | auto next_nca = std::make_shared<NCA>(next_file); | ||
| 96 | if (next_nca->GetType() == NCAContentType::Program) | ||
| 97 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | ||
| 98 | if (next_nca->GetStatus() == Loader::ResultStatus::Success) | ||
| 99 | ncas_title[rec.type] = std::move(next_nca); | ||
| 100 | } | ||
| 101 | |||
| 102 | break; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | NSP::~NSP() = default; | ||
| 109 | |||
| 110 | Loader::ResultStatus NSP::GetStatus() const { | ||
| 111 | return status; | ||
| 112 | } | ||
| 113 | |||
| 114 | Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { | ||
| 115 | const auto iter = program_status.find(title_id); | ||
| 116 | if (iter == program_status.end()) | ||
| 117 | return Loader::ResultStatus::ErrorNSPMissingProgramNCA; | ||
| 118 | return iter->second; | ||
| 119 | } | ||
| 120 | |||
| 121 | u64 NSP::GetFirstTitleID() const { | ||
| 122 | if (program_status.empty()) | ||
| 123 | return 0; | ||
| 124 | return program_status.begin()->first; | ||
| 125 | } | ||
| 126 | |||
| 127 | u64 NSP::GetProgramTitleID() const { | ||
| 128 | const auto out = GetFirstTitleID(); | ||
| 129 | if ((out & 0x800) == 0) | ||
| 130 | return out; | ||
| 131 | |||
| 132 | const auto ids = GetTitleIDs(); | ||
| 133 | const auto iter = | ||
| 134 | std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); | ||
| 135 | return iter == ids.end() ? out : *iter; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::vector<u64> NSP::GetTitleIDs() const { | ||
| 139 | std::vector<u64> out; | ||
| 140 | out.reserve(ncas.size()); | ||
| 141 | for (const auto& kv : ncas) | ||
| 142 | out.push_back(kv.first); | ||
| 143 | return out; | ||
| 144 | } | ||
| 145 | |||
| 146 | bool NSP::IsExtractedType() const { | ||
| 147 | return extracted; | ||
| 148 | } | ||
| 149 | |||
| 150 | VirtualFile NSP::GetRomFS() const { | ||
| 151 | return romfs; | ||
| 152 | } | ||
| 153 | |||
| 154 | VirtualDir NSP::GetExeFS() const { | ||
| 155 | return exefs; | ||
| 156 | } | ||
| 157 | |||
| 158 | std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const { | ||
| 159 | if (extracted) | ||
| 160 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 161 | std::vector<std::shared_ptr<NCA>> out; | ||
| 162 | for (const auto& map : ncas) { | ||
| 163 | for (const auto& inner_map : map.second) | ||
| 164 | out.push_back(inner_map.second); | ||
| 165 | } | ||
| 166 | return out; | ||
| 167 | } | ||
| 168 | |||
| 169 | std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const { | ||
| 170 | if (extracted) | ||
| 171 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 172 | std::multimap<u64, std::shared_ptr<NCA>> out; | ||
| 173 | for (const auto& map : ncas) { | ||
| 174 | for (const auto& inner_map : map.second) | ||
| 175 | out.emplace(map.first, inner_map.second); | ||
| 176 | } | ||
| 177 | return out; | ||
| 178 | } | ||
| 179 | |||
| 180 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const { | ||
| 181 | return ncas; | ||
| 182 | } | ||
| 183 | |||
| 184 | std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { | ||
| 185 | if (extracted) | ||
| 186 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 187 | |||
| 188 | const auto title_id_iter = ncas.find(title_id); | ||
| 189 | if (title_id_iter == ncas.end()) | ||
| 190 | return nullptr; | ||
| 191 | |||
| 192 | const auto type_iter = title_id_iter->second.find(type); | ||
| 193 | if (type_iter == title_id_iter->second.end()) | ||
| 194 | return nullptr; | ||
| 195 | |||
| 196 | return type_iter->second; | ||
| 197 | } | ||
| 198 | |||
| 199 | VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { | ||
| 200 | if (extracted) | ||
| 201 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 202 | const auto nca = GetNCA(title_id, type); | ||
| 203 | if (nca != nullptr) | ||
| 204 | return nca->GetBaseFile(); | ||
| 205 | return nullptr; | ||
| 206 | } | ||
| 207 | |||
| 208 | std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const { | ||
| 209 | if (extracted) | ||
| 210 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 211 | std::vector<Core::Crypto::Key128> out; | ||
| 212 | for (const auto& ticket_file : ticket_files) { | ||
| 213 | if (ticket_file == nullptr || | ||
| 214 | ticket_file->GetSize() < | ||
| 215 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 216 | continue; | ||
| 217 | } | ||
| 218 | |||
| 219 | out.emplace_back(); | ||
| 220 | ticket_file->Read(out.back().data(), out.back().size(), | ||
| 221 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 222 | } | ||
| 223 | return out; | ||
| 224 | } | ||
| 225 | |||
| 226 | std::vector<VirtualFile> NSP::GetFiles() const { | ||
| 227 | return pfs->GetFiles(); | ||
| 228 | } | ||
| 229 | |||
| 230 | std::vector<VirtualDir> NSP::GetSubdirectories() const { | ||
| 231 | return pfs->GetSubdirectories(); | ||
| 232 | } | ||
| 233 | |||
| 234 | std::string NSP::GetName() const { | ||
| 235 | return file->GetName(); | ||
| 236 | } | ||
| 237 | |||
| 238 | VirtualDir NSP::GetParentDirectory() const { | ||
| 239 | return file->GetContainingDirectory(); | ||
| 240 | } | ||
| 241 | |||
| 242 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 243 | return false; | ||
| 244 | } | ||
| 245 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h new file mode 100644 index 000000000..e85a2b76e --- /dev/null +++ b/src/core/file_sys/submission_package.h | |||
| @@ -0,0 +1,76 @@ | |||
| 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 <map> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/file_sys/vfs.h" | ||
| 12 | |||
| 13 | namespace Loader { | ||
| 14 | enum class ResultStatus : u16; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class NCA; | ||
| 20 | class PartitionFilesystem; | ||
| 21 | |||
| 22 | enum class ContentRecordType : u8; | ||
| 23 | |||
| 24 | class NSP : public ReadOnlyVfsDirectory { | ||
| 25 | public: | ||
| 26 | explicit NSP(VirtualFile file); | ||
| 27 | ~NSP() override; | ||
| 28 | |||
| 29 | Loader::ResultStatus GetStatus() const; | ||
| 30 | Loader::ResultStatus GetProgramStatus(u64 title_id) const; | ||
| 31 | // Should only be used when one title id can be assured. | ||
| 32 | u64 GetFirstTitleID() const; | ||
| 33 | u64 GetProgramTitleID() const; | ||
| 34 | std::vector<u64> GetTitleIDs() const; | ||
| 35 | |||
| 36 | bool IsExtractedType() const; | ||
| 37 | |||
| 38 | // Common (Can be safely called on both types) | ||
| 39 | VirtualFile GetRomFS() const; | ||
| 40 | VirtualDir GetExeFS() const; | ||
| 41 | |||
| 42 | // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) | ||
| 43 | std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const; | ||
| 44 | std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; | ||
| 45 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const; | ||
| 46 | std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const; | ||
| 47 | VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; | ||
| 48 | std::vector<Core::Crypto::Key128> GetTitlekey() const; | ||
| 49 | |||
| 50 | std::vector<VirtualFile> GetFiles() const override; | ||
| 51 | |||
| 52 | std::vector<VirtualDir> GetSubdirectories() const override; | ||
| 53 | |||
| 54 | std::string GetName() const override; | ||
| 55 | |||
| 56 | VirtualDir GetParentDirectory() const override; | ||
| 57 | |||
| 58 | protected: | ||
| 59 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 60 | |||
| 61 | private: | ||
| 62 | VirtualFile file; | ||
| 63 | |||
| 64 | bool extracted; | ||
| 65 | Loader::ResultStatus status; | ||
| 66 | std::map<u64, Loader::ResultStatus> program_status; | ||
| 67 | |||
| 68 | std::shared_ptr<PartitionFilesystem> pfs; | ||
| 69 | // Map title id -> {map type -> NCA} | ||
| 70 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; | ||
| 71 | std::vector<VirtualFile> ticket_files; | ||
| 72 | |||
| 73 | VirtualFile romfs; | ||
| 74 | VirtualDir exefs; | ||
| 75 | }; | ||
| 76 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 146c839f4..d7b52abfd 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -167,18 +167,18 @@ std::string VfsFile::GetExtension() const { | |||
| 167 | 167 | ||
| 168 | VfsDirectory::~VfsDirectory() = default; | 168 | VfsDirectory::~VfsDirectory() = default; |
| 169 | 169 | ||
| 170 | boost::optional<u8> VfsFile::ReadByte(size_t offset) const { | 170 | boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const { |
| 171 | u8 out{}; | 171 | u8 out{}; |
| 172 | size_t size = Read(&out, 1, offset); | 172 | std::size_t size = Read(&out, 1, offset); |
| 173 | if (size == 1) | 173 | if (size == 1) |
| 174 | return out; | 174 | return out; |
| 175 | 175 | ||
| 176 | return boost::none; | 176 | return boost::none; |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { | 179 | std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { |
| 180 | std::vector<u8> out(size); | 180 | std::vector<u8> out(size); |
| 181 | size_t read_size = Read(out.data(), size, offset); | 181 | std::size_t read_size = Read(out.data(), size, offset); |
| 182 | out.resize(read_size); | 182 | out.resize(read_size); |
| 183 | return out; | 183 | return out; |
| 184 | } | 184 | } |
| @@ -187,11 +187,11 @@ std::vector<u8> VfsFile::ReadAllBytes() const { | |||
| 187 | return ReadBytes(GetSize()); | 187 | return ReadBytes(GetSize()); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | bool VfsFile::WriteByte(u8 data, size_t offset) { | 190 | bool VfsFile::WriteByte(u8 data, std::size_t offset) { |
| 191 | return Write(&data, 1, offset) == 1; | 191 | return Write(&data, 1, offset) == 1; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) { | 194 | std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) { |
| 195 | return Write(data.data(), data.size(), offset); | 195 | return Write(data.data(), data.size(), offset); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| @@ -215,7 +215,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) co | |||
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | auto dir = GetSubdirectory(vec[0]); | 217 | auto dir = GetSubdirectory(vec[0]); |
| 218 | for (size_t component = 1; component < vec.size() - 1; ++component) { | 218 | for (std::size_t component = 1; component < vec.size() - 1; ++component) { |
| 219 | if (dir == nullptr) { | 219 | if (dir == nullptr) { |
| 220 | return nullptr; | 220 | return nullptr; |
| 221 | } | 221 | } |
| @@ -249,7 +249,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_vie | |||
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | auto dir = GetSubdirectory(vec[0]); | 251 | auto dir = GetSubdirectory(vec[0]); |
| 252 | for (size_t component = 1; component < vec.size(); ++component) { | 252 | for (std::size_t component = 1; component < vec.size(); ++component) { |
| 253 | if (dir == nullptr) { | 253 | if (dir == nullptr) { |
| 254 | return nullptr; | 254 | return nullptr; |
| 255 | } | 255 | } |
| @@ -286,7 +286,7 @@ bool VfsDirectory::IsRoot() const { | |||
| 286 | return GetParentDirectory() == nullptr; | 286 | return GetParentDirectory() == nullptr; |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | size_t VfsDirectory::GetSize() const { | 289 | std::size_t VfsDirectory::GetSize() const { |
| 290 | const auto& files = GetFiles(); | 290 | const auto& files = GetFiles(); |
| 291 | const auto sum_sizes = [](const auto& range) { | 291 | const auto sum_sizes = [](const auto& range) { |
| 292 | return std::accumulate(range.begin(), range.end(), 0ULL, | 292 | return std::accumulate(range.begin(), range.end(), 0ULL, |
| @@ -434,13 +434,13 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) { | |||
| 434 | return false; | 434 | return false; |
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { | 437 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { |
| 438 | if (file1->GetSize() != file2->GetSize()) | 438 | if (file1->GetSize() != file2->GetSize()) |
| 439 | return false; | 439 | return false; |
| 440 | 440 | ||
| 441 | std::vector<u8> f1_v(block_size); | 441 | std::vector<u8> f1_v(block_size); |
| 442 | std::vector<u8> f2_v(block_size); | 442 | std::vector<u8> f2_v(block_size); |
| 443 | for (size_t i = 0; i < file1->GetSize(); i += block_size) { | 443 | for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { |
| 444 | auto f1_vs = file1->Read(f1_v.data(), block_size, i); | 444 | auto f1_vs = file1->Read(f1_v.data(), block_size, i); |
| 445 | auto f2_vs = file2->Read(f2_v.data(), block_size, i); | 445 | auto f2_vs = file2->Read(f2_v.data(), block_size, i); |
| 446 | 446 | ||
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 5142a3e86..74489b452 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -92,9 +92,9 @@ public: | |||
| 92 | // Retrieves the extension of the file name. | 92 | // Retrieves the extension of the file name. |
| 93 | virtual std::string GetExtension() const; | 93 | virtual std::string GetExtension() const; |
| 94 | // Retrieves the size of the file. | 94 | // Retrieves the size of the file. |
| 95 | virtual size_t GetSize() const = 0; | 95 | virtual std::size_t GetSize() const = 0; |
| 96 | // Resizes the file to new_size. Returns whether or not the operation was successful. | 96 | // Resizes the file to new_size. Returns whether or not the operation was successful. |
| 97 | virtual bool Resize(size_t new_size) = 0; | 97 | virtual bool Resize(std::size_t new_size) = 0; |
| 98 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. | 98 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. |
| 99 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; | 99 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; |
| 100 | 100 | ||
| @@ -105,15 +105,15 @@ public: | |||
| 105 | 105 | ||
| 106 | // The primary method of reading from the file. Reads length bytes into data starting at offset | 106 | // The primary method of reading from the file. Reads length bytes into data starting at offset |
| 107 | // into file. Returns number of bytes successfully read. | 107 | // into file. Returns number of bytes successfully read. |
| 108 | virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; | 108 | virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; |
| 109 | // The primary method of writing to the file. Writes length bytes from data starting at offset | 109 | // The primary method of writing to the file. Writes length bytes from data starting at offset |
| 110 | // into file. Returns number of bytes successfully written. | 110 | // into file. Returns number of bytes successfully written. |
| 111 | virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; | 111 | virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; |
| 112 | 112 | ||
| 113 | // Reads exactly one byte at the offset provided, returning boost::none on error. | 113 | // Reads exactly one byte at the offset provided, returning boost::none on error. |
| 114 | virtual boost::optional<u8> ReadByte(size_t offset = 0) const; | 114 | virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const; |
| 115 | // Reads size bytes starting at offset in file into a vector. | 115 | // Reads size bytes starting at offset in file into a vector. |
| 116 | virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; | 116 | virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; |
| 117 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), | 117 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), |
| 118 | // 0)' | 118 | // 0)' |
| 119 | virtual std::vector<u8> ReadAllBytes() const; | 119 | virtual std::vector<u8> ReadAllBytes() const; |
| @@ -121,7 +121,7 @@ public: | |||
| 121 | // Reads an array of type T, size number_elements starting at offset. | 121 | // Reads an array of type T, size number_elements starting at offset. |
| 122 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. | 122 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. |
| 123 | template <typename T> | 123 | template <typename T> |
| 124 | size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { | 124 | std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { |
| 125 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 125 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 126 | 126 | ||
| 127 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); | 127 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); |
| @@ -130,7 +130,7 @@ public: | |||
| 130 | // Reads size bytes into the memory starting at data starting at offset into the file. | 130 | // Reads size bytes into the memory starting at data starting at offset into the file. |
| 131 | // Returns the number of bytes read successfully. | 131 | // Returns the number of bytes read successfully. |
| 132 | template <typename T> | 132 | template <typename T> |
| 133 | size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { | 133 | std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { |
| 134 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 134 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 135 | return Read(reinterpret_cast<u8*>(data), size, offset); | 135 | return Read(reinterpret_cast<u8*>(data), size, offset); |
| 136 | } | 136 | } |
| @@ -138,22 +138,22 @@ public: | |||
| 138 | // Reads one object of type T starting at offset in file. | 138 | // Reads one object of type T starting at offset in file. |
| 139 | // Returns the number of bytes read successfully (sizeof(T)). | 139 | // Returns the number of bytes read successfully (sizeof(T)). |
| 140 | template <typename T> | 140 | template <typename T> |
| 141 | size_t ReadObject(T* data, size_t offset = 0) const { | 141 | std::size_t ReadObject(T* data, std::size_t offset = 0) const { |
| 142 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 142 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 143 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); | 143 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written | 146 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written |
| 147 | // successfully. | 147 | // successfully. |
| 148 | virtual bool WriteByte(u8 data, size_t offset = 0); | 148 | virtual bool WriteByte(u8 data, std::size_t offset = 0); |
| 149 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully | 149 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully |
| 150 | // written. | 150 | // written. |
| 151 | virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0); | 151 | virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0); |
| 152 | 152 | ||
| 153 | // Writes an array of type T, size number_elements to offset in file. | 153 | // Writes an array of type T, size number_elements to offset in file. |
| 154 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. | 154 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. |
| 155 | template <typename T> | 155 | template <typename T> |
| 156 | size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) { | 156 | std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { |
| 157 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 157 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 158 | return Write(data, number_elements * sizeof(T), offset); | 158 | return Write(data, number_elements * sizeof(T), offset); |
| 159 | } | 159 | } |
| @@ -161,7 +161,7 @@ public: | |||
| 161 | // Writes size bytes starting at memory location data to offset in file. | 161 | // Writes size bytes starting at memory location data to offset in file. |
| 162 | // Returns the number of bytes written successfully. | 162 | // Returns the number of bytes written successfully. |
| 163 | template <typename T> | 163 | template <typename T> |
| 164 | size_t WriteBytes(const T* data, size_t size, size_t offset = 0) { | 164 | std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { |
| 165 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 165 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 166 | return Write(reinterpret_cast<const u8*>(data), size, offset); | 166 | return Write(reinterpret_cast<const u8*>(data), size, offset); |
| 167 | } | 167 | } |
| @@ -169,7 +169,7 @@ public: | |||
| 169 | // Writes one object of type T to offset in file. | 169 | // Writes one object of type T to offset in file. |
| 170 | // Returns the number of bytes written successfully (sizeof(T)). | 170 | // Returns the number of bytes written successfully (sizeof(T)). |
| 171 | template <typename T> | 171 | template <typename T> |
| 172 | size_t WriteObject(const T& data, size_t offset = 0) { | 172 | std::size_t WriteObject(const T& data, std::size_t offset = 0) { |
| 173 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 173 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 174 | return Write(&data, sizeof(T), offset); | 174 | return Write(&data, sizeof(T), offset); |
| 175 | } | 175 | } |
| @@ -221,7 +221,7 @@ public: | |||
| 221 | // Returns the name of the directory. | 221 | // Returns the name of the directory. |
| 222 | virtual std::string GetName() const = 0; | 222 | virtual std::string GetName() const = 0; |
| 223 | // Returns the total size of all files and subdirectories in this directory. | 223 | // Returns the total size of all files and subdirectories in this directory. |
| 224 | virtual size_t GetSize() const; | 224 | virtual std::size_t GetSize() const; |
| 225 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or | 225 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or |
| 226 | // has no parent. | 226 | // has no parent. |
| 227 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; | 227 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; |
| @@ -311,7 +311,7 @@ public: | |||
| 311 | }; | 311 | }; |
| 312 | 312 | ||
| 313 | // Compare the two files, byte-for-byte, in increments specificed by block_size | 313 | // Compare the two files, byte-for-byte, in increments specificed by block_size |
| 314 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); | 314 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200); |
| 315 | 315 | ||
| 316 | // A method that copies the raw data between two different implementations of VirtualFile. If you | 316 | // A method that copies the raw data between two different implementations of VirtualFile. If you |
| 317 | // are using the same implementation, it is probably better to use the Copy method in the parent | 317 | // are using the same implementation, it is probably better to use the Copy method in the parent |
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index e6bf586a3..dc7a279a9 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp | |||
| @@ -20,13 +20,15 @@ VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) { | |||
| 20 | 20 | ||
| 21 | ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) | 21 | ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) |
| 22 | : name(std::move(name)) { | 22 | : name(std::move(name)) { |
| 23 | size_t next_offset = 0; | 23 | std::size_t next_offset = 0; |
| 24 | for (const auto& file : files_) { | 24 | for (const auto& file : files_) { |
| 25 | files[next_offset] = file; | 25 | files[next_offset] = file; |
| 26 | next_offset += file->GetSize(); | 26 | next_offset += file->GetSize(); |
| 27 | } | 27 | } |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; | ||
| 31 | |||
| 30 | std::string ConcatenatedVfsFile::GetName() const { | 32 | std::string ConcatenatedVfsFile::GetName() const { |
| 31 | if (files.empty()) | 33 | if (files.empty()) |
| 32 | return ""; | 34 | return ""; |
| @@ -35,13 +37,13 @@ std::string ConcatenatedVfsFile::GetName() const { | |||
| 35 | return files.begin()->second->GetName(); | 37 | return files.begin()->second->GetName(); |
| 36 | } | 38 | } |
| 37 | 39 | ||
| 38 | size_t ConcatenatedVfsFile::GetSize() const { | 40 | std::size_t ConcatenatedVfsFile::GetSize() const { |
| 39 | if (files.empty()) | 41 | if (files.empty()) |
| 40 | return 0; | 42 | return 0; |
| 41 | return files.rbegin()->first + files.rbegin()->second->GetSize(); | 43 | return files.rbegin()->first + files.rbegin()->second->GetSize(); |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | bool ConcatenatedVfsFile::Resize(size_t new_size) { | 46 | bool ConcatenatedVfsFile::Resize(std::size_t new_size) { |
| 45 | return false; | 47 | return false; |
| 46 | } | 48 | } |
| 47 | 49 | ||
| @@ -59,7 +61,7 @@ bool ConcatenatedVfsFile::IsReadable() const { | |||
| 59 | return true; | 61 | return true; |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { | 64 | std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 63 | auto entry = files.end(); | 65 | auto entry = files.end(); |
| 64 | for (auto iter = files.begin(); iter != files.end(); ++iter) { | 66 | for (auto iter = files.begin(); iter != files.end(); ++iter) { |
| 65 | if (iter->first > offset) { | 67 | if (iter->first > offset) { |
| @@ -84,7 +86,7 @@ size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { | |||
| 84 | return entry->second->Read(data, length, offset - entry->first); | 86 | return entry->second->Read(data, length, offset - entry->first); |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 87 | size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) { | 89 | std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 88 | return 0; | 90 | return 0; |
| 89 | } | 91 | } |
| 90 | 92 | ||
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h index 686d32515..717d04bdc 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs_concat.h | |||
| @@ -22,14 +22,16 @@ class ConcatenatedVfsFile : public VfsFile { | |||
| 22 | ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); | 22 | ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); |
| 23 | 23 | ||
| 24 | public: | 24 | public: |
| 25 | ~ConcatenatedVfsFile() override; | ||
| 26 | |||
| 25 | std::string GetName() const override; | 27 | std::string GetName() const override; |
| 26 | size_t GetSize() const override; | 28 | std::size_t GetSize() const override; |
| 27 | bool Resize(size_t new_size) override; | 29 | bool Resize(std::size_t new_size) override; |
| 28 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 30 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 29 | bool IsWritable() const override; | 31 | bool IsWritable() const override; |
| 30 | bool IsReadable() const override; | 32 | bool IsReadable() const override; |
| 31 | size_t Read(u8* data, size_t length, size_t offset) const override; | 33 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 32 | size_t Write(const u8* data, size_t length, size_t offset) override; | 34 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 33 | bool Rename(std::string_view name) override; | 35 | bool Rename(std::string_view name) override; |
| 34 | 36 | ||
| 35 | private: | 37 | private: |
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index 847cde2f5..a4c6719a0 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp | |||
| @@ -9,20 +9,22 @@ | |||
| 9 | 9 | ||
| 10 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | 11 | ||
| 12 | OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, | 12 | OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, std::size_t size_, std::size_t offset_, |
| 13 | std::string name_, VirtualDir parent_) | 13 | std::string name_, VirtualDir parent_) |
| 14 | : file(file_), offset(offset_), size(size_), name(std::move(name_)), | 14 | : file(file_), offset(offset_), size(size_), name(std::move(name_)), |
| 15 | parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} | 15 | parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} |
| 16 | 16 | ||
| 17 | OffsetVfsFile::~OffsetVfsFile() = default; | ||
| 18 | |||
| 17 | std::string OffsetVfsFile::GetName() const { | 19 | std::string OffsetVfsFile::GetName() const { |
| 18 | return name.empty() ? file->GetName() : name; | 20 | return name.empty() ? file->GetName() : name; |
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | size_t OffsetVfsFile::GetSize() const { | 23 | std::size_t OffsetVfsFile::GetSize() const { |
| 22 | return size; | 24 | return size; |
| 23 | } | 25 | } |
| 24 | 26 | ||
| 25 | bool OffsetVfsFile::Resize(size_t new_size) { | 27 | bool OffsetVfsFile::Resize(std::size_t new_size) { |
| 26 | if (offset + new_size < file->GetSize()) { | 28 | if (offset + new_size < file->GetSize()) { |
| 27 | size = new_size; | 29 | size = new_size; |
| 28 | } else { | 30 | } else { |
| @@ -47,22 +49,22 @@ bool OffsetVfsFile::IsReadable() const { | |||
| 47 | return file->IsReadable(); | 49 | return file->IsReadable(); |
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { | 52 | std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { |
| 51 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); | 53 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { | 56 | std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { |
| 55 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); | 57 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { | 60 | boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { |
| 59 | if (r_offset < size) | 61 | if (r_offset < size) |
| 60 | return file->ReadByte(offset + r_offset); | 62 | return file->ReadByte(offset + r_offset); |
| 61 | 63 | ||
| 62 | return boost::none; | 64 | return boost::none; |
| 63 | } | 65 | } |
| 64 | 66 | ||
| 65 | std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { | 67 | std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { |
| 66 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); | 68 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); |
| 67 | } | 69 | } |
| 68 | 70 | ||
| @@ -70,14 +72,14 @@ std::vector<u8> OffsetVfsFile::ReadAllBytes() const { | |||
| 70 | return file->ReadBytes(size, offset); | 72 | return file->ReadBytes(size, offset); |
| 71 | } | 73 | } |
| 72 | 74 | ||
| 73 | bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { | 75 | bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { |
| 74 | if (r_offset < size) | 76 | if (r_offset < size) |
| 75 | return file->WriteByte(data, offset + r_offset); | 77 | return file->WriteByte(data, offset + r_offset); |
| 76 | 78 | ||
| 77 | return false; | 79 | return false; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) { | 82 | std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) { |
| 81 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); | 83 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); |
| 82 | } | 84 | } |
| 83 | 85 | ||
| @@ -85,12 +87,12 @@ bool OffsetVfsFile::Rename(std::string_view name) { | |||
| 85 | return file->Rename(name); | 87 | return file->Rename(name); |
| 86 | } | 88 | } |
| 87 | 89 | ||
| 88 | size_t OffsetVfsFile::GetOffset() const { | 90 | std::size_t OffsetVfsFile::GetOffset() const { |
| 89 | return offset; | 91 | return offset; |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 92 | size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { | 94 | std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { |
| 93 | return std::clamp(r_size, size_t{0}, size - r_offset); | 95 | return std::clamp(r_size, std::size_t{0}, size - r_offset); |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | } // namespace FileSys | 98 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index cb92d1570..8062702a7 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h | |||
| @@ -17,33 +17,34 @@ namespace FileSys { | |||
| 17 | // the size of this wrapper. | 17 | // the size of this wrapper. |
| 18 | class OffsetVfsFile : public VfsFile { | 18 | class OffsetVfsFile : public VfsFile { |
| 19 | public: | 19 | public: |
| 20 | OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, | 20 | OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0, |
| 21 | std::string new_name = "", VirtualDir new_parent = nullptr); | 21 | std::string new_name = "", VirtualDir new_parent = nullptr); |
| 22 | ~OffsetVfsFile() override; | ||
| 22 | 23 | ||
| 23 | std::string GetName() const override; | 24 | std::string GetName() const override; |
| 24 | size_t GetSize() const override; | 25 | std::size_t GetSize() const override; |
| 25 | bool Resize(size_t new_size) override; | 26 | bool Resize(std::size_t new_size) override; |
| 26 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 27 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 27 | bool IsWritable() const override; | 28 | bool IsWritable() const override; |
| 28 | bool IsReadable() const override; | 29 | bool IsReadable() const override; |
| 29 | size_t Read(u8* data, size_t length, size_t offset) const override; | 30 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 30 | size_t Write(const u8* data, size_t length, size_t offset) override; | 31 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 31 | boost::optional<u8> ReadByte(size_t offset) const override; | 32 | boost::optional<u8> ReadByte(std::size_t offset) const override; |
| 32 | std::vector<u8> ReadBytes(size_t size, size_t offset) const override; | 33 | std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; |
| 33 | std::vector<u8> ReadAllBytes() const override; | 34 | std::vector<u8> ReadAllBytes() const override; |
| 34 | bool WriteByte(u8 data, size_t offset) override; | 35 | bool WriteByte(u8 data, std::size_t offset) override; |
| 35 | size_t WriteBytes(const std::vector<u8>& data, size_t offset) override; | 36 | std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override; |
| 36 | 37 | ||
| 37 | bool Rename(std::string_view name) override; | 38 | bool Rename(std::string_view name) override; |
| 38 | 39 | ||
| 39 | size_t GetOffset() const; | 40 | std::size_t GetOffset() const; |
| 40 | 41 | ||
| 41 | private: | 42 | private: |
| 42 | size_t TrimToFit(size_t r_size, size_t r_offset) const; | 43 | std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; |
| 43 | 44 | ||
| 44 | std::shared_ptr<VfsFile> file; | 45 | std::shared_ptr<VfsFile> file; |
| 45 | size_t offset; | 46 | std::size_t offset; |
| 46 | size_t size; | 47 | std::size_t size; |
| 47 | std::string name; | 48 | std::string name; |
| 48 | VirtualDir parent; | 49 | VirtualDir parent; |
| 49 | }; | 50 | }; |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 2b8ac7103..5e242e20f 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/common_paths.h" | 10 | #include "common/common_paths.h" |
| 11 | #include "common/file_util.h" | ||
| 11 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 12 | #include "core/file_sys/vfs_real.h" | 13 | #include "core/file_sys/vfs_real.h" |
| 13 | 14 | ||
| @@ -39,6 +40,7 @@ static std::string ModeFlagsToString(Mode mode) { | |||
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} | 42 | RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} |
| 43 | RealVfsFilesystem::~RealVfsFilesystem() = default; | ||
| 42 | 44 | ||
| 43 | std::string RealVfsFilesystem::GetName() const { | 45 | std::string RealVfsFilesystem::GetName() const { |
| 44 | return "Real"; | 46 | return "Real"; |
| @@ -219,15 +221,17 @@ RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOF | |||
| 219 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | 221 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), |
| 220 | perms(perms_) {} | 222 | perms(perms_) {} |
| 221 | 223 | ||
| 224 | RealVfsFile::~RealVfsFile() = default; | ||
| 225 | |||
| 222 | std::string RealVfsFile::GetName() const { | 226 | std::string RealVfsFile::GetName() const { |
| 223 | return path_components.back(); | 227 | return path_components.back(); |
| 224 | } | 228 | } |
| 225 | 229 | ||
| 226 | size_t RealVfsFile::GetSize() const { | 230 | std::size_t RealVfsFile::GetSize() const { |
| 227 | return backing->GetSize(); | 231 | return backing->GetSize(); |
| 228 | } | 232 | } |
| 229 | 233 | ||
| 230 | bool RealVfsFile::Resize(size_t new_size) { | 234 | bool RealVfsFile::Resize(std::size_t new_size) { |
| 231 | return backing->Resize(new_size); | 235 | return backing->Resize(new_size); |
| 232 | } | 236 | } |
| 233 | 237 | ||
| @@ -243,13 +247,13 @@ bool RealVfsFile::IsReadable() const { | |||
| 243 | return (perms & Mode::ReadWrite) != 0; | 247 | return (perms & Mode::ReadWrite) != 0; |
| 244 | } | 248 | } |
| 245 | 249 | ||
| 246 | size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | 250 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 247 | if (!backing->Seek(offset, SEEK_SET)) | 251 | if (!backing->Seek(offset, SEEK_SET)) |
| 248 | return 0; | 252 | return 0; |
| 249 | return backing->ReadBytes(data, length); | 253 | return backing->ReadBytes(data, length); |
| 250 | } | 254 | } |
| 251 | 255 | ||
| 252 | size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | 256 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 253 | if (!backing->Seek(offset, SEEK_SET)) | 257 | if (!backing->Seek(offset, SEEK_SET)) |
| 254 | return 0; | 258 | return 0; |
| 255 | return backing->WriteBytes(data, length); | 259 | return backing->WriteBytes(data, length); |
| @@ -312,6 +316,8 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& | |||
| 312 | FileUtil::CreateDir(path); | 316 | FileUtil::CreateDir(path); |
| 313 | } | 317 | } |
| 314 | 318 | ||
| 319 | RealVfsDirectory::~RealVfsDirectory() = default; | ||
| 320 | |||
| 315 | std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { | 321 | std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { |
| 316 | const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | 322 | const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); |
| 317 | if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) | 323 | if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) |
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 989803d43..681c43e82 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -6,15 +6,19 @@ | |||
| 6 | 6 | ||
| 7 | #include <string_view> | 7 | #include <string_view> |
| 8 | #include <boost/container/flat_map.hpp> | 8 | #include <boost/container/flat_map.hpp> |
| 9 | #include "common/file_util.h" | ||
| 10 | #include "core/file_sys/mode.h" | 9 | #include "core/file_sys/mode.h" |
| 11 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 12 | 11 | ||
| 12 | namespace FileUtil { | ||
| 13 | class IOFile; | ||
| 14 | } | ||
| 15 | |||
| 13 | namespace FileSys { | 16 | namespace FileSys { |
| 14 | 17 | ||
| 15 | class RealVfsFilesystem : public VfsFilesystem { | 18 | class RealVfsFilesystem : public VfsFilesystem { |
| 16 | public: | 19 | public: |
| 17 | RealVfsFilesystem(); | 20 | RealVfsFilesystem(); |
| 21 | ~RealVfsFilesystem() override; | ||
| 18 | 22 | ||
| 19 | std::string GetName() const override; | 23 | std::string GetName() const override; |
| 20 | bool IsReadable() const override; | 24 | bool IsReadable() const override; |
| @@ -40,21 +44,23 @@ class RealVfsFile : public VfsFile { | |||
| 40 | friend class RealVfsDirectory; | 44 | friend class RealVfsDirectory; |
| 41 | friend class RealVfsFilesystem; | 45 | friend class RealVfsFilesystem; |
| 42 | 46 | ||
| 43 | RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, | ||
| 44 | const std::string& path, Mode perms = Mode::Read); | ||
| 45 | |||
| 46 | public: | 47 | public: |
| 48 | ~RealVfsFile() override; | ||
| 49 | |||
| 47 | std::string GetName() const override; | 50 | std::string GetName() const override; |
| 48 | size_t GetSize() const override; | 51 | std::size_t GetSize() const override; |
| 49 | bool Resize(size_t new_size) override; | 52 | bool Resize(std::size_t new_size) override; |
| 50 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 53 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 51 | bool IsWritable() const override; | 54 | bool IsWritable() const override; |
| 52 | bool IsReadable() const override; | 55 | bool IsReadable() const override; |
| 53 | size_t Read(u8* data, size_t length, size_t offset) const override; | 56 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 54 | size_t Write(const u8* data, size_t length, size_t offset) override; | 57 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 55 | bool Rename(std::string_view name) override; | 58 | bool Rename(std::string_view name) override; |
| 56 | 59 | ||
| 57 | private: | 60 | private: |
| 61 | RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, | ||
| 62 | const std::string& path, Mode perms = Mode::Read); | ||
| 63 | |||
| 58 | bool Close(); | 64 | bool Close(); |
| 59 | 65 | ||
| 60 | RealVfsFilesystem& base; | 66 | RealVfsFilesystem& base; |
| @@ -70,9 +76,9 @@ private: | |||
| 70 | class RealVfsDirectory : public VfsDirectory { | 76 | class RealVfsDirectory : public VfsDirectory { |
| 71 | friend class RealVfsFilesystem; | 77 | friend class RealVfsFilesystem; |
| 72 | 78 | ||
| 73 | RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); | ||
| 74 | |||
| 75 | public: | 79 | public: |
| 80 | ~RealVfsDirectory() override; | ||
| 81 | |||
| 76 | std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; | 82 | std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; |
| 77 | std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; | 83 | std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; |
| 78 | std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; | 84 | std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; |
| @@ -97,6 +103,8 @@ protected: | |||
| 97 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 103 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
| 98 | 104 | ||
| 99 | private: | 105 | private: |
| 106 | RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); | ||
| 107 | |||
| 100 | template <typename T, typename R> | 108 | template <typename T, typename R> |
| 101 | std::vector<std::shared_ptr<R>> IterateEntries() const; | 109 | std::vector<std::shared_ptr<R>> IterateEntries() const; |
| 102 | 110 | ||
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 98e7c4598..ec7f735b5 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp | |||
| @@ -13,6 +13,8 @@ VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, | |||
| 13 | : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), | 13 | : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), |
| 14 | name(std::move(name_)) {} | 14 | name(std::move(name_)) {} |
| 15 | 15 | ||
| 16 | VectorVfsDirectory::~VectorVfsDirectory() = default; | ||
| 17 | |||
| 16 | std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { | 18 | std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { |
| 17 | return files; | 19 | return files; |
| 18 | } | 20 | } |
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 179f62e4b..cba44a7a6 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h | |||
| @@ -15,6 +15,7 @@ public: | |||
| 15 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, | 15 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, |
| 16 | std::vector<VirtualDir> dirs = {}, std::string name = "", | 16 | std::vector<VirtualDir> dirs = {}, std::string name = "", |
| 17 | VirtualDir parent = nullptr); | 17 | VirtualDir parent = nullptr); |
| 18 | ~VectorVfsDirectory() override; | ||
| 18 | 19 | ||
| 19 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 20 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| 20 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | 21 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 552835738..b2b164368 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <mbedtls/md.h> | 10 | #include <mbedtls/md.h> |
| 11 | #include <mbedtls/sha256.h> | 11 | #include <mbedtls/sha256.h> |
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/file_util.h" | ||
| 13 | #include "common/hex_util.h" | 14 | #include "common/hex_util.h" |
| 14 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 15 | #include "core/crypto/aes_util.h" | 16 | #include "core/crypto/aes_util.h" |
| @@ -24,14 +25,11 @@ namespace FileSys { | |||
| 24 | constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000; | 25 | constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000; |
| 25 | 26 | ||
| 26 | template <typename SourceData, typename SourceKey, typename Destination> | 27 | template <typename SourceData, typename SourceKey, typename Destination> |
| 27 | static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length, | 28 | static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t key_length, |
| 28 | const SourceData* data, size_t data_length) { | 29 | const SourceData* data, std::size_t data_length) { |
| 29 | mbedtls_md_context_t context; | 30 | mbedtls_md_context_t context; |
| 30 | mbedtls_md_init(&context); | 31 | mbedtls_md_init(&context); |
| 31 | 32 | ||
| 32 | const auto key_f = reinterpret_cast<const u8*>(key); | ||
| 33 | const std::vector<u8> key_v(key_f, key_f + key_length); | ||
| 34 | |||
| 35 | if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) || | 33 | if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) || |
| 36 | mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) || | 34 | mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) || |
| 37 | mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) || | 35 | mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) || |
| @@ -44,7 +42,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_ | |||
| 44 | return true; | 42 | return true; |
| 45 | } | 43 | } |
| 46 | 44 | ||
| 47 | NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { | 45 | NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { |
| 48 | std::string path = FileUtil::SanitizePath(file->GetFullPath()); | 46 | std::string path = FileUtil::SanitizePath(file->GetFullPath()); |
| 49 | static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", | 47 | static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", |
| 50 | std::regex_constants::ECMAScript | | 48 | std::regex_constants::ECMAScript | |
| @@ -64,13 +62,15 @@ NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NA | |||
| 64 | } | 62 | } |
| 65 | 63 | ||
| 66 | NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) | 64 | NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) |
| 67 | : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { | 65 | : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { |
| 68 | Core::Crypto::SHA256Hash hash{}; | 66 | Core::Crypto::SHA256Hash hash{}; |
| 69 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); | 67 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); |
| 70 | status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], | 68 | status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], |
| 71 | Common::HexArrayToString(nca_id, false))); | 69 | Common::HexArrayToString(nca_id, false))); |
| 72 | } | 70 | } |
| 73 | 71 | ||
| 72 | NAX::~NAX() = default; | ||
| 73 | |||
| 74 | Loader::ResultStatus NAX::Parse(std::string_view path) { | 74 | Loader::ResultStatus NAX::Parse(std::string_view path) { |
| 75 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) | 75 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) |
| 76 | return Loader::ResultStatus::ErrorBadNAXHeader; | 76 | return Loader::ResultStatus::ErrorBadNAXHeader; |
| @@ -90,7 +90,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) { | |||
| 90 | 90 | ||
| 91 | const auto enc_keys = header->key_area; | 91 | const auto enc_keys = header->key_area; |
| 92 | 92 | ||
| 93 | size_t i = 0; | 93 | std::size_t i = 0; |
| 94 | for (; i < sd_keys.size(); ++i) { | 94 | for (; i < sd_keys.size(); ++i) { |
| 95 | std::array<Core::Crypto::Key128, 2> nax_keys{}; | 95 | std::array<Core::Crypto::Key128, 2> nax_keys{}; |
| 96 | if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(), | 96 | if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(), |
| @@ -98,7 +98,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) { | |||
| 98 | return Loader::ResultStatus::ErrorNAXKeyHMACFailed; | 98 | return Loader::ResultStatus::ErrorNAXKeyHMACFailed; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | for (size_t j = 0; j < nax_keys.size(); ++j) { | 101 | for (std::size_t j = 0; j < nax_keys.size(); ++j) { |
| 102 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j], | 102 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j], |
| 103 | Core::Crypto::Mode::ECB); | 103 | Core::Crypto::Mode::ECB); |
| 104 | cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(), | 104 | cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(), |
| @@ -137,9 +137,9 @@ VirtualFile NAX::GetDecrypted() const { | |||
| 137 | return dec_file; | 137 | return dec_file; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | std::shared_ptr<NCA> NAX::AsNCA() const { | 140 | std::unique_ptr<NCA> NAX::AsNCA() const { |
| 141 | if (type == NAXContentType::NCA) | 141 | if (type == NAXContentType::NCA) |
| 142 | return std::make_shared<NCA>(GetDecrypted()); | 142 | return std::make_unique<NCA>(GetDecrypted()); |
| 143 | return nullptr; | 143 | return nullptr; |
| 144 | } | 144 | } |
| 145 | 145 | ||
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 55d2154a6..8fedd8585 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h | |||
| @@ -33,12 +33,13 @@ class NAX : public ReadOnlyVfsDirectory { | |||
| 33 | public: | 33 | public: |
| 34 | explicit NAX(VirtualFile file); | 34 | explicit NAX(VirtualFile file); |
| 35 | explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id); | 35 | explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id); |
| 36 | ~NAX() override; | ||
| 36 | 37 | ||
| 37 | Loader::ResultStatus GetStatus() const; | 38 | Loader::ResultStatus GetStatus() const; |
| 38 | 39 | ||
| 39 | VirtualFile GetDecrypted() const; | 40 | VirtualFile GetDecrypted() const; |
| 40 | 41 | ||
| 41 | std::shared_ptr<NCA> AsNCA() const; | 42 | std::unique_ptr<NCA> AsNCA() const; |
| 42 | 43 | ||
| 43 | NAXContentType GetContentType() const; | 44 | NAXContentType GetContentType() const; |
| 44 | 45 | ||
| @@ -60,7 +61,7 @@ private: | |||
| 60 | 61 | ||
| 61 | VirtualFile file; | 62 | VirtualFile file; |
| 62 | Loader::ResultStatus status; | 63 | Loader::ResultStatus status; |
| 63 | NAXContentType type; | 64 | NAXContentType type{}; |
| 64 | 65 | ||
| 65 | VirtualFile dec_file; | 66 | VirtualFile dec_file; |
| 66 | 67 | ||
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 332e5c3d0..0ecdd9f82 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -65,9 +65,9 @@ constexpr u32 MSG_WAITALL = 8; | |||
| 65 | constexpr u32 LR_REGISTER = 30; | 65 | constexpr u32 LR_REGISTER = 30; |
| 66 | constexpr u32 SP_REGISTER = 31; | 66 | constexpr u32 SP_REGISTER = 31; |
| 67 | constexpr u32 PC_REGISTER = 32; | 67 | constexpr u32 PC_REGISTER = 32; |
| 68 | constexpr u32 CPSR_REGISTER = 33; | 68 | constexpr u32 PSTATE_REGISTER = 33; |
| 69 | constexpr u32 UC_ARM64_REG_Q0 = 34; | 69 | constexpr u32 UC_ARM64_REG_Q0 = 34; |
| 70 | constexpr u32 FPSCR_REGISTER = 66; | 70 | constexpr u32 FPCR_REGISTER = 66; |
| 71 | 71 | ||
| 72 | // TODO/WiP - Used while working on support for FPU | 72 | // TODO/WiP - Used while working on support for FPU |
| 73 | constexpr u32 TODO_DUMMY_REG_997 = 997; | 73 | constexpr u32 TODO_DUMMY_REG_997 = 997; |
| @@ -116,7 +116,7 @@ constexpr char target_xml[] = | |||
| 116 | 116 | ||
| 117 | <reg name="pc" bitsize="64" type="code_ptr"/> | 117 | <reg name="pc" bitsize="64" type="code_ptr"/> |
| 118 | 118 | ||
| 119 | <flags id="cpsr_flags" size="4"> | 119 | <flags id="pstate_flags" size="4"> |
| 120 | <field name="SP" start="0" end="0"/> | 120 | <field name="SP" start="0" end="0"/> |
| 121 | <field name="" start="1" end="1"/> | 121 | <field name="" start="1" end="1"/> |
| 122 | <field name="EL" start="2" end="3"/> | 122 | <field name="EL" start="2" end="3"/> |
| @@ -135,7 +135,7 @@ constexpr char target_xml[] = | |||
| 135 | <field name="Z" start="30" end="30"/> | 135 | <field name="Z" start="30" end="30"/> |
| 136 | <field name="N" start="31" end="31"/> | 136 | <field name="N" start="31" end="31"/> |
| 137 | </flags> | 137 | </flags> |
| 138 | <reg name="cpsr" bitsize="32" type="cpsr_flags"/> | 138 | <reg name="pstate" bitsize="32" type="pstate_flags"/> |
| 139 | </feature> | 139 | </feature> |
| 140 | <feature name="org.gnu.gdb.aarch64.fpu"> | 140 | <feature name="org.gnu.gdb.aarch64.fpu"> |
| 141 | </feature> | 141 | </feature> |
| @@ -227,10 +227,10 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { | |||
| 227 | return thread->context.sp; | 227 | return thread->context.sp; |
| 228 | } else if (id == PC_REGISTER) { | 228 | } else if (id == PC_REGISTER) { |
| 229 | return thread->context.pc; | 229 | return thread->context.pc; |
| 230 | } else if (id == CPSR_REGISTER) { | 230 | } else if (id == PSTATE_REGISTER) { |
| 231 | return thread->context.cpsr; | 231 | return thread->context.pstate; |
| 232 | } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { | 232 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { |
| 233 | return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0]; | 233 | return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0]; |
| 234 | } else { | 234 | } else { |
| 235 | return 0; | 235 | return 0; |
| 236 | } | 236 | } |
| @@ -247,10 +247,10 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) | |||
| 247 | thread->context.sp = val; | 247 | thread->context.sp = val; |
| 248 | } else if (id == PC_REGISTER) { | 248 | } else if (id == PC_REGISTER) { |
| 249 | thread->context.pc = val; | 249 | thread->context.pc = val; |
| 250 | } else if (id == CPSR_REGISTER) { | 250 | } else if (id == PSTATE_REGISTER) { |
| 251 | thread->context.cpsr = val; | 251 | thread->context.pstate = val; |
| 252 | } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { | 252 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { |
| 253 | thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val; | 253 | thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; |
| 254 | } | 254 | } |
| 255 | } | 255 | } |
| 256 | 256 | ||
| @@ -292,7 +292,7 @@ static u8 NibbleToHex(u8 n) { | |||
| 292 | * @param src Pointer to array of output hex string characters. | 292 | * @param src Pointer to array of output hex string characters. |
| 293 | * @param len Length of src array. | 293 | * @param len Length of src array. |
| 294 | */ | 294 | */ |
| 295 | static u32 HexToInt(const u8* src, size_t len) { | 295 | static u32 HexToInt(const u8* src, std::size_t len) { |
| 296 | u32 output = 0; | 296 | u32 output = 0; |
| 297 | while (len-- > 0) { | 297 | while (len-- > 0) { |
| 298 | output = (output << 4) | HexCharToValue(src[0]); | 298 | output = (output << 4) | HexCharToValue(src[0]); |
| @@ -307,7 +307,7 @@ static u32 HexToInt(const u8* src, size_t len) { | |||
| 307 | * @param src Pointer to array of output hex string characters. | 307 | * @param src Pointer to array of output hex string characters. |
| 308 | * @param len Length of src array. | 308 | * @param len Length of src array. |
| 309 | */ | 309 | */ |
| 310 | static u64 HexToLong(const u8* src, size_t len) { | 310 | static u64 HexToLong(const u8* src, std::size_t len) { |
| 311 | u64 output = 0; | 311 | u64 output = 0; |
| 312 | while (len-- > 0) { | 312 | while (len-- > 0) { |
| 313 | output = (output << 4) | HexCharToValue(src[0]); | 313 | output = (output << 4) | HexCharToValue(src[0]); |
| @@ -323,7 +323,7 @@ static u64 HexToLong(const u8* src, size_t len) { | |||
| 323 | * @param src Pointer to array of u8 bytes. | 323 | * @param src Pointer to array of u8 bytes. |
| 324 | * @param len Length of src array. | 324 | * @param len Length of src array. |
| 325 | */ | 325 | */ |
| 326 | static void MemToGdbHex(u8* dest, const u8* src, size_t len) { | 326 | static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) { |
| 327 | while (len-- > 0) { | 327 | while (len-- > 0) { |
| 328 | u8 tmp = *src++; | 328 | u8 tmp = *src++; |
| 329 | *dest++ = NibbleToHex(tmp >> 4); | 329 | *dest++ = NibbleToHex(tmp >> 4); |
| @@ -338,7 +338,7 @@ static void MemToGdbHex(u8* dest, const u8* src, size_t len) { | |||
| 338 | * @param src Pointer to array of output hex string characters. | 338 | * @param src Pointer to array of output hex string characters. |
| 339 | * @param len Length of src array. | 339 | * @param len Length of src array. |
| 340 | */ | 340 | */ |
| 341 | static void GdbHexToMem(u8* dest, const u8* src, size_t len) { | 341 | static void GdbHexToMem(u8* dest, const u8* src, std::size_t len) { |
| 342 | while (len-- > 0) { | 342 | while (len-- > 0) { |
| 343 | *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); | 343 | *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); |
| 344 | src += 2; | 344 | src += 2; |
| @@ -406,7 +406,7 @@ static u64 GdbHexToLong(const u8* src) { | |||
| 406 | /// Read a byte from the gdb client. | 406 | /// Read a byte from the gdb client. |
| 407 | static u8 ReadByte() { | 407 | static u8 ReadByte() { |
| 408 | u8 c; | 408 | u8 c; |
| 409 | size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); | 409 | std::size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); |
| 410 | if (received_size != 1) { | 410 | if (received_size != 1) { |
| 411 | LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); | 411 | LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); |
| 412 | Shutdown(); | 412 | Shutdown(); |
| @@ -416,7 +416,7 @@ static u8 ReadByte() { | |||
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | /// Calculate the checksum of the current command buffer. | 418 | /// Calculate the checksum of the current command buffer. |
| 419 | static u8 CalculateChecksum(const u8* buffer, size_t length) { | 419 | static u8 CalculateChecksum(const u8* buffer, std::size_t length) { |
| 420 | return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); | 420 | return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); |
| 421 | } | 421 | } |
| 422 | 422 | ||
| @@ -518,7 +518,7 @@ bool CheckBreakpoint(VAddr addr, BreakpointType type) { | |||
| 518 | * @param packet Packet to be sent to client. | 518 | * @param packet Packet to be sent to client. |
| 519 | */ | 519 | */ |
| 520 | static void SendPacket(const char packet) { | 520 | static void SendPacket(const char packet) { |
| 521 | size_t sent_size = send(gdbserver_socket, &packet, 1, 0); | 521 | std::size_t sent_size = send(gdbserver_socket, &packet, 1, 0); |
| 522 | if (sent_size != 1) { | 522 | if (sent_size != 1) { |
| 523 | LOG_ERROR(Debug_GDBStub, "send failed"); | 523 | LOG_ERROR(Debug_GDBStub, "send failed"); |
| 524 | } | 524 | } |
| @@ -781,11 +781,11 @@ static void ReadRegister() { | |||
| 781 | LongToGdbHex(reply, RegRead(id, current_thread)); | 781 | LongToGdbHex(reply, RegRead(id, current_thread)); |
| 782 | } else if (id == PC_REGISTER) { | 782 | } else if (id == PC_REGISTER) { |
| 783 | LongToGdbHex(reply, RegRead(id, current_thread)); | 783 | LongToGdbHex(reply, RegRead(id, current_thread)); |
| 784 | } else if (id == CPSR_REGISTER) { | 784 | } else if (id == PSTATE_REGISTER) { |
| 785 | IntToGdbHex(reply, (u32)RegRead(id, current_thread)); | 785 | IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); |
| 786 | } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { | 786 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { |
| 787 | LongToGdbHex(reply, RegRead(id, current_thread)); | 787 | LongToGdbHex(reply, RegRead(id, current_thread)); |
| 788 | } else if (id == FPSCR_REGISTER) { | 788 | } else if (id == FPCR_REGISTER) { |
| 789 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); | 789 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); |
| 790 | } else { | 790 | } else { |
| 791 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); | 791 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); |
| @@ -811,7 +811,7 @@ static void ReadRegisters() { | |||
| 811 | 811 | ||
| 812 | bufptr += 16; | 812 | bufptr += 16; |
| 813 | 813 | ||
| 814 | IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); | 814 | IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread))); |
| 815 | 815 | ||
| 816 | bufptr += 8; | 816 | bufptr += 8; |
| 817 | 817 | ||
| @@ -843,11 +843,11 @@ static void WriteRegister() { | |||
| 843 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 843 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); |
| 844 | } else if (id == PC_REGISTER) { | 844 | } else if (id == PC_REGISTER) { |
| 845 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 845 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); |
| 846 | } else if (id == CPSR_REGISTER) { | 846 | } else if (id == PSTATE_REGISTER) { |
| 847 | RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); | 847 | RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); |
| 848 | } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { | 848 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { |
| 849 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 849 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); |
| 850 | } else if (id == FPSCR_REGISTER) { | 850 | } else if (id == FPCR_REGISTER) { |
| 851 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); | 851 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); |
| 852 | } else { | 852 | } else { |
| 853 | RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); | 853 | RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); |
| @@ -866,16 +866,16 @@ static void WriteRegisters() { | |||
| 866 | if (command_buffer[0] != 'G') | 866 | if (command_buffer[0] != 'G') |
| 867 | return SendReply("E01"); | 867 | return SendReply("E01"); |
| 868 | 868 | ||
| 869 | for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { | 869 | for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) { |
| 870 | if (reg <= SP_REGISTER) { | 870 | if (reg <= SP_REGISTER) { |
| 871 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 871 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 872 | } else if (reg == PC_REGISTER) { | 872 | } else if (reg == PC_REGISTER) { |
| 873 | RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 873 | RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 874 | } else if (reg == CPSR_REGISTER) { | 874 | } else if (reg == PSTATE_REGISTER) { |
| 875 | RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); | 875 | RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); |
| 876 | } else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) { | 876 | } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { |
| 877 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 877 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 878 | } else if (reg == FPSCR_REGISTER) { | 878 | } else if (reg == FPCR_REGISTER) { |
| 879 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 879 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 880 | } else { | 880 | } else { |
| 881 | UNIMPLEMENTED(); | 881 | UNIMPLEMENTED(); |
| @@ -995,7 +995,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { | |||
| 995 | breakpoint.addr = addr; | 995 | breakpoint.addr = addr; |
| 996 | breakpoint.len = len; | 996 | breakpoint.len = len; |
| 997 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | 997 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); |
| 998 | static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}}; | 998 | static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}}; |
| 999 | Memory::WriteBlock(addr, btrap.data(), btrap.size()); | 999 | Memory::WriteBlock(addr, btrap.data(), btrap.size()); |
| 1000 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 1000 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); |
| 1001 | p.insert({addr, breakpoint}); | 1001 | p.insert({addr, breakpoint}); |
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index eaa5395ac..419f45896 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | namespace IPC { | 12 | namespace IPC { |
| 13 | 13 | ||
| 14 | /// Size of the command buffer area, in 32-bit words. | 14 | /// Size of the command buffer area, in 32-bit words. |
| 15 | constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); |
| 16 | 16 | ||
| 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for | 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for |
| 18 | // convenience. | 18 | // convenience. |
| @@ -153,7 +153,7 @@ struct DataPayloadHeader { | |||
| 153 | u32_le magic; | 153 | u32_le magic; |
| 154 | INSERT_PADDING_WORDS(1); | 154 | INSERT_PADDING_WORDS(1); |
| 155 | }; | 155 | }; |
| 156 | static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadRequest size is incorrect"); | 156 | static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect"); |
| 157 | 157 | ||
| 158 | struct DomainMessageHeader { | 158 | struct DomainMessageHeader { |
| 159 | enum class CommandType : u32_le { | 159 | enum class CommandType : u32_le { |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 0f3ffdb60..a4bfe2eb0 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -152,8 +152,8 @@ public: | |||
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | void ValidateHeader() { | 154 | void ValidateHeader() { |
| 155 | const size_t num_domain_objects = context->NumDomainObjects(); | 155 | const std::size_t num_domain_objects = context->NumDomainObjects(); |
| 156 | const size_t num_move_objects = context->NumMoveObjects(); | 156 | const std::size_t num_move_objects = context->NumMoveObjects(); |
| 157 | ASSERT_MSG(!num_domain_objects || !num_move_objects, | 157 | ASSERT_MSG(!num_domain_objects || !num_move_objects, |
| 158 | "cannot move normal handles and domain objects"); | 158 | "cannot move normal handles and domain objects"); |
| 159 | ASSERT_MSG((index - datapayload_index) == normal_params_size, | 159 | ASSERT_MSG((index - datapayload_index) == normal_params_size, |
| @@ -290,13 +290,6 @@ public: | |||
| 290 | Skip(CommandIdSize, false); | 290 | Skip(CommandIdSize, false); |
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy, | ||
| 294 | u32 num_handles_to_move, | ||
| 295 | ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) const { | ||
| 296 | return ResponseBuilder{*context, normal_params_size, num_handles_to_copy, | ||
| 297 | num_handles_to_move, flags}; | ||
| 298 | } | ||
| 299 | |||
| 300 | template <typename T> | 293 | template <typename T> |
| 301 | T Pop(); | 294 | T Pop(); |
| 302 | 295 | ||
| @@ -329,10 +322,10 @@ public: | |||
| 329 | T PopRaw(); | 322 | T PopRaw(); |
| 330 | 323 | ||
| 331 | template <typename T> | 324 | template <typename T> |
| 332 | Kernel::SharedPtr<T> GetMoveObject(size_t index); | 325 | Kernel::SharedPtr<T> GetMoveObject(std::size_t index); |
| 333 | 326 | ||
| 334 | template <typename T> | 327 | template <typename T> |
| 335 | Kernel::SharedPtr<T> GetCopyObject(size_t index); | 328 | Kernel::SharedPtr<T> GetCopyObject(std::size_t index); |
| 336 | 329 | ||
| 337 | template <class T> | 330 | template <class T> |
| 338 | std::shared_ptr<T> PopIpcInterface() { | 331 | std::shared_ptr<T> PopIpcInterface() { |
| @@ -406,12 +399,12 @@ void RequestParser::Pop(First& first_value, Other&... other_values) { | |||
| 406 | } | 399 | } |
| 407 | 400 | ||
| 408 | template <typename T> | 401 | template <typename T> |
| 409 | Kernel::SharedPtr<T> RequestParser::GetMoveObject(size_t index) { | 402 | Kernel::SharedPtr<T> RequestParser::GetMoveObject(std::size_t index) { |
| 410 | return context->GetMoveObject<T>(index); | 403 | return context->GetMoveObject<T>(index); |
| 411 | } | 404 | } |
| 412 | 405 | ||
| 413 | template <typename T> | 406 | template <typename T> |
| 414 | Kernel::SharedPtr<T> RequestParser::GetCopyObject(size_t index) { | 407 | Kernel::SharedPtr<T> RequestParser::GetCopyObject(std::size_t index) { |
| 415 | return context->GetCopyObject<T>(index); | 408 | return context->GetCopyObject<T>(index); |
| 416 | } | 409 | } |
| 417 | 410 | ||
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 6657accd5..93577591f 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -35,16 +35,17 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) { | |||
| 35 | 35 | ||
| 36 | // Gets the threads waiting on an address. | 36 | // Gets the threads waiting on an address. |
| 37 | static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { | 37 | static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { |
| 38 | const auto RetrieveWaitingThreads = | 38 | const auto RetrieveWaitingThreads = [](std::size_t core_index, |
| 39 | [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { | 39 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 40 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 40 | VAddr arb_addr) { |
| 41 | auto& thread_list = scheduler->GetThreadList(); | 41 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 42 | 42 | auto& thread_list = scheduler->GetThreadList(); | |
| 43 | for (auto& thread : thread_list) { | 43 | |
| 44 | if (thread->arb_wait_address == arb_addr) | 44 | for (auto& thread : thread_list) { |
| 45 | waiting_threads.push_back(thread); | 45 | if (thread->arb_wait_address == arb_addr) |
| 46 | } | 46 | waiting_threads.push_back(thread); |
| 47 | }; | 47 | } |
| 48 | }; | ||
| 48 | 49 | ||
| 49 | // Retrieve all threads that are waiting for this address. | 50 | // Retrieve all threads that are waiting for this address. |
| 50 | std::vector<SharedPtr<Thread>> threads; | 51 | std::vector<SharedPtr<Thread>> threads; |
| @@ -66,12 +67,12 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) | |||
| 66 | static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { | 67 | static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { |
| 67 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process | 68 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process |
| 68 | // them all. | 69 | // them all. |
| 69 | size_t last = waiting_threads.size(); | 70 | std::size_t last = waiting_threads.size(); |
| 70 | if (num_to_wake > 0) | 71 | if (num_to_wake > 0) |
| 71 | last = num_to_wake; | 72 | last = num_to_wake; |
| 72 | 73 | ||
| 73 | // Signal the waiting threads. | 74 | // Signal the waiting threads. |
| 74 | for (size_t i = 0; i < last; i++) { | 75 | for (std::size_t i = 0; i < last; i++) { |
| 75 | ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); | 76 | ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); |
| 76 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | 77 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 77 | waiting_threads[i]->arb_wait_address = 0; | 78 | waiting_threads[i]->arb_wait_address = 0; |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 4054d5db6..8c2be2681 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -17,10 +17,12 @@ enum { | |||
| 17 | 17 | ||
| 18 | // Confirmed Switch OS error codes | 18 | // Confirmed Switch OS error codes |
| 19 | MaxConnectionsReached = 7, | 19 | MaxConnectionsReached = 7, |
| 20 | InvalidSize = 101, | ||
| 20 | InvalidAddress = 102, | 21 | InvalidAddress = 102, |
| 21 | HandleTableFull = 105, | 22 | HandleTableFull = 105, |
| 22 | InvalidMemoryState = 106, | 23 | InvalidMemoryState = 106, |
| 23 | InvalidMemoryPermissions = 108, | 24 | InvalidMemoryPermissions = 108, |
| 25 | InvalidThreadPriority = 112, | ||
| 24 | InvalidProcessorId = 113, | 26 | InvalidProcessorId = 113, |
| 25 | InvalidHandle = 114, | 27 | InvalidHandle = 114, |
| 26 | InvalidCombination = 116, | 28 | InvalidCombination = 116, |
| @@ -28,6 +30,7 @@ enum { | |||
| 28 | SynchronizationCanceled = 118, | 30 | SynchronizationCanceled = 118, |
| 29 | TooLarge = 119, | 31 | TooLarge = 119, |
| 30 | InvalidEnumValue = 120, | 32 | InvalidEnumValue = 120, |
| 33 | NoSuchEntry = 121, | ||
| 31 | InvalidState = 125, | 34 | InvalidState = 125, |
| 32 | ResourceLimitExceeded = 132, | 35 | ResourceLimitExceeded = 132, |
| 33 | }; | 36 | }; |
| @@ -36,7 +39,7 @@ enum { | |||
| 36 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always | 39 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always |
| 37 | // double check that the code matches before re-using the constant. | 40 | // double check that the code matches before re-using the constant. |
| 38 | 41 | ||
| 39 | // TODO(bunnei): Replace these with correct errors for Switch OS | 42 | // TODO(bunnei): Replace -1 with correct errors for Switch OS |
| 40 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); | 43 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); |
| 41 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); | 44 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); |
| 42 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); | 45 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); |
| @@ -53,15 +56,17 @@ constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::In | |||
| 53 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, | 56 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, |
| 54 | ErrCodes::InvalidMemoryPermissions); | 57 | ErrCodes::InvalidMemoryPermissions); |
| 55 | constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); | 58 | constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); |
| 59 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); | ||
| 60 | constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); | ||
| 56 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); | 61 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); |
| 62 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, | ||
| 63 | ErrCodes::InvalidThreadPriority); | ||
| 57 | constexpr ResultCode ERR_INVALID_POINTER(-1); | 64 | constexpr ResultCode ERR_INVALID_POINTER(-1); |
| 58 | constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); | 65 | constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); |
| 59 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); | 66 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); |
| 60 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. | 67 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. |
| 61 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); | 68 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); |
| 62 | constexpr ResultCode ERR_NOT_FOUND(-1); | 69 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); |
| 63 | constexpr ResultCode ERR_OUT_OF_RANGE(-1); | ||
| 64 | constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(-1); | ||
| 65 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); | 70 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); |
| 66 | /// Returned when Accept() is called on a port with no sessions to be accepted. | 71 | /// Returned when Accept() is called on a port with no sessions to be accepted. |
| 67 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); | 72 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); |
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 3a079b9a9..5ee5c05e3 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp | |||
| @@ -65,7 +65,7 @@ ResultCode HandleTable::Close(Handle handle) { | |||
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | bool HandleTable::IsValid(Handle handle) const { | 67 | bool HandleTable::IsValid(Handle handle) const { |
| 68 | size_t slot = GetSlot(handle); | 68 | std::size_t slot = GetSlot(handle); |
| 69 | u16 generation = GetGeneration(handle); | 69 | u16 generation = GetGeneration(handle); |
| 70 | 70 | ||
| 71 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | 71 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; |
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index cac928adb..9e2f33e8a 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h | |||
| @@ -93,7 +93,7 @@ private: | |||
| 93 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | 93 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further |
| 94 | * reduced by ExHeader values, but this is not emulated here. | 94 | * reduced by ExHeader values, but this is not emulated here. |
| 95 | */ | 95 | */ |
| 96 | static const size_t MAX_COUNT = 4096; | 96 | static const std::size_t MAX_COUNT = 4096; |
| 97 | 97 | ||
| 98 | static u16 GetSlot(Handle handle) { | 98 | static u16 GetSlot(Handle handle) { |
| 99 | return handle >> 15; | 99 | return handle >> 15; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 7264be906..72fb9d250 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -42,9 +42,9 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, | |||
| 42 | Kernel::SharedPtr<Kernel::Event> event) { | 42 | Kernel::SharedPtr<Kernel::Event> event) { |
| 43 | 43 | ||
| 44 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 44 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 45 | thread->wakeup_callback = | 45 | thread->wakeup_callback = [context = *this, callback]( |
| 46 | [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread, | 46 | ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 47 | SharedPtr<WaitObject> object, size_t index) mutable -> bool { | 47 | SharedPtr<WaitObject> object, std::size_t index) mutable -> bool { |
| 48 | ASSERT(thread->status == ThreadStatus::WaitHLEEvent); | 48 | ASSERT(thread->status == ThreadStatus::WaitHLEEvent); |
| 49 | callback(thread, context, reason); | 49 | callback(thread, context, reason); |
| 50 | context.WriteToOutgoingCommandBuffer(*thread); | 50 | context.WriteToOutgoingCommandBuffer(*thread); |
| @@ -199,8 +199,8 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb | |||
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | // The data_size already includes the payload header, the padding and the domain header. | 201 | // The data_size already includes the payload header, the padding and the domain header. |
| 202 | size_t size = data_payload_offset + command_header->data_size - | 202 | std::size_t size = data_payload_offset + command_header->data_size - |
| 203 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; | 203 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; |
| 204 | if (domain_message_header) | 204 | if (domain_message_header) |
| 205 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); | 205 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); |
| 206 | std::copy_n(src_cmdbuf, size, cmd_buf.begin()); | 206 | std::copy_n(src_cmdbuf, size, cmd_buf.begin()); |
| @@ -217,8 +217,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 217 | ParseCommandBuffer(cmd_buf.data(), false); | 217 | ParseCommandBuffer(cmd_buf.data(), false); |
| 218 | 218 | ||
| 219 | // The data_size already includes the payload header, the padding and the domain header. | 219 | // The data_size already includes the payload header, the padding and the domain header. |
| 220 | size_t size = data_payload_offset + command_header->data_size - | 220 | std::size_t size = data_payload_offset + command_header->data_size - |
| 221 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; | 221 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; |
| 222 | if (domain_message_header) | 222 | if (domain_message_header) |
| 223 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); | 223 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); |
| 224 | 224 | ||
| @@ -229,7 +229,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 229 | "Handle descriptor bit set but no handles to translate"); | 229 | "Handle descriptor bit set but no handles to translate"); |
| 230 | // We write the translated handles at a specific offset in the command buffer, this space | 230 | // We write the translated handles at a specific offset in the command buffer, this space |
| 231 | // was already reserved when writing the header. | 231 | // was already reserved when writing the header. |
| 232 | size_t current_offset = | 232 | std::size_t current_offset = |
| 233 | (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32); | 233 | (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32); |
| 234 | ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented"); | 234 | ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented"); |
| 235 | 235 | ||
| @@ -258,7 +258,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 258 | ASSERT(domain_message_header->num_objects == domain_objects.size()); | 258 | ASSERT(domain_message_header->num_objects == domain_objects.size()); |
| 259 | // Write the domain objects to the command buffer, these go after the raw untranslated data. | 259 | // Write the domain objects to the command buffer, these go after the raw untranslated data. |
| 260 | // TODO(Subv): This completely ignores C buffers. | 260 | // TODO(Subv): This completely ignores C buffers. |
| 261 | size_t domain_offset = size - domain_message_header->num_objects; | 261 | std::size_t domain_offset = size - domain_message_header->num_objects; |
| 262 | auto& request_handlers = server_session->domain_request_handlers; | 262 | auto& request_handlers = server_session->domain_request_handlers; |
| 263 | 263 | ||
| 264 | for (auto& object : domain_objects) { | 264 | for (auto& object : domain_objects) { |
| @@ -291,14 +291,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { | |||
| 291 | return buffer; | 291 | return buffer; |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { | 294 | std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, |
| 295 | int buffer_index) const { | ||
| 295 | if (size == 0) { | 296 | if (size == 0) { |
| 296 | LOG_WARNING(Core, "skip empty buffer write"); | 297 | LOG_WARNING(Core, "skip empty buffer write"); |
| 297 | return 0; | 298 | return 0; |
| 298 | } | 299 | } |
| 299 | 300 | ||
| 300 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; | 301 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; |
| 301 | const size_t buffer_size{GetWriteBufferSize(buffer_index)}; | 302 | const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; |
| 302 | if (size > buffer_size) { | 303 | if (size > buffer_size) { |
| 303 | LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, | 304 | LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, |
| 304 | buffer_size); | 305 | buffer_size); |
| @@ -314,13 +315,13 @@ size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffe | |||
| 314 | return size; | 315 | return size; |
| 315 | } | 316 | } |
| 316 | 317 | ||
| 317 | size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { | 318 | std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { |
| 318 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; | 319 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; |
| 319 | return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() | 320 | return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() |
| 320 | : BufferDescriptorX()[buffer_index].Size(); | 321 | : BufferDescriptorX()[buffer_index].Size(); |
| 321 | } | 322 | } |
| 322 | 323 | ||
| 323 | size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { | 324 | std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { |
| 324 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; | 325 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; |
| 325 | return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() | 326 | return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() |
| 326 | : BufferDescriptorC()[buffer_index].Size(); | 327 | : BufferDescriptorC()[buffer_index].Size(); |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f0d07f1b6..894479ee0 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -170,7 +170,7 @@ public: | |||
| 170 | std::vector<u8> ReadBuffer(int buffer_index = 0) const; | 170 | std::vector<u8> ReadBuffer(int buffer_index = 0) const; |
| 171 | 171 | ||
| 172 | /// Helper function to write a buffer using the appropriate buffer descriptor | 172 | /// Helper function to write a buffer using the appropriate buffer descriptor |
| 173 | size_t WriteBuffer(const void* buffer, size_t size, int buffer_index = 0) const; | 173 | std::size_t WriteBuffer(const void* buffer, std::size_t size, int buffer_index = 0) const; |
| 174 | 174 | ||
| 175 | /* Helper function to write a buffer using the appropriate buffer descriptor | 175 | /* Helper function to write a buffer using the appropriate buffer descriptor |
| 176 | * | 176 | * |
| @@ -182,7 +182,7 @@ public: | |||
| 182 | */ | 182 | */ |
| 183 | template <typename ContiguousContainer, | 183 | template <typename ContiguousContainer, |
| 184 | typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> | 184 | typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> |
| 185 | size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { | 185 | std::size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { |
| 186 | using ContiguousType = typename ContiguousContainer::value_type; | 186 | using ContiguousType = typename ContiguousContainer::value_type; |
| 187 | 187 | ||
| 188 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 188 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| @@ -193,19 +193,19 @@ public: | |||
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | /// Helper function to get the size of the input buffer | 195 | /// Helper function to get the size of the input buffer |
| 196 | size_t GetReadBufferSize(int buffer_index = 0) const; | 196 | std::size_t GetReadBufferSize(int buffer_index = 0) const; |
| 197 | 197 | ||
| 198 | /// Helper function to get the size of the output buffer | 198 | /// Helper function to get the size of the output buffer |
| 199 | size_t GetWriteBufferSize(int buffer_index = 0) const; | 199 | std::size_t GetWriteBufferSize(int buffer_index = 0) const; |
| 200 | 200 | ||
| 201 | template <typename T> | 201 | template <typename T> |
| 202 | SharedPtr<T> GetCopyObject(size_t index) { | 202 | SharedPtr<T> GetCopyObject(std::size_t index) { |
| 203 | ASSERT(index < copy_objects.size()); | 203 | ASSERT(index < copy_objects.size()); |
| 204 | return DynamicObjectCast<T>(copy_objects[index]); | 204 | return DynamicObjectCast<T>(copy_objects[index]); |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | template <typename T> | 207 | template <typename T> |
| 208 | SharedPtr<T> GetMoveObject(size_t index) { | 208 | SharedPtr<T> GetMoveObject(std::size_t index) { |
| 209 | ASSERT(index < move_objects.size()); | 209 | ASSERT(index < move_objects.size()); |
| 210 | return DynamicObjectCast<T>(move_objects[index]); | 210 | return DynamicObjectCast<T>(move_objects[index]); |
| 211 | } | 211 | } |
| @@ -223,7 +223,7 @@ public: | |||
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | template <typename T> | 225 | template <typename T> |
| 226 | std::shared_ptr<T> GetDomainRequestHandler(size_t index) const { | 226 | std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const { |
| 227 | return std::static_pointer_cast<T>(domain_request_handlers[index]); | 227 | return std::static_pointer_cast<T>(domain_request_handlers[index]); |
| 228 | } | 228 | } |
| 229 | 229 | ||
| @@ -240,15 +240,15 @@ public: | |||
| 240 | domain_objects.clear(); | 240 | domain_objects.clear(); |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | size_t NumMoveObjects() const { | 243 | std::size_t NumMoveObjects() const { |
| 244 | return move_objects.size(); | 244 | return move_objects.size(); |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | size_t NumCopyObjects() const { | 247 | std::size_t NumCopyObjects() const { |
| 248 | return copy_objects.size(); | 248 | return copy_objects.size(); |
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | size_t NumDomainObjects() const { | 251 | std::size_t NumDomainObjects() const { |
| 252 | return domain_objects.size(); | 252 | return domain_objects.size(); |
| 253 | } | 253 | } |
| 254 | 254 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 615d7901a..3e0800a71 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/hle/kernel/client_port.h" | ||
| 16 | #include "core/hle/kernel/handle_table.h" | 17 | #include "core/hle/kernel/handle_table.h" |
| 17 | #include "core/hle/kernel/kernel.h" | 18 | #include "core/hle/kernel/kernel.h" |
| 18 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| @@ -115,6 +116,7 @@ struct KernelCore::Impl { | |||
| 115 | next_thread_id = 1; | 116 | next_thread_id = 1; |
| 116 | 117 | ||
| 117 | process_list.clear(); | 118 | process_list.clear(); |
| 119 | current_process.reset(); | ||
| 118 | 120 | ||
| 119 | handle_table.Clear(); | 121 | handle_table.Clear(); |
| 120 | resource_limits.fill(nullptr); | 122 | resource_limits.fill(nullptr); |
| @@ -124,6 +126,8 @@ struct KernelCore::Impl { | |||
| 124 | 126 | ||
| 125 | timer_callback_handle_table.Clear(); | 127 | timer_callback_handle_table.Clear(); |
| 126 | timer_callback_event_type = nullptr; | 128 | timer_callback_event_type = nullptr; |
| 129 | |||
| 130 | named_ports.clear(); | ||
| 127 | } | 131 | } |
| 128 | 132 | ||
| 129 | void InitializeResourceLimits(KernelCore& kernel) { | 133 | void InitializeResourceLimits(KernelCore& kernel) { |
| @@ -203,6 +207,7 @@ struct KernelCore::Impl { | |||
| 203 | 207 | ||
| 204 | // Lists all processes that exist in the current session. | 208 | // Lists all processes that exist in the current session. |
| 205 | std::vector<SharedPtr<Process>> process_list; | 209 | std::vector<SharedPtr<Process>> process_list; |
| 210 | SharedPtr<Process> current_process; | ||
| 206 | 211 | ||
| 207 | Kernel::HandleTable handle_table; | 212 | Kernel::HandleTable handle_table; |
| 208 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; | 213 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; |
| @@ -217,6 +222,10 @@ struct KernelCore::Impl { | |||
| 217 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, | 222 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, |
| 218 | // allowing us to simply use a pool index or similar. | 223 | // allowing us to simply use a pool index or similar. |
| 219 | Kernel::HandleTable thread_wakeup_callback_handle_table; | 224 | Kernel::HandleTable thread_wakeup_callback_handle_table; |
| 225 | |||
| 226 | /// Map of named ports managed by the kernel, which can be retrieved using | ||
| 227 | /// the ConnectToPort SVC. | ||
| 228 | NamedPortTable named_ports; | ||
| 220 | }; | 229 | }; |
| 221 | 230 | ||
| 222 | KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {} | 231 | KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {} |
| @@ -257,6 +266,35 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) { | |||
| 257 | impl->process_list.push_back(std::move(process)); | 266 | impl->process_list.push_back(std::move(process)); |
| 258 | } | 267 | } |
| 259 | 268 | ||
| 269 | void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) { | ||
| 270 | impl->current_process = std::move(process); | ||
| 271 | } | ||
| 272 | |||
| 273 | SharedPtr<Process>& KernelCore::CurrentProcess() { | ||
| 274 | return impl->current_process; | ||
| 275 | } | ||
| 276 | |||
| 277 | const SharedPtr<Process>& KernelCore::CurrentProcess() const { | ||
| 278 | return impl->current_process; | ||
| 279 | } | ||
| 280 | |||
| 281 | void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) { | ||
| 282 | impl->named_ports.emplace(std::move(name), std::move(port)); | ||
| 283 | } | ||
| 284 | |||
| 285 | KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) { | ||
| 286 | return impl->named_ports.find(name); | ||
| 287 | } | ||
| 288 | |||
| 289 | KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort( | ||
| 290 | const std::string& name) const { | ||
| 291 | return impl->named_ports.find(name); | ||
| 292 | } | ||
| 293 | |||
| 294 | bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const { | ||
| 295 | return port != impl->named_ports.cend(); | ||
| 296 | } | ||
| 297 | |||
| 260 | u32 KernelCore::CreateNewObjectID() { | 298 | u32 KernelCore::CreateNewObjectID() { |
| 261 | return impl->next_object_id++; | 299 | return impl->next_object_id++; |
| 262 | } | 300 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 089e959ac..c0771ecf0 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | #include <unordered_map> | ||
| 7 | #include "core/hle/kernel/object.h" | 9 | #include "core/hle/kernel/object.h" |
| 8 | 10 | ||
| 9 | template <typename T> | 11 | template <typename T> |
| @@ -15,6 +17,7 @@ struct EventType; | |||
| 15 | 17 | ||
| 16 | namespace Kernel { | 18 | namespace Kernel { |
| 17 | 19 | ||
| 20 | class ClientPort; | ||
| 18 | class HandleTable; | 21 | class HandleTable; |
| 19 | class Process; | 22 | class Process; |
| 20 | class ResourceLimit; | 23 | class ResourceLimit; |
| @@ -25,6 +28,9 @@ enum class ResourceLimitCategory : u8; | |||
| 25 | 28 | ||
| 26 | /// Represents a single instance of the kernel. | 29 | /// Represents a single instance of the kernel. |
| 27 | class KernelCore { | 30 | class KernelCore { |
| 31 | private: | ||
| 32 | using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>; | ||
| 33 | |||
| 28 | public: | 34 | public: |
| 29 | KernelCore(); | 35 | KernelCore(); |
| 30 | ~KernelCore(); | 36 | ~KernelCore(); |
| @@ -59,6 +65,27 @@ public: | |||
| 59 | /// Adds the given shared pointer to an internal list of active processes. | 65 | /// Adds the given shared pointer to an internal list of active processes. |
| 60 | void AppendNewProcess(SharedPtr<Process> process); | 66 | void AppendNewProcess(SharedPtr<Process> process); |
| 61 | 67 | ||
| 68 | /// Makes the given process the new current process. | ||
| 69 | void MakeCurrentProcess(SharedPtr<Process> process); | ||
| 70 | |||
| 71 | /// Retrieves a reference to the current process. | ||
| 72 | SharedPtr<Process>& CurrentProcess(); | ||
| 73 | |||
| 74 | /// Retrieves a const reference to the current process. | ||
| 75 | const SharedPtr<Process>& CurrentProcess() const; | ||
| 76 | |||
| 77 | /// Adds a port to the named port table | ||
| 78 | void AddNamedPort(std::string name, SharedPtr<ClientPort> port); | ||
| 79 | |||
| 80 | /// Finds a port within the named port table with the given name. | ||
| 81 | NamedPortTable::iterator FindNamedPort(const std::string& name); | ||
| 82 | |||
| 83 | /// Finds a port within the named port table with the given name. | ||
| 84 | NamedPortTable::const_iterator FindNamedPort(const std::string& name) const; | ||
| 85 | |||
| 86 | /// Determines whether or not the given port is a valid named port. | ||
| 87 | bool IsValidNamedPort(NamedPortTable::const_iterator port) const; | ||
| 88 | |||
| 62 | private: | 89 | private: |
| 63 | friend class Object; | 90 | friend class Object; |
| 64 | friend class Process; | 91 | friend class Process; |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 36bf0b677..81675eac5 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "core/hle/kernel/object.h" | 16 | #include "core/hle/kernel/object.h" |
| 17 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 19 | #include "core/memory.h" | ||
| 19 | 20 | ||
| 20 | namespace Kernel { | 21 | namespace Kernel { |
| 21 | 22 | ||
| @@ -62,7 +63,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho | |||
| 62 | Handle requesting_thread_handle) { | 63 | Handle requesting_thread_handle) { |
| 63 | // The mutex address must be 4-byte aligned | 64 | // The mutex address must be 4-byte aligned |
| 64 | if ((address % sizeof(u32)) != 0) { | 65 | if ((address % sizeof(u32)) != 0) { |
| 65 | return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 66 | return ERR_INVALID_ADDRESS; |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); | 69 | SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); |
| @@ -100,7 +101,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho | |||
| 100 | ResultCode Mutex::Release(VAddr address) { | 101 | ResultCode Mutex::Release(VAddr address) { |
| 101 | // The mutex address must be 4-byte aligned | 102 | // The mutex address must be 4-byte aligned |
| 102 | if ((address % sizeof(u32)) != 0) { | 103 | if ((address % sizeof(u32)) != 0) { |
| 103 | return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 104 | return ERR_INVALID_ADDRESS; |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); | 107 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b025e323f..914bbe0a1 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -40,8 +40,8 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { | |||
| 40 | return process; | 40 | return process; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | 43 | void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { |
| 44 | for (size_t i = 0; i < len; ++i) { | 44 | for (std::size_t i = 0; i < len; ++i) { |
| 45 | u32 descriptor = kernel_caps[i]; | 45 | u32 descriptor = kernel_caps[i]; |
| 46 | u32 type = descriptor >> 20; | 46 | u32 type = descriptor >> 20; |
| 47 | 47 | ||
| @@ -125,7 +125,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | |||
| 125 | vm_manager.LogLayout(); | 125 | vm_manager.LogLayout(); |
| 126 | status = ProcessStatus::Running; | 126 | status = ProcessStatus::Running; |
| 127 | 127 | ||
| 128 | Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this); | 128 | Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | 131 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { |
| @@ -211,7 +211,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 211 | "Shared memory exceeds bounds of mapped block"); | 211 | "Shared memory exceeds bounds of mapped block"); |
| 212 | 212 | ||
| 213 | const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; | 213 | const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; |
| 214 | size_t backing_block_offset = vma->second.offset + vma_offset; | 214 | std::size_t backing_block_offset = vma->second.offset + vma_offset; |
| 215 | 215 | ||
| 216 | CASCADE_RESULT(auto new_vma, | 216 | CASCADE_RESULT(auto new_vma, |
| 217 | vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, | 217 | vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 1587d40c1..81538f70c 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -59,7 +59,7 @@ class ResourceLimit; | |||
| 59 | 59 | ||
| 60 | struct CodeSet final : public Object { | 60 | struct CodeSet final : public Object { |
| 61 | struct Segment { | 61 | struct Segment { |
| 62 | size_t offset = 0; | 62 | std::size_t offset = 0; |
| 63 | VAddr addr = 0; | 63 | VAddr addr = 0; |
| 64 | u32 size = 0; | 64 | u32 size = 0; |
| 65 | }; | 65 | }; |
| @@ -164,7 +164,7 @@ public: | |||
| 164 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | 164 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them |
| 165 | * to this process. | 165 | * to this process. |
| 166 | */ | 166 | */ |
| 167 | void ParseKernelCaps(const u32* kernel_caps, size_t len); | 167 | void ParseKernelCaps(const u32* kernel_caps, std::size_t len); |
| 168 | 168 | ||
| 169 | /** | 169 | /** |
| 170 | * Applies address space changes and launches the process main thread. | 170 | * Applies address space changes and launches the process main thread. |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 2c729afe3..2c06bb7ce 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -119,7 +119,7 @@ public: | |||
| 119 | /// Backing memory for this shared memory block. | 119 | /// Backing memory for this shared memory block. |
| 120 | std::shared_ptr<std::vector<u8>> backing_block; | 120 | std::shared_ptr<std::vector<u8>> backing_block; |
| 121 | /// Offset into the backing block for this shared memory. | 121 | /// Offset into the backing block for this shared memory. |
| 122 | size_t backing_block_offset; | 122 | std::size_t backing_block_offset; |
| 123 | /// Size of the memory block. Page-aligned. | 123 | /// Size of the memory block. Page-aligned. |
| 124 | u64 size; | 124 | u64 size; |
| 125 | /// Permission restrictions applied to the process which created the block. | 125 | /// Permission restrictions applied to the process which created the block. |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5da71cff0..371fc439e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -35,10 +35,21 @@ | |||
| 35 | #include "core/hle/service/service.h" | 35 | #include "core/hle/service/service.h" |
| 36 | 36 | ||
| 37 | namespace Kernel { | 37 | namespace Kernel { |
| 38 | namespace { | ||
| 39 | constexpr bool Is4KBAligned(VAddr address) { | ||
| 40 | return (address & 0xFFF) == 0; | ||
| 41 | } | ||
| 42 | } // Anonymous namespace | ||
| 38 | 43 | ||
| 39 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 44 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| 40 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | 45 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { |
| 41 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); | 46 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); |
| 47 | |||
| 48 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 4GB. | ||
| 49 | if ((heap_size & 0xFFFFFFFE001FFFFF) != 0) { | ||
| 50 | return ERR_INVALID_SIZE; | ||
| 51 | } | ||
| 52 | |||
| 42 | auto& process = *Core::CurrentProcess(); | 53 | auto& process = *Core::CurrentProcess(); |
| 43 | CASCADE_RESULT(*heap_addr, | 54 | CASCADE_RESULT(*heap_addr, |
| 44 | process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); | 55 | process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); |
| @@ -56,6 +67,15 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state | |||
| 56 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 67 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { |
| 57 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 68 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 58 | src_addr, size); | 69 | src_addr, size); |
| 70 | |||
| 71 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | ||
| 72 | return ERR_INVALID_ADDRESS; | ||
| 73 | } | ||
| 74 | |||
| 75 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 76 | return ERR_INVALID_SIZE; | ||
| 77 | } | ||
| 78 | |||
| 59 | return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); | 79 | return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); |
| 60 | } | 80 | } |
| 61 | 81 | ||
| @@ -63,24 +83,36 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 63 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 83 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { |
| 64 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 84 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 65 | src_addr, size); | 85 | src_addr, size); |
| 86 | |||
| 87 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | ||
| 88 | return ERR_INVALID_ADDRESS; | ||
| 89 | } | ||
| 90 | |||
| 91 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 92 | return ERR_INVALID_SIZE; | ||
| 93 | } | ||
| 94 | |||
| 66 | return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); | 95 | return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); |
| 67 | } | 96 | } |
| 68 | 97 | ||
| 69 | /// Connect to an OS service given the port name, returns the handle to the port to out | 98 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 70 | static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { | 99 | static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { |
| 71 | if (!Memory::IsValidVirtualAddress(port_name_address)) | 100 | if (!Memory::IsValidVirtualAddress(port_name_address)) { |
| 72 | return ERR_NOT_FOUND; | 101 | return ERR_NOT_FOUND; |
| 102 | } | ||
| 73 | 103 | ||
| 74 | static constexpr std::size_t PortNameMaxLength = 11; | 104 | static constexpr std::size_t PortNameMaxLength = 11; |
| 75 | // Read 1 char beyond the max allowed port name to detect names that are too long. | 105 | // Read 1 char beyond the max allowed port name to detect names that are too long. |
| 76 | std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); | 106 | std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); |
| 77 | if (port_name.size() > PortNameMaxLength) | 107 | if (port_name.size() > PortNameMaxLength) { |
| 78 | return ERR_PORT_NAME_TOO_LONG; | 108 | return ERR_PORT_NAME_TOO_LONG; |
| 109 | } | ||
| 79 | 110 | ||
| 80 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); | 111 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); |
| 81 | 112 | ||
| 82 | auto it = Service::g_kernel_named_ports.find(port_name); | 113 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 83 | if (it == Service::g_kernel_named_ports.end()) { | 114 | auto it = kernel.FindNamedPort(port_name); |
| 115 | if (!kernel.IsValidNamedPort(it)) { | ||
| 84 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); | 116 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); |
| 85 | return ERR_NOT_FOUND; | 117 | return ERR_NOT_FOUND; |
| 86 | } | 118 | } |
| @@ -91,7 +123,6 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address | |||
| 91 | CASCADE_RESULT(client_session, client_port->Connect()); | 123 | CASCADE_RESULT(client_session, client_port->Connect()); |
| 92 | 124 | ||
| 93 | // Return the client session | 125 | // Return the client session |
| 94 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 95 | CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); | 126 | CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); |
| 96 | return RESULT_SUCCESS; | 127 | return RESULT_SUCCESS; |
| 97 | } | 128 | } |
| @@ -144,7 +175,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | |||
| 144 | 175 | ||
| 145 | /// Default thread wakeup callback for WaitSynchronization | 176 | /// Default thread wakeup callback for WaitSynchronization |
| 146 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 177 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 147 | SharedPtr<WaitObject> object, size_t index) { | 178 | SharedPtr<WaitObject> object, std::size_t index) { |
| 148 | ASSERT(thread->status == ThreadStatus::WaitSynchAny); | 179 | ASSERT(thread->status == ThreadStatus::WaitSynchAny); |
| 149 | 180 | ||
| 150 | if (reason == ThreadWakeupReason::Timeout) { | 181 | if (reason == ThreadWakeupReason::Timeout) { |
| @@ -249,6 +280,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 249 | "requesting_current_thread_handle=0x{:08X}", | 280 | "requesting_current_thread_handle=0x{:08X}", |
| 250 | holding_thread_handle, mutex_addr, requesting_thread_handle); | 281 | holding_thread_handle, mutex_addr, requesting_thread_handle); |
| 251 | 282 | ||
| 283 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||
| 284 | return ERR_INVALID_ADDRESS_STATE; | ||
| 285 | } | ||
| 286 | |||
| 252 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); | 287 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); |
| 253 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, | 288 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, |
| 254 | requesting_thread_handle); | 289 | requesting_thread_handle); |
| @@ -258,6 +293,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 258 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 293 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { |
| 259 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 294 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); |
| 260 | 295 | ||
| 296 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||
| 297 | return ERR_INVALID_ADDRESS_STATE; | ||
| 298 | } | ||
| 299 | |||
| 261 | return Mutex::Release(mutex_addr); | 300 | return Mutex::Release(mutex_addr); |
| 262 | } | 301 | } |
| 263 | 302 | ||
| @@ -271,7 +310,11 @@ static void Break(u64 reason, u64 info1, u64 info2) { | |||
| 271 | } | 310 | } |
| 272 | 311 | ||
| 273 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 312 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 274 | static void OutputDebugString(VAddr address, s32 len) { | 313 | static void OutputDebugString(VAddr address, u64 len) { |
| 314 | if (len == 0) { | ||
| 315 | return; | ||
| 316 | } | ||
| 317 | |||
| 275 | std::string str(len, '\0'); | 318 | std::string str(len, '\0'); |
| 276 | Memory::ReadBlock(address, str.data(), str.size()); | 319 | Memory::ReadBlock(address, str.data(), str.size()); |
| 277 | LOG_DEBUG(Debug_Emulated, "{}", str); | 320 | LOG_DEBUG(Debug_Emulated, "{}", str); |
| @@ -376,7 +419,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) { | |||
| 376 | /// Sets the priority for the specified thread | 419 | /// Sets the priority for the specified thread |
| 377 | static ResultCode SetThreadPriority(Handle handle, u32 priority) { | 420 | static ResultCode SetThreadPriority(Handle handle, u32 priority) { |
| 378 | if (priority > THREADPRIO_LOWEST) { | 421 | if (priority > THREADPRIO_LOWEST) { |
| 379 | return ERR_OUT_OF_RANGE; | 422 | return ERR_INVALID_THREAD_PRIORITY; |
| 380 | } | 423 | } |
| 381 | 424 | ||
| 382 | auto& kernel = Core::System::GetInstance().Kernel(); | 425 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -409,35 +452,43 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 409 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | 452 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", |
| 410 | shared_memory_handle, addr, size, permissions); | 453 | shared_memory_handle, addr, size, permissions); |
| 411 | 454 | ||
| 455 | if (!Is4KBAligned(addr)) { | ||
| 456 | return ERR_INVALID_ADDRESS; | ||
| 457 | } | ||
| 458 | |||
| 459 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 460 | return ERR_INVALID_SIZE; | ||
| 461 | } | ||
| 462 | |||
| 463 | const auto permissions_type = static_cast<MemoryPermission>(permissions); | ||
| 464 | if (permissions_type != MemoryPermission::Read && | ||
| 465 | permissions_type != MemoryPermission::ReadWrite) { | ||
| 466 | LOG_ERROR(Kernel_SVC, "Invalid permissions=0x{:08X}", permissions); | ||
| 467 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 468 | } | ||
| 469 | |||
| 412 | auto& kernel = Core::System::GetInstance().Kernel(); | 470 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 413 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 471 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); |
| 414 | if (!shared_memory) { | 472 | if (!shared_memory) { |
| 415 | return ERR_INVALID_HANDLE; | 473 | return ERR_INVALID_HANDLE; |
| 416 | } | 474 | } |
| 417 | 475 | ||
| 418 | MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions); | 476 | return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, |
| 419 | switch (permissions_type) { | 477 | MemoryPermission::DontCare); |
| 420 | case MemoryPermission::Read: | ||
| 421 | case MemoryPermission::Write: | ||
| 422 | case MemoryPermission::ReadWrite: | ||
| 423 | case MemoryPermission::Execute: | ||
| 424 | case MemoryPermission::ReadExecute: | ||
| 425 | case MemoryPermission::WriteExecute: | ||
| 426 | case MemoryPermission::ReadWriteExecute: | ||
| 427 | case MemoryPermission::DontCare: | ||
| 428 | return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, | ||
| 429 | MemoryPermission::DontCare); | ||
| 430 | default: | ||
| 431 | LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); | ||
| 432 | } | ||
| 433 | |||
| 434 | return RESULT_SUCCESS; | ||
| 435 | } | 478 | } |
| 436 | 479 | ||
| 437 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { | 480 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { |
| 438 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", | 481 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", |
| 439 | shared_memory_handle, addr, size); | 482 | shared_memory_handle, addr, size); |
| 440 | 483 | ||
| 484 | if (!Is4KBAligned(addr)) { | ||
| 485 | return ERR_INVALID_ADDRESS; | ||
| 486 | } | ||
| 487 | |||
| 488 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 489 | return ERR_INVALID_SIZE; | ||
| 490 | } | ||
| 491 | |||
| 441 | auto& kernel = Core::System::GetInstance().Kernel(); | 492 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 442 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 493 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); |
| 443 | 494 | ||
| @@ -518,10 +569,10 @@ static void ExitProcess() { | |||
| 518 | /// Creates a new thread | 569 | /// Creates a new thread |
| 519 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | 570 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, |
| 520 | u32 priority, s32 processor_id) { | 571 | u32 priority, s32 processor_id) { |
| 521 | std::string name = fmt::format("unknown-{:X}", entry_point); | 572 | std::string name = fmt::format("thread-{:X}", entry_point); |
| 522 | 573 | ||
| 523 | if (priority > THREADPRIO_LOWEST) { | 574 | if (priority > THREADPRIO_LOWEST) { |
| 524 | return ERR_OUT_OF_RANGE; | 575 | return ERR_INVALID_THREAD_PRIORITY; |
| 525 | } | 576 | } |
| 526 | 577 | ||
| 527 | SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; | 578 | SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; |
| @@ -542,8 +593,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 542 | case THREADPROCESSORID_3: | 593 | case THREADPROCESSORID_3: |
| 543 | break; | 594 | break; |
| 544 | default: | 595 | default: |
| 545 | ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id); | 596 | LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id); |
| 546 | break; | 597 | return ERR_INVALID_PROCESSOR_ID; |
| 547 | } | 598 | } |
| 548 | 599 | ||
| 549 | auto& kernel = Core::System::GetInstance().Kernel(); | 600 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -641,16 +692,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 641 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", | 692 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", |
| 642 | condition_variable_addr, target); | 693 | condition_variable_addr, target); |
| 643 | 694 | ||
| 644 | auto RetrieveWaitingThreads = | 695 | auto RetrieveWaitingThreads = [](std::size_t core_index, |
| 645 | [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) { | 696 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 646 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 697 | VAddr condvar_addr) { |
| 647 | auto& thread_list = scheduler->GetThreadList(); | 698 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 699 | auto& thread_list = scheduler->GetThreadList(); | ||
| 648 | 700 | ||
| 649 | for (auto& thread : thread_list) { | 701 | for (auto& thread : thread_list) { |
| 650 | if (thread->condvar_wait_address == condvar_addr) | 702 | if (thread->condvar_wait_address == condvar_addr) |
| 651 | waiting_threads.push_back(thread); | 703 | waiting_threads.push_back(thread); |
| 652 | } | 704 | } |
| 653 | }; | 705 | }; |
| 654 | 706 | ||
| 655 | // Retrieve a list of all threads that are waiting for this condition variable. | 707 | // Retrieve a list of all threads that are waiting for this condition variable. |
| 656 | std::vector<SharedPtr<Thread>> waiting_threads; | 708 | std::vector<SharedPtr<Thread>> waiting_threads; |
| @@ -666,7 +718,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 666 | 718 | ||
| 667 | // Only process up to 'target' threads, unless 'target' is -1, in which case process | 719 | // Only process up to 'target' threads, unless 'target' is -1, in which case process |
| 668 | // them all. | 720 | // them all. |
| 669 | size_t last = waiting_threads.size(); | 721 | std::size_t last = waiting_threads.size(); |
| 670 | if (target != -1) | 722 | if (target != -1) |
| 671 | last = target; | 723 | last = target; |
| 672 | 724 | ||
| @@ -674,12 +726,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 674 | if (last > waiting_threads.size()) | 726 | if (last > waiting_threads.size()) |
| 675 | return RESULT_SUCCESS; | 727 | return RESULT_SUCCESS; |
| 676 | 728 | ||
| 677 | for (size_t index = 0; index < last; ++index) { | 729 | for (std::size_t index = 0; index < last; ++index) { |
| 678 | auto& thread = waiting_threads[index]; | 730 | auto& thread = waiting_threads[index]; |
| 679 | 731 | ||
| 680 | ASSERT(thread->condvar_wait_address == condition_variable_addr); | 732 | ASSERT(thread->condvar_wait_address == condition_variable_addr); |
| 681 | 733 | ||
| 682 | size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); | 734 | std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); |
| 683 | 735 | ||
| 684 | auto& monitor = Core::System::GetInstance().Monitor(); | 736 | auto& monitor = Core::System::GetInstance().Monitor(); |
| 685 | 737 | ||
| @@ -892,12 +944,28 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 892 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, | 944 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, |
| 893 | local_permissions, remote_permissions); | 945 | local_permissions, remote_permissions); |
| 894 | 946 | ||
| 947 | // Size must be a multiple of 4KB and be less than or equal to | ||
| 948 | // approx. 8 GB (actually (1GB - 512B) * 8) | ||
| 949 | if (size == 0 || (size & 0xFFFFFFFE00000FFF) != 0) { | ||
| 950 | return ERR_INVALID_SIZE; | ||
| 951 | } | ||
| 952 | |||
| 953 | const auto local_perms = static_cast<MemoryPermission>(local_permissions); | ||
| 954 | if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) { | ||
| 955 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 956 | } | ||
| 957 | |||
| 958 | const auto remote_perms = static_cast<MemoryPermission>(remote_permissions); | ||
| 959 | if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite && | ||
| 960 | remote_perms != MemoryPermission::DontCare) { | ||
| 961 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 962 | } | ||
| 963 | |||
| 895 | auto& kernel = Core::System::GetInstance().Kernel(); | 964 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 896 | auto& handle_table = kernel.HandleTable(); | 965 | auto& handle_table = kernel.HandleTable(); |
| 897 | auto shared_mem_handle = | 966 | auto shared_mem_handle = |
| 898 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, | 967 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, |
| 899 | static_cast<MemoryPermission>(local_permissions), | 968 | local_perms, remote_perms); |
| 900 | static_cast<MemoryPermission>(remote_permissions)); | ||
| 901 | 969 | ||
| 902 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); | 970 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); |
| 903 | return RESULT_SUCCESS; | 971 | return RESULT_SUCCESS; |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 79c3fe31b..fea9ba5ea 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -13,7 +13,9 @@ | |||
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | #define PARAM(n) Core::CurrentArmInterface().GetReg(n) | 16 | static inline u64 Param(int n) { |
| 17 | return Core::CurrentArmInterface().GetReg(n); | ||
| 18 | } | ||
| 17 | 19 | ||
| 18 | /** | 20 | /** |
| 19 | * HLE a function return from the current ARM userland process | 21 | * HLE a function return from the current ARM userland process |
| @@ -28,23 +30,23 @@ static inline void FuncReturn(u64 res) { | |||
| 28 | 30 | ||
| 29 | template <ResultCode func(u64)> | 31 | template <ResultCode func(u64)> |
| 30 | void SvcWrap() { | 32 | void SvcWrap() { |
| 31 | FuncReturn(func(PARAM(0)).raw); | 33 | FuncReturn(func(Param(0)).raw); |
| 32 | } | 34 | } |
| 33 | 35 | ||
| 34 | template <ResultCode func(u32)> | 36 | template <ResultCode func(u32)> |
| 35 | void SvcWrap() { | 37 | void SvcWrap() { |
| 36 | FuncReturn(func((u32)PARAM(0)).raw); | 38 | FuncReturn(func((u32)Param(0)).raw); |
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | template <ResultCode func(u32, u32)> | 41 | template <ResultCode func(u32, u32)> |
| 40 | void SvcWrap() { | 42 | void SvcWrap() { |
| 41 | FuncReturn(func((u32)PARAM(0), (u32)PARAM(1)).raw); | 43 | FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | template <ResultCode func(u32*, u32)> | 46 | template <ResultCode func(u32*, u32)> |
| 45 | void SvcWrap() { | 47 | void SvcWrap() { |
| 46 | u32 param_1 = 0; | 48 | u32 param_1 = 0; |
| 47 | u32 retval = func(¶m_1, (u32)PARAM(1)).raw; | 49 | u32 retval = func(¶m_1, (u32)Param(1)).raw; |
| 48 | Core::CurrentArmInterface().SetReg(1, param_1); | 50 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 49 | FuncReturn(retval); | 51 | FuncReturn(retval); |
| 50 | } | 52 | } |
| @@ -52,39 +54,39 @@ void SvcWrap() { | |||
| 52 | template <ResultCode func(u32*, u64)> | 54 | template <ResultCode func(u32*, u64)> |
| 53 | void SvcWrap() { | 55 | void SvcWrap() { |
| 54 | u32 param_1 = 0; | 56 | u32 param_1 = 0; |
| 55 | u32 retval = func(¶m_1, PARAM(1)).raw; | 57 | u32 retval = func(¶m_1, Param(1)).raw; |
| 56 | Core::CurrentArmInterface().SetReg(1, param_1); | 58 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 57 | FuncReturn(retval); | 59 | FuncReturn(retval); |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | template <ResultCode func(u64, s32)> | 62 | template <ResultCode func(u64, s32)> |
| 61 | void SvcWrap() { | 63 | void SvcWrap() { |
| 62 | FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); | 64 | FuncReturn(func(Param(0), (s32)Param(1)).raw); |
| 63 | } | 65 | } |
| 64 | 66 | ||
| 65 | template <ResultCode func(u64*, u64)> | 67 | template <ResultCode func(u64*, u64)> |
| 66 | void SvcWrap() { | 68 | void SvcWrap() { |
| 67 | u64 param_1 = 0; | 69 | u64 param_1 = 0; |
| 68 | u32 retval = func(¶m_1, PARAM(1)).raw; | 70 | u32 retval = func(¶m_1, Param(1)).raw; |
| 69 | Core::CurrentArmInterface().SetReg(1, param_1); | 71 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 70 | FuncReturn(retval); | 72 | FuncReturn(retval); |
| 71 | } | 73 | } |
| 72 | 74 | ||
| 73 | template <ResultCode func(u32, u64)> | 75 | template <ResultCode func(u32, u64)> |
| 74 | void SvcWrap() { | 76 | void SvcWrap() { |
| 75 | FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw); | 77 | FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); |
| 76 | } | 78 | } |
| 77 | 79 | ||
| 78 | template <ResultCode func(u32, u32, u64)> | 80 | template <ResultCode func(u32, u32, u64)> |
| 79 | void SvcWrap() { | 81 | void SvcWrap() { |
| 80 | FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw); | 82 | FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); |
| 81 | } | 83 | } |
| 82 | 84 | ||
| 83 | template <ResultCode func(u32, u32*, u64*)> | 85 | template <ResultCode func(u32, u32*, u64*)> |
| 84 | void SvcWrap() { | 86 | void SvcWrap() { |
| 85 | u32 param_1 = 0; | 87 | u32 param_1 = 0; |
| 86 | u64 param_2 = 0; | 88 | u64 param_2 = 0; |
| 87 | ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); | 89 | ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); |
| 88 | Core::CurrentArmInterface().SetReg(1, param_1); | 90 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 89 | Core::CurrentArmInterface().SetReg(2, param_2); | 91 | Core::CurrentArmInterface().SetReg(2, param_2); |
| 90 | FuncReturn(retval.raw); | 92 | FuncReturn(retval.raw); |
| @@ -93,46 +95,46 @@ void SvcWrap() { | |||
| 93 | template <ResultCode func(u64, u64, u32, u32)> | 95 | template <ResultCode func(u64, u64, u32, u32)> |
| 94 | void SvcWrap() { | 96 | void SvcWrap() { |
| 95 | FuncReturn( | 97 | FuncReturn( |
| 96 | func(PARAM(0), PARAM(1), (u32)(PARAM(3) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw); | 98 | func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); |
| 97 | } | 99 | } |
| 98 | 100 | ||
| 99 | template <ResultCode func(u32, u64, u32)> | 101 | template <ResultCode func(u32, u64, u32)> |
| 100 | void SvcWrap() { | 102 | void SvcWrap() { |
| 101 | FuncReturn(func((u32)PARAM(0), PARAM(1), (u32)PARAM(2)).raw); | 103 | FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); |
| 102 | } | 104 | } |
| 103 | 105 | ||
| 104 | template <ResultCode func(u64, u64, u64)> | 106 | template <ResultCode func(u64, u64, u64)> |
| 105 | void SvcWrap() { | 107 | void SvcWrap() { |
| 106 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | 108 | FuncReturn(func(Param(0), Param(1), Param(2)).raw); |
| 107 | } | 109 | } |
| 108 | 110 | ||
| 109 | template <ResultCode func(u32, u64, u64, u32)> | 111 | template <ResultCode func(u32, u64, u64, u32)> |
| 110 | void SvcWrap() { | 112 | void SvcWrap() { |
| 111 | FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw); | 113 | FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); |
| 112 | } | 114 | } |
| 113 | 115 | ||
| 114 | template <ResultCode func(u32, u64, u64)> | 116 | template <ResultCode func(u32, u64, u64)> |
| 115 | void SvcWrap() { | 117 | void SvcWrap() { |
| 116 | FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw); | 118 | FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); |
| 117 | } | 119 | } |
| 118 | 120 | ||
| 119 | template <ResultCode func(u32*, u64, u64, s64)> | 121 | template <ResultCode func(u32*, u64, u64, s64)> |
| 120 | void SvcWrap() { | 122 | void SvcWrap() { |
| 121 | u32 param_1 = 0; | 123 | u32 param_1 = 0; |
| 122 | ResultCode retval = func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)); | 124 | ResultCode retval = func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); |
| 123 | Core::CurrentArmInterface().SetReg(1, param_1); | 125 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 124 | FuncReturn(retval.raw); | 126 | FuncReturn(retval.raw); |
| 125 | } | 127 | } |
| 126 | 128 | ||
| 127 | template <ResultCode func(u64, u64, u32, s64)> | 129 | template <ResultCode func(u64, u64, u32, s64)> |
| 128 | void SvcWrap() { | 130 | void SvcWrap() { |
| 129 | FuncReturn(func(PARAM(0), PARAM(1), (u32)PARAM(2), (s64)PARAM(3)).raw); | 131 | FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); |
| 130 | } | 132 | } |
| 131 | 133 | ||
| 132 | template <ResultCode func(u64*, u64, u64, u64)> | 134 | template <ResultCode func(u64*, u64, u64, u64)> |
| 133 | void SvcWrap() { | 135 | void SvcWrap() { |
| 134 | u64 param_1 = 0; | 136 | u64 param_1 = 0; |
| 135 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; | 137 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw; |
| 136 | Core::CurrentArmInterface().SetReg(1, param_1); | 138 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 137 | FuncReturn(retval); | 139 | FuncReturn(retval); |
| 138 | } | 140 | } |
| @@ -141,7 +143,7 @@ template <ResultCode func(u32*, u64, u64, u64, u32, s32)> | |||
| 141 | void SvcWrap() { | 143 | void SvcWrap() { |
| 142 | u32 param_1 = 0; | 144 | u32 param_1 = 0; |
| 143 | u32 retval = | 145 | u32 retval = |
| 144 | func(¶m_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF)) | 146 | func(¶m_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) |
| 145 | .raw; | 147 | .raw; |
| 146 | Core::CurrentArmInterface().SetReg(1, param_1); | 148 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 147 | FuncReturn(retval); | 149 | FuncReturn(retval); |
| @@ -151,13 +153,13 @@ template <ResultCode func(MemoryInfo*, PageInfo*, u64)> | |||
| 151 | void SvcWrap() { | 153 | void SvcWrap() { |
| 152 | MemoryInfo memory_info = {}; | 154 | MemoryInfo memory_info = {}; |
| 153 | PageInfo page_info = {}; | 155 | PageInfo page_info = {}; |
| 154 | u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; | 156 | u32 retval = func(&memory_info, &page_info, Param(2)).raw; |
| 155 | 157 | ||
| 156 | Memory::Write64(PARAM(0), memory_info.base_address); | 158 | Memory::Write64(Param(0), memory_info.base_address); |
| 157 | Memory::Write64(PARAM(0) + 8, memory_info.size); | 159 | Memory::Write64(Param(0) + 8, memory_info.size); |
| 158 | Memory::Write32(PARAM(0) + 16, memory_info.type); | 160 | Memory::Write32(Param(0) + 16, memory_info.type); |
| 159 | Memory::Write32(PARAM(0) + 20, memory_info.attributes); | 161 | Memory::Write32(Param(0) + 20, memory_info.attributes); |
| 160 | Memory::Write32(PARAM(0) + 24, memory_info.permission); | 162 | Memory::Write32(Param(0) + 24, memory_info.permission); |
| 161 | 163 | ||
| 162 | FuncReturn(retval); | 164 | FuncReturn(retval); |
| 163 | } | 165 | } |
| @@ -165,7 +167,7 @@ void SvcWrap() { | |||
| 165 | template <ResultCode func(u32*, u64, u64, u32)> | 167 | template <ResultCode func(u32*, u64, u64, u32)> |
| 166 | void SvcWrap() { | 168 | void SvcWrap() { |
| 167 | u32 param_1 = 0; | 169 | u32 param_1 = 0; |
| 168 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; | 170 | u32 retval = func(¶m_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; |
| 169 | Core::CurrentArmInterface().SetReg(1, param_1); | 171 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 170 | FuncReturn(retval); | 172 | FuncReturn(retval); |
| 171 | } | 173 | } |
| @@ -174,7 +176,7 @@ template <ResultCode func(Handle*, u64, u32, u32)> | |||
| 174 | void SvcWrap() { | 176 | void SvcWrap() { |
| 175 | u32 param_1 = 0; | 177 | u32 param_1 = 0; |
| 176 | u32 retval = | 178 | u32 retval = |
| 177 | func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; | 179 | func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; |
| 178 | Core::CurrentArmInterface().SetReg(1, param_1); | 180 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 179 | FuncReturn(retval); | 181 | FuncReturn(retval); |
| 180 | } | 182 | } |
| @@ -182,14 +184,14 @@ void SvcWrap() { | |||
| 182 | template <ResultCode func(u64, u32, s32, s64)> | 184 | template <ResultCode func(u64, u32, s32, s64)> |
| 183 | void SvcWrap() { | 185 | void SvcWrap() { |
| 184 | FuncReturn( | 186 | FuncReturn( |
| 185 | func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)) | 187 | func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) |
| 186 | .raw); | 188 | .raw); |
| 187 | } | 189 | } |
| 188 | 190 | ||
| 189 | template <ResultCode func(u64, u32, s32, s32)> | 191 | template <ResultCode func(u64, u32, s32, s32)> |
| 190 | void SvcWrap() { | 192 | void SvcWrap() { |
| 191 | FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), | 193 | FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), |
| 192 | (s32)(PARAM(3) & 0xFFFFFFFF)) | 194 | (s32)(Param(3) & 0xFFFFFFFF)) |
| 193 | .raw); | 195 | .raw); |
| 194 | } | 196 | } |
| 195 | 197 | ||
| @@ -219,20 +221,17 @@ void SvcWrap() { | |||
| 219 | 221 | ||
| 220 | template <void func(s64)> | 222 | template <void func(s64)> |
| 221 | void SvcWrap() { | 223 | void SvcWrap() { |
| 222 | func((s64)PARAM(0)); | 224 | func((s64)Param(0)); |
| 223 | } | 225 | } |
| 224 | 226 | ||
| 225 | template <void func(u64, s32 len)> | 227 | template <void func(u64, u64 len)> |
| 226 | void SvcWrap() { | 228 | void SvcWrap() { |
| 227 | func(PARAM(0), (s32)(PARAM(1) & 0xFFFFFFFF)); | 229 | func(Param(0), Param(1)); |
| 228 | } | 230 | } |
| 229 | 231 | ||
| 230 | template <void func(u64, u64, u64)> | 232 | template <void func(u64, u64, u64)> |
| 231 | void SvcWrap() { | 233 | void SvcWrap() { |
| 232 | func(PARAM(0), PARAM(1), PARAM(2)); | 234 | func(Param(0), Param(1), Param(2)); |
| 233 | } | 235 | } |
| 234 | 236 | ||
| 235 | #undef PARAM | ||
| 236 | #undef FuncReturn | ||
| 237 | |||
| 238 | } // namespace Kernel | 237 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3d10d9af2..c2d7535c9 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -217,8 +217,8 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd | |||
| 217 | context.cpu_registers[0] = arg; | 217 | context.cpu_registers[0] = arg; |
| 218 | context.pc = entry_point; | 218 | context.pc = entry_point; |
| 219 | context.sp = stack_top; | 219 | context.sp = stack_top; |
| 220 | context.cpsr = 0; | 220 | context.pstate = 0; |
| 221 | context.fpscr = 0; | 221 | context.fpcr = 0; |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, | 224 | ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, |
| @@ -227,12 +227,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | |||
| 227 | // Check if priority is in ranged. Lowest priority -> highest priority id. | 227 | // Check if priority is in ranged. Lowest priority -> highest priority id. |
| 228 | if (priority > THREADPRIO_LOWEST) { | 228 | if (priority > THREADPRIO_LOWEST) { |
| 229 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); | 229 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); |
| 230 | return ERR_OUT_OF_RANGE; | 230 | return ERR_INVALID_THREAD_PRIORITY; |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | if (processor_id > THREADPROCESSORID_MAX) { | 233 | if (processor_id > THREADPROCESSORID_MAX) { |
| 234 | LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); | 234 | LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); |
| 235 | return ERR_OUT_OF_RANGE_KERNEL; | 235 | return ERR_INVALID_PROCESSOR_ID; |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | // TODO(yuriks): Other checks, returning 0xD9001BEA | 238 | // TODO(yuriks): Other checks, returning 0xD9001BEA |
| @@ -275,7 +275,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | |||
| 275 | available_slot = 0; // Use the first slot in the new page | 275 | available_slot = 0; // Use the first slot in the new page |
| 276 | 276 | ||
| 277 | // Allocate some memory from the end of the linear heap for this region. | 277 | // Allocate some memory from the end of the linear heap for this region. |
| 278 | const size_t offset = thread->tls_memory->size(); | 278 | const std::size_t offset = thread->tls_memory->size(); |
| 279 | thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); | 279 | thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); |
| 280 | 280 | ||
| 281 | auto& vm_manager = owner_process->vm_manager; | 281 | auto& vm_manager = owner_process->vm_manager; |
| @@ -311,13 +311,13 @@ void Thread::BoostPriority(u32 priority) { | |||
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, | 313 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, |
| 314 | SharedPtr<Process> owner_process) { | 314 | Process& owner_process) { |
| 315 | // Setup page table so we can write to memory | 315 | // Setup page table so we can write to memory |
| 316 | SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); | 316 | SetCurrentPageTable(&owner_process.vm_manager.page_table); |
| 317 | 317 | ||
| 318 | // Initialize new "main" thread | 318 | // Initialize new "main" thread |
| 319 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, | 319 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, |
| 320 | Memory::STACK_AREA_VADDR_END, std::move(owner_process)); | 320 | Memory::STACK_AREA_VADDR_END, &owner_process); |
| 321 | 321 | ||
| 322 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 322 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 323 | 323 | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 20f50458b..91e9b79ec 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -15,6 +15,12 @@ | |||
| 15 | #include "core/hle/kernel/wait_object.h" | 15 | #include "core/hle/kernel/wait_object.h" |
| 16 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| 17 | 17 | ||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | class KernelCore; | ||
| 21 | class Process; | ||
| 22 | class Scheduler; | ||
| 23 | |||
| 18 | enum ThreadPriority : u32 { | 24 | enum ThreadPriority : u32 { |
| 19 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority | 25 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority |
| 20 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps | 26 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps |
| @@ -54,12 +60,6 @@ enum class ThreadWakeupReason { | |||
| 54 | Timeout // The thread was woken up due to a wait timeout. | 60 | Timeout // The thread was woken up due to a wait timeout. |
| 55 | }; | 61 | }; |
| 56 | 62 | ||
| 57 | namespace Kernel { | ||
| 58 | |||
| 59 | class KernelCore; | ||
| 60 | class Process; | ||
| 61 | class Scheduler; | ||
| 62 | |||
| 63 | class Thread final : public WaitObject { | 63 | class Thread final : public WaitObject { |
| 64 | public: | 64 | public: |
| 65 | /** | 65 | /** |
| @@ -254,7 +254,7 @@ public: | |||
| 254 | Handle callback_handle; | 254 | Handle callback_handle; |
| 255 | 255 | ||
| 256 | using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 256 | using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 257 | SharedPtr<WaitObject> object, size_t index); | 257 | SharedPtr<WaitObject> object, std::size_t index); |
| 258 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 258 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread |
| 259 | // was waiting via WaitSynchronizationN then the object will be the last object that became | 259 | // was waiting via WaitSynchronizationN then the object will be the last object that became |
| 260 | // available. In case of a timeout, the object will be nullptr. | 260 | // available. In case of a timeout, the object will be nullptr. |
| @@ -281,7 +281,7 @@ private: | |||
| 281 | * @return A shared pointer to the main thread | 281 | * @return A shared pointer to the main thread |
| 282 | */ | 282 | */ |
| 283 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, | 283 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, |
| 284 | SharedPtr<Process> owner_process); | 284 | Process& owner_process); |
| 285 | 285 | ||
| 286 | /** | 286 | /** |
| 287 | * Gets the current thread | 287 | * Gets the current thread |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 479cacb62..608cbd57b 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -86,7 +86,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | |||
| 86 | 86 | ||
| 87 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | 87 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, |
| 88 | std::shared_ptr<std::vector<u8>> block, | 88 | std::shared_ptr<std::vector<u8>> block, |
| 89 | size_t offset, u64 size, | 89 | std::size_t offset, u64 size, |
| 90 | MemoryState state) { | 90 | MemoryState state) { |
| 91 | ASSERT(block != nullptr); | 91 | ASSERT(block != nullptr); |
| 92 | ASSERT(offset + size <= block->size()); | 92 | ASSERT(offset + size <= block->size()); |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 98bd04bea..de75036c0 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -81,7 +81,7 @@ struct VirtualMemoryArea { | |||
| 81 | /// Memory block backing this VMA. | 81 | /// Memory block backing this VMA. |
| 82 | std::shared_ptr<std::vector<u8>> backing_block = nullptr; | 82 | std::shared_ptr<std::vector<u8>> backing_block = nullptr; |
| 83 | /// Offset into the backing_memory the mapping starts from. | 83 | /// Offset into the backing_memory the mapping starts from. |
| 84 | size_t offset = 0; | 84 | std::size_t offset = 0; |
| 85 | 85 | ||
| 86 | // Settings for type = BackingMemory | 86 | // Settings for type = BackingMemory |
| 87 | /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. | 87 | /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. |
| @@ -147,7 +147,7 @@ public: | |||
| 147 | * @param state MemoryState tag to attach to the VMA. | 147 | * @param state MemoryState tag to attach to the VMA. |
| 148 | */ | 148 | */ |
| 149 | ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, | 149 | ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, |
| 150 | size_t offset, u64 size, MemoryState state); | 150 | std::size_t offset, u64 size, MemoryState state); |
| 151 | 151 | ||
| 152 | /** | 152 | /** |
| 153 | * Maps an unmanaged host memory pointer at a given address. | 153 | * Maps an unmanaged host memory pointer at a given address. |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index eef00b729..b190ceb98 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -81,7 +81,7 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { | |||
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | size_t index = thread->GetWaitObjectIndex(this); | 84 | std::size_t index = thread->GetWaitObjectIndex(this); |
| 85 | 85 | ||
| 86 | for (auto& object : thread->wait_objects) | 86 | for (auto& object : thread->wait_objects) |
| 87 | object->RemoveWaitingThread(thread.get()); | 87 | object->RemoveWaitingThread(thread.get()); |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1502dbf55..e61748ca3 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -34,7 +34,7 @@ public: | |||
| 34 | static const FunctionInfo functions[] = { | 34 | static const FunctionInfo functions[] = { |
| 35 | {0, &IProfile::Get, "Get"}, | 35 | {0, &IProfile::Get, "Get"}, |
| 36 | {1, &IProfile::GetBase, "GetBase"}, | 36 | {1, &IProfile::GetBase, "GetBase"}, |
| 37 | {10, nullptr, "GetImageSize"}, | 37 | {10, &IProfile::GetImageSize, "GetImageSize"}, |
| 38 | {11, &IProfile::LoadImage, "LoadImage"}, | 38 | {11, &IProfile::LoadImage, "LoadImage"}, |
| 39 | }; | 39 | }; |
| 40 | RegisterHandlers(functions); | 40 | RegisterHandlers(functions); |
| @@ -93,6 +93,14 @@ private: | |||
| 93 | rb.Push<u32>(jpeg_size); | 93 | rb.Push<u32>(jpeg_size); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | void GetImageSize(Kernel::HLERequestContext& ctx) { | ||
| 97 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 98 | constexpr u32 jpeg_size = 107; | ||
| 99 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 100 | rb.Push(RESULT_SUCCESS); | ||
| 101 | rb.Push<u32>(jpeg_size); | ||
| 102 | } | ||
| 103 | |||
| 96 | const ProfileManager& profile_manager; | 104 | const ProfileManager& profile_manager; |
| 97 | UUID user_id; ///< The user id this profile refers to. | 105 | UUID user_id; ///< The user id this profile refers to. |
| 98 | }; | 106 | }; |
| @@ -122,11 +130,10 @@ private: | |||
| 122 | 130 | ||
| 123 | void GetAccountId(Kernel::HLERequestContext& ctx) { | 131 | void GetAccountId(Kernel::HLERequestContext& ctx) { |
| 124 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 132 | LOG_WARNING(Service_ACC, "(STUBBED) called"); |
| 125 | // TODO(Subv): Find out what this actually does and implement it. Stub it as an error for | 133 | // Should return a nintendo account ID |
| 126 | // now since we do not implement NNID. Returning a bogus id here will cause games to send | 134 | IPC::ResponseBuilder rb{ctx, 4}; |
| 127 | // invalid IPC requests after ListOpenUsers is called. | 135 | rb.Push(RESULT_SUCCESS); |
| 128 | IPC::ResponseBuilder rb{ctx, 2}; | 136 | rb.PushRaw<u64>(1); |
| 129 | rb.Push(ResultCode(-1)); | ||
| 130 | } | 137 | } |
| 131 | }; | 138 | }; |
| 132 | 139 | ||
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index 9bd595a37..e84d9f7cf 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp | |||
| @@ -18,4 +18,6 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 18 | RegisterHandlers(functions); | 18 | RegisterHandlers(functions); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | ACC_AA::~ACC_AA() = default; | ||
| 22 | |||
| 21 | } // namespace Service::Account | 23 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h index 2e08c781a..9edb0421b 100644 --- a/src/core/hle/service/acc/acc_aa.h +++ b/src/core/hle/service/acc/acc_aa.h | |||
| @@ -12,6 +12,7 @@ class ACC_AA final : public Module::Interface { | |||
| 12 | public: | 12 | public: |
| 13 | explicit ACC_AA(std::shared_ptr<Module> module, | 13 | explicit ACC_AA(std::shared_ptr<Module> module, |
| 14 | std::shared_ptr<ProfileManager> profile_manager); | 14 | std::shared_ptr<ProfileManager> profile_manager); |
| 15 | ~ACC_AA() override; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | } // namespace Service::Account | 18 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 0218ee859..ad455c3a7 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -51,4 +51,6 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 51 | RegisterHandlers(functions); | 51 | RegisterHandlers(functions); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | ACC_SU::~ACC_SU() = default; | ||
| 55 | |||
| 54 | } // namespace Service::Account | 56 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h index 79a47d88d..fcced063a 100644 --- a/src/core/hle/service/acc/acc_su.h +++ b/src/core/hle/service/acc/acc_su.h | |||
| @@ -6,14 +6,13 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hle/service/acc/acc.h" | 7 | #include "core/hle/service/acc/acc.h" |
| 8 | 8 | ||
| 9 | namespace Service { | 9 | namespace Service::Account { |
| 10 | namespace Account { | ||
| 11 | 10 | ||
| 12 | class ACC_SU final : public Module::Interface { | 11 | class ACC_SU final : public Module::Interface { |
| 13 | public: | 12 | public: |
| 14 | explicit ACC_SU(std::shared_ptr<Module> module, | 13 | explicit ACC_SU(std::shared_ptr<Module> module, |
| 15 | std::shared_ptr<ProfileManager> profile_manager); | 14 | std::shared_ptr<ProfileManager> profile_manager); |
| 15 | ~ACC_SU() override; | ||
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | } // namespace Account | 18 | } // namespace Service::Account |
| 19 | } // namespace Service | ||
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 84a4d05b8..72d4adf35 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -31,4 +31,6 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 31 | RegisterHandlers(functions); | 31 | RegisterHandlers(functions); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | ACC_U0::~ACC_U0() = default; | ||
| 35 | |||
| 34 | } // namespace Service::Account | 36 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index e8a114f99..a1290e0bd 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h | |||
| @@ -12,6 +12,7 @@ class ACC_U0 final : public Module::Interface { | |||
| 12 | public: | 12 | public: |
| 13 | explicit ACC_U0(std::shared_ptr<Module> module, | 13 | explicit ACC_U0(std::shared_ptr<Module> module, |
| 14 | std::shared_ptr<ProfileManager> profile_manager); | 14 | std::shared_ptr<ProfileManager> profile_manager); |
| 15 | ~ACC_U0() override; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | } // namespace Service::Account | 18 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 495693949..d480f08e5 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp | |||
| @@ -38,4 +38,6 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 38 | RegisterHandlers(functions); | 38 | RegisterHandlers(functions); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | ACC_U1::~ACC_U1() = default; | ||
| 42 | |||
| 41 | } // namespace Service::Account | 43 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h index a77520e6f..9e79daee3 100644 --- a/src/core/hle/service/acc/acc_u1.h +++ b/src/core/hle/service/acc/acc_u1.h | |||
| @@ -12,6 +12,7 @@ class ACC_U1 final : public Module::Interface { | |||
| 12 | public: | 12 | public: |
| 13 | explicit ACC_U1(std::shared_ptr<Module> module, | 13 | explicit ACC_U1(std::shared_ptr<Module> module, |
| 14 | std::shared_ptr<ProfileManager> profile_manager); | 14 | std::shared_ptr<ProfileManager> profile_manager); |
| 15 | ~ACC_U1() override; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | } // namespace Service::Account | 18 | } // namespace Service::Account |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index e0b03d763..bcb3475db 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -25,13 +25,15 @@ const UUID& UUID::Generate() { | |||
| 25 | ProfileManager::ProfileManager() { | 25 | ProfileManager::ProfileManager() { |
| 26 | // TODO(ogniK): Create the default user we have for now until loading/saving users is added | 26 | // TODO(ogniK): Create the default user we have for now until loading/saving users is added |
| 27 | auto user_uuid = UUID{1, 0}; | 27 | auto user_uuid = UUID{1, 0}; |
| 28 | CreateNewUser(user_uuid, Settings::values.username); | 28 | ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); |
| 29 | OpenUser(user_uuid); | 29 | OpenUser(user_uuid); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | ProfileManager::~ProfileManager() = default; | ||
| 33 | |||
| 32 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the | 34 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the |
| 33 | /// internal management of the users profiles | 35 | /// internal management of the users profiles |
| 34 | boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | 36 | boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { |
| 35 | if (user_count >= MAX_USERS) { | 37 | if (user_count >= MAX_USERS) { |
| 36 | return boost::none; | 38 | return boost::none; |
| 37 | } | 39 | } |
| @@ -40,7 +42,7 @@ boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | |||
| 40 | } | 42 | } |
| 41 | 43 | ||
| 42 | /// Deletes a specific profile based on it's profile index | 44 | /// Deletes a specific profile based on it's profile index |
| 43 | bool ProfileManager::RemoveProfileAtIndex(size_t index) { | 45 | bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { |
| 44 | if (index >= MAX_USERS || index >= user_count) { | 46 | if (index >= MAX_USERS || index >= user_count) { |
| 45 | return false; | 47 | return false; |
| 46 | } | 48 | } |
| @@ -89,7 +91,8 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern | |||
| 89 | /// specifically by allowing an std::string for the username. This is required specifically since | 91 | /// specifically by allowing an std::string for the username. This is required specifically since |
| 90 | /// we're loading a string straight from the config | 92 | /// we're loading a string straight from the config |
| 91 | ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { | 93 | ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { |
| 92 | ProfileUsername username_output; | 94 | ProfileUsername username_output{}; |
| 95 | |||
| 93 | if (username.size() > username_output.size()) { | 96 | if (username.size() > username_output.size()) { |
| 94 | std::copy_n(username.begin(), username_output.size(), username_output.begin()); | 97 | std::copy_n(username.begin(), username_output.size(), username_output.begin()); |
| 95 | } else { | 98 | } else { |
| @@ -99,7 +102,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 99 | } | 102 | } |
| 100 | 103 | ||
| 101 | /// Returns a users profile index based on their user id. | 104 | /// Returns a users profile index based on their user id. |
| 102 | boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 105 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 103 | if (!uuid) { | 106 | if (!uuid) { |
| 104 | return boost::none; | 107 | return boost::none; |
| 105 | } | 108 | } |
| @@ -108,16 +111,17 @@ boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | |||
| 108 | if (iter == profiles.end()) { | 111 | if (iter == profiles.end()) { |
| 109 | return boost::none; | 112 | return boost::none; |
| 110 | } | 113 | } |
| 111 | return static_cast<size_t>(std::distance(profiles.begin(), iter)); | 114 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| 112 | } | 115 | } |
| 113 | 116 | ||
| 114 | /// Returns a users profile index based on their profile | 117 | /// Returns a users profile index based on their profile |
| 115 | boost::optional<size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { | 118 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { |
| 116 | return GetUserIndex(user.user_uuid); | 119 | return GetUserIndex(user.user_uuid); |
| 117 | } | 120 | } |
| 118 | 121 | ||
| 119 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 122 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 120 | bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const { | 123 | bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, |
| 124 | ProfileBase& profile) const { | ||
| 121 | if (index == boost::none || index >= MAX_USERS) { | 125 | if (index == boost::none || index >= MAX_USERS) { |
| 122 | return false; | 126 | return false; |
| 123 | } | 127 | } |
| @@ -141,14 +145,16 @@ bool ProfileManager::GetProfileBase(const ProfileInfo& user, ProfileBase& profil | |||
| 141 | 145 | ||
| 142 | /// Returns the current user count on the system. We keep a variable which tracks the count so we | 146 | /// Returns the current user count on the system. We keep a variable which tracks the count so we |
| 143 | /// don't have to loop the internal profile array every call. | 147 | /// don't have to loop the internal profile array every call. |
| 144 | size_t ProfileManager::GetUserCount() const { | 148 | |
| 149 | std::size_t ProfileManager::GetUserCount() const { | ||
| 145 | return user_count; | 150 | return user_count; |
| 146 | } | 151 | } |
| 147 | 152 | ||
| 148 | /// Lists the current "opened" users on the system. Users are typically not open until they sign | 153 | /// Lists the current "opened" users on the system. Users are typically not open until they sign |
| 149 | /// into something or pick a profile. As of right now users should all be open until qlaunch is | 154 | /// into something or pick a profile. As of right now users should all be open until qlaunch is |
| 150 | /// booting | 155 | /// booting |
| 151 | size_t ProfileManager::GetOpenUserCount() const { | 156 | |
| 157 | std::size_t ProfileManager::GetOpenUserCount() const { | ||
| 152 | return std::count_if(profiles.begin(), profiles.end(), | 158 | return std::count_if(profiles.begin(), profiles.end(), |
| 153 | [](const ProfileInfo& p) { return p.is_open; }); | 159 | [](const ProfileInfo& p) { return p.is_open; }); |
| 154 | } | 160 | } |
| @@ -204,7 +210,7 @@ UUID ProfileManager::GetLastOpenedUser() const { | |||
| 204 | } | 210 | } |
| 205 | 211 | ||
| 206 | /// Return the users profile base and the unknown arbitary data. | 212 | /// Return the users profile base and the unknown arbitary data. |
| 207 | bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, | 213 | bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, |
| 208 | ProfileData& data) const { | 214 | ProfileData& data) const { |
| 209 | if (GetProfileBase(index, profile)) { | 215 | if (GetProfileBase(index, profile)) { |
| 210 | data = profiles[index.get()].data; | 216 | data = profiles[index.get()].data; |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 52967844d..bffd4cf4d 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -12,8 +12,8 @@ | |||
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 13 | 13 | ||
| 14 | namespace Service::Account { | 14 | namespace Service::Account { |
| 15 | constexpr size_t MAX_USERS = 8; | 15 | constexpr std::size_t MAX_USERS = 8; |
| 16 | constexpr size_t MAX_DATA = 128; | 16 | constexpr std::size_t MAX_DATA = 128; |
| 17 | constexpr u128 INVALID_UUID{{0, 0}}; | 17 | constexpr u128 INVALID_UUID{{0, 0}}; |
| 18 | 18 | ||
| 19 | struct UUID { | 19 | struct UUID { |
| @@ -82,21 +82,23 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size"); | |||
| 82 | class ProfileManager { | 82 | class ProfileManager { |
| 83 | public: | 83 | public: |
| 84 | ProfileManager(); // TODO(ogniK): Load from system save | 84 | ProfileManager(); // TODO(ogniK): Load from system save |
| 85 | ~ProfileManager(); | ||
| 86 | |||
| 85 | ResultCode AddUser(const ProfileInfo& user); | 87 | ResultCode AddUser(const ProfileInfo& user); |
| 86 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); | 88 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); |
| 87 | ResultCode CreateNewUser(UUID uuid, const std::string& username); | 89 | ResultCode CreateNewUser(UUID uuid, const std::string& username); |
| 88 | boost::optional<size_t> GetUserIndex(const UUID& uuid) const; | 90 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; |
| 89 | boost::optional<size_t> GetUserIndex(const ProfileInfo& user) const; | 91 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; |
| 90 | bool GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const; | 92 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; |
| 91 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; | 93 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; |
| 92 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; | 94 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; |
| 93 | bool GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, | 95 | bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, |
| 94 | ProfileData& data) const; | 96 | ProfileData& data) const; |
| 95 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; | 97 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; |
| 96 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, | 98 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, |
| 97 | ProfileData& data) const; | 99 | ProfileData& data) const; |
| 98 | size_t GetUserCount() const; | 100 | std::size_t GetUserCount() const; |
| 99 | size_t GetOpenUserCount() const; | 101 | std::size_t GetOpenUserCount() const; |
| 100 | bool UserExists(UUID uuid) const; | 102 | bool UserExists(UUID uuid) const; |
| 101 | void OpenUser(UUID uuid); | 103 | void OpenUser(UUID uuid); |
| 102 | void CloseUser(UUID uuid); | 104 | void CloseUser(UUID uuid); |
| @@ -108,9 +110,9 @@ public: | |||
| 108 | 110 | ||
| 109 | private: | 111 | private: |
| 110 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 112 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 111 | size_t user_count = 0; | 113 | std::size_t user_count = 0; |
| 112 | boost::optional<size_t> AddToProfiles(const ProfileInfo& profile); | 114 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); |
| 113 | bool RemoveProfileAtIndex(size_t index); | 115 | bool RemoveProfileAtIndex(std::size_t index); |
| 114 | UUID last_opened_user{INVALID_UUID}; | 116 | UUID last_opened_user{INVALID_UUID}; |
| 115 | }; | 117 | }; |
| 116 | 118 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 818c03e0f..69bfce1c1 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "core/hle/service/nvflinger/nvflinger.h" | 20 | #include "core/hle/service/nvflinger/nvflinger.h" |
| 21 | #include "core/hle/service/pm/pm.h" | 21 | #include "core/hle/service/pm/pm.h" |
| 22 | #include "core/hle/service/set/set.h" | 22 | #include "core/hle/service/set/set.h" |
| 23 | #include "core/hle/service/vi/vi.h" | ||
| 23 | #include "core/settings.h" | 24 | #include "core/settings.h" |
| 24 | 25 | ||
| 25 | namespace Service::AM { | 26 | namespace Service::AM { |
| @@ -35,6 +36,8 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { | |||
| 35 | RegisterHandlers(functions); | 36 | RegisterHandlers(functions); |
| 36 | } | 37 | } |
| 37 | 38 | ||
| 39 | IWindowController::~IWindowController() = default; | ||
| 40 | |||
| 38 | void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { | 41 | void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { |
| 39 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 42 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 40 | IPC::ResponseBuilder rb{ctx, 4}; | 43 | IPC::ResponseBuilder rb{ctx, 4}; |
| @@ -61,6 +64,8 @@ IAudioController::IAudioController() : ServiceFramework("IAudioController") { | |||
| 61 | RegisterHandlers(functions); | 64 | RegisterHandlers(functions); |
| 62 | } | 65 | } |
| 63 | 66 | ||
| 67 | IAudioController::~IAudioController() = default; | ||
| 68 | |||
| 64 | void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { | 69 | void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { |
| 65 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 70 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 66 | IPC::ResponseBuilder rb{ctx, 2}; | 71 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -116,7 +121,10 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 116 | RegisterHandlers(functions); | 121 | RegisterHandlers(functions); |
| 117 | } | 122 | } |
| 118 | 123 | ||
| 124 | IDisplayController::~IDisplayController() = default; | ||
| 125 | |||
| 119 | IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} | 126 | IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} |
| 127 | IDebugFunctions::~IDebugFunctions() = default; | ||
| 120 | 128 | ||
| 121 | ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | 129 | ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) |
| 122 | : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { | 130 | : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { |
| @@ -165,6 +173,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||
| 165 | Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent"); | 173 | Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent"); |
| 166 | } | 174 | } |
| 167 | 175 | ||
| 176 | ISelfController::~ISelfController() = default; | ||
| 177 | |||
| 168 | void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { | 178 | void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { |
| 169 | // Takes 3 input u8s with each field located immediately after the previous u8, these are | 179 | // Takes 3 input u8s with each field located immediately after the previous u8, these are |
| 170 | // bool flags. No output. | 180 | // bool flags. No output. |
| @@ -325,7 +335,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" | |||
| 325 | {51, nullptr, "SetVrModeEnabled"}, | 335 | {51, nullptr, "SetVrModeEnabled"}, |
| 326 | {52, nullptr, "SwitchLcdBacklight"}, | 336 | {52, nullptr, "SwitchLcdBacklight"}, |
| 327 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, | 337 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, |
| 328 | {60, nullptr, "GetDefaultDisplayResolution"}, | 338 | {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, |
| 329 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, | 339 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, |
| 330 | "GetDefaultDisplayResolutionChangeEvent"}, | 340 | "GetDefaultDisplayResolutionChangeEvent"}, |
| 331 | {62, nullptr, "GetHdcpAuthenticationState"}, | 341 | {62, nullptr, "GetHdcpAuthenticationState"}, |
| @@ -337,6 +347,8 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" | |||
| 337 | event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); | 347 | event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); |
| 338 | } | 348 | } |
| 339 | 349 | ||
| 350 | ICommonStateGetter::~ICommonStateGetter() = default; | ||
| 351 | |||
| 340 | void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) { | 352 | void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) { |
| 341 | IPC::ResponseBuilder rb{ctx, 3}; | 353 | IPC::ResponseBuilder rb{ctx, 3}; |
| 342 | rb.Push(RESULT_SUCCESS); | 354 | rb.Push(RESULT_SUCCESS); |
| @@ -382,6 +394,21 @@ void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLEReque | |||
| 382 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 394 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 383 | } | 395 | } |
| 384 | 396 | ||
| 397 | void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { | ||
| 398 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 399 | rb.Push(RESULT_SUCCESS); | ||
| 400 | |||
| 401 | if (Settings::values.use_docked_mode) { | ||
| 402 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); | ||
| 403 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); | ||
| 404 | } else { | ||
| 405 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); | ||
| 406 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); | ||
| 407 | } | ||
| 408 | |||
| 409 | LOG_DEBUG(Service_AM, "called"); | ||
| 410 | } | ||
| 411 | |||
| 385 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { | 412 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { |
| 386 | const bool use_docked_mode{Settings::values.use_docked_mode}; | 413 | const bool use_docked_mode{Settings::values.use_docked_mode}; |
| 387 | IPC::ResponseBuilder rb{ctx, 3}; | 414 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -435,7 +462,7 @@ private: | |||
| 435 | 462 | ||
| 436 | std::memcpy(&buffer[offset], data.data(), data.size()); | 463 | std::memcpy(&buffer[offset], data.data(), data.size()); |
| 437 | 464 | ||
| 438 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; | 465 | IPC::ResponseBuilder rb{ctx, 2}; |
| 439 | rb.Push(RESULT_SUCCESS); | 466 | rb.Push(RESULT_SUCCESS); |
| 440 | 467 | ||
| 441 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | 468 | LOG_DEBUG(Service_AM, "called, offset={}", offset); |
| @@ -445,13 +472,13 @@ private: | |||
| 445 | IPC::RequestParser rp{ctx}; | 472 | IPC::RequestParser rp{ctx}; |
| 446 | 473 | ||
| 447 | const u64 offset{rp.Pop<u64>()}; | 474 | const u64 offset{rp.Pop<u64>()}; |
| 448 | const size_t size{ctx.GetWriteBufferSize()}; | 475 | const std::size_t size{ctx.GetWriteBufferSize()}; |
| 449 | 476 | ||
| 450 | ASSERT(offset + size <= buffer.size()); | 477 | ASSERT(offset + size <= buffer.size()); |
| 451 | 478 | ||
| 452 | ctx.WriteBuffer(buffer.data() + offset, size); | 479 | ctx.WriteBuffer(buffer.data() + offset, size); |
| 453 | 480 | ||
| 454 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; | 481 | IPC::ResponseBuilder rb{ctx, 2}; |
| 455 | rb.Push(RESULT_SUCCESS); | 482 | rb.Push(RESULT_SUCCESS); |
| 456 | 483 | ||
| 457 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | 484 | LOG_DEBUG(Service_AM, "called, offset={}", offset); |
| @@ -541,7 +568,7 @@ private: | |||
| 541 | IPC::RequestParser rp{ctx}; | 568 | IPC::RequestParser rp{ctx}; |
| 542 | storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); | 569 | storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); |
| 543 | 570 | ||
| 544 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; | 571 | IPC::ResponseBuilder rb{ctx, 2}; |
| 545 | rb.Push(RESULT_SUCCESS); | 572 | rb.Push(RESULT_SUCCESS); |
| 546 | 573 | ||
| 547 | LOG_DEBUG(Service_AM, "called"); | 574 | LOG_DEBUG(Service_AM, "called"); |
| @@ -573,6 +600,8 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple | |||
| 573 | RegisterHandlers(functions); | 600 | RegisterHandlers(functions); |
| 574 | } | 601 | } |
| 575 | 602 | ||
| 603 | ILibraryAppletCreator::~ILibraryAppletCreator() = default; | ||
| 604 | |||
| 576 | void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { | 605 | void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { |
| 577 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 606 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 578 | 607 | ||
| @@ -587,7 +616,7 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { | |||
| 587 | const u64 size{rp.Pop<u64>()}; | 616 | const u64 size{rp.Pop<u64>()}; |
| 588 | std::vector<u8> buffer(size); | 617 | std::vector<u8> buffer(size); |
| 589 | 618 | ||
| 590 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)}; | 619 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 591 | rb.Push(RESULT_SUCCESS); | 620 | rb.Push(RESULT_SUCCESS); |
| 592 | rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); | 621 | rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); |
| 593 | 622 | ||
| @@ -638,6 +667,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF | |||
| 638 | RegisterHandlers(functions); | 667 | RegisterHandlers(functions); |
| 639 | } | 668 | } |
| 640 | 669 | ||
| 670 | IApplicationFunctions::~IApplicationFunctions() = default; | ||
| 671 | |||
| 641 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | 672 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { |
| 642 | constexpr std::array<u8, 0x88> data{{ | 673 | constexpr std::array<u8, 0x88> data{{ |
| 643 | 0xca, 0x97, 0x94, 0xc7, // Magic | 674 | 0xca, 0x97, 0x94, 0xc7, // Magic |
| @@ -760,6 +791,8 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions" | |||
| 760 | RegisterHandlers(functions); | 791 | RegisterHandlers(functions); |
| 761 | } | 792 | } |
| 762 | 793 | ||
| 794 | IHomeMenuFunctions::~IHomeMenuFunctions() = default; | ||
| 795 | |||
| 763 | void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { | 796 | void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { |
| 764 | IPC::ResponseBuilder rb{ctx, 2}; | 797 | IPC::ResponseBuilder rb{ctx, 2}; |
| 765 | rb.Push(RESULT_SUCCESS); | 798 | rb.Push(RESULT_SUCCESS); |
| @@ -783,6 +816,8 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat | |||
| 783 | RegisterHandlers(functions); | 816 | RegisterHandlers(functions); |
| 784 | } | 817 | } |
| 785 | 818 | ||
| 819 | IGlobalStateController::~IGlobalStateController() = default; | ||
| 820 | |||
| 786 | IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { | 821 | IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { |
| 787 | static const FunctionInfo functions[] = { | 822 | static const FunctionInfo functions[] = { |
| 788 | {0, nullptr, "CreateApplication"}, | 823 | {0, nullptr, "CreateApplication"}, |
| @@ -793,6 +828,8 @@ IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreat | |||
| 793 | RegisterHandlers(functions); | 828 | RegisterHandlers(functions); |
| 794 | } | 829 | } |
| 795 | 830 | ||
| 831 | IApplicationCreator::~IApplicationCreator() = default; | ||
| 832 | |||
| 796 | IProcessWindingController::IProcessWindingController() | 833 | IProcessWindingController::IProcessWindingController() |
| 797 | : ServiceFramework("IProcessWindingController") { | 834 | : ServiceFramework("IProcessWindingController") { |
| 798 | static const FunctionInfo functions[] = { | 835 | static const FunctionInfo functions[] = { |
| @@ -807,4 +844,6 @@ IProcessWindingController::IProcessWindingController() | |||
| 807 | }; | 844 | }; |
| 808 | RegisterHandlers(functions); | 845 | RegisterHandlers(functions); |
| 809 | } | 846 | } |
| 847 | |||
| 848 | IProcessWindingController::~IProcessWindingController() = default; | ||
| 810 | } // namespace Service::AM | 849 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 9e8bb4e43..b39b0d838 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -42,6 +42,7 @@ enum SystemLanguage { | |||
| 42 | class IWindowController final : public ServiceFramework<IWindowController> { | 42 | class IWindowController final : public ServiceFramework<IWindowController> { |
| 43 | public: | 43 | public: |
| 44 | IWindowController(); | 44 | IWindowController(); |
| 45 | ~IWindowController() override; | ||
| 45 | 46 | ||
| 46 | private: | 47 | private: |
| 47 | void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); | 48 | void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); |
| @@ -51,6 +52,7 @@ private: | |||
| 51 | class IAudioController final : public ServiceFramework<IAudioController> { | 52 | class IAudioController final : public ServiceFramework<IAudioController> { |
| 52 | public: | 53 | public: |
| 53 | IAudioController(); | 54 | IAudioController(); |
| 55 | ~IAudioController() override; | ||
| 54 | 56 | ||
| 55 | private: | 57 | private: |
| 56 | void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); | 58 | void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); |
| @@ -63,16 +65,19 @@ private: | |||
| 63 | class IDisplayController final : public ServiceFramework<IDisplayController> { | 65 | class IDisplayController final : public ServiceFramework<IDisplayController> { |
| 64 | public: | 66 | public: |
| 65 | IDisplayController(); | 67 | IDisplayController(); |
| 68 | ~IDisplayController() override; | ||
| 66 | }; | 69 | }; |
| 67 | 70 | ||
| 68 | class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { | 71 | class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { |
| 69 | public: | 72 | public: |
| 70 | IDebugFunctions(); | 73 | IDebugFunctions(); |
| 74 | ~IDebugFunctions() override; | ||
| 71 | }; | 75 | }; |
| 72 | 76 | ||
| 73 | class ISelfController final : public ServiceFramework<ISelfController> { | 77 | class ISelfController final : public ServiceFramework<ISelfController> { |
| 74 | public: | 78 | public: |
| 75 | explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); | 79 | explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); |
| 80 | ~ISelfController() override; | ||
| 76 | 81 | ||
| 77 | private: | 82 | private: |
| 78 | void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); | 83 | void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); |
| @@ -98,6 +103,7 @@ private: | |||
| 98 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { | 103 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { |
| 99 | public: | 104 | public: |
| 100 | ICommonStateGetter(); | 105 | ICommonStateGetter(); |
| 106 | ~ICommonStateGetter() override; | ||
| 101 | 107 | ||
| 102 | private: | 108 | private: |
| 103 | enum class FocusState : u8 { | 109 | enum class FocusState : u8 { |
| @@ -117,6 +123,7 @@ private: | |||
| 117 | void GetOperationMode(Kernel::HLERequestContext& ctx); | 123 | void GetOperationMode(Kernel::HLERequestContext& ctx); |
| 118 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); | 124 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); |
| 119 | void GetBootMode(Kernel::HLERequestContext& ctx); | 125 | void GetBootMode(Kernel::HLERequestContext& ctx); |
| 126 | void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); | ||
| 120 | 127 | ||
| 121 | Kernel::SharedPtr<Kernel::Event> event; | 128 | Kernel::SharedPtr<Kernel::Event> event; |
| 122 | }; | 129 | }; |
| @@ -124,6 +131,7 @@ private: | |||
| 124 | class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { | 131 | class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { |
| 125 | public: | 132 | public: |
| 126 | ILibraryAppletCreator(); | 133 | ILibraryAppletCreator(); |
| 134 | ~ILibraryAppletCreator() override; | ||
| 127 | 135 | ||
| 128 | private: | 136 | private: |
| 129 | void CreateLibraryApplet(Kernel::HLERequestContext& ctx); | 137 | void CreateLibraryApplet(Kernel::HLERequestContext& ctx); |
| @@ -133,6 +141,7 @@ private: | |||
| 133 | class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { | 141 | class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { |
| 134 | public: | 142 | public: |
| 135 | IApplicationFunctions(); | 143 | IApplicationFunctions(); |
| 144 | ~IApplicationFunctions() override; | ||
| 136 | 145 | ||
| 137 | private: | 146 | private: |
| 138 | void PopLaunchParameter(Kernel::HLERequestContext& ctx); | 147 | void PopLaunchParameter(Kernel::HLERequestContext& ctx); |
| @@ -150,6 +159,7 @@ private: | |||
| 150 | class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { | 159 | class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { |
| 151 | public: | 160 | public: |
| 152 | IHomeMenuFunctions(); | 161 | IHomeMenuFunctions(); |
| 162 | ~IHomeMenuFunctions() override; | ||
| 153 | 163 | ||
| 154 | private: | 164 | private: |
| 155 | void RequestToGetForeground(Kernel::HLERequestContext& ctx); | 165 | void RequestToGetForeground(Kernel::HLERequestContext& ctx); |
| @@ -158,16 +168,19 @@ private: | |||
| 158 | class IGlobalStateController final : public ServiceFramework<IGlobalStateController> { | 168 | class IGlobalStateController final : public ServiceFramework<IGlobalStateController> { |
| 159 | public: | 169 | public: |
| 160 | IGlobalStateController(); | 170 | IGlobalStateController(); |
| 171 | ~IGlobalStateController() override; | ||
| 161 | }; | 172 | }; |
| 162 | 173 | ||
| 163 | class IApplicationCreator final : public ServiceFramework<IApplicationCreator> { | 174 | class IApplicationCreator final : public ServiceFramework<IApplicationCreator> { |
| 164 | public: | 175 | public: |
| 165 | IApplicationCreator(); | 176 | IApplicationCreator(); |
| 177 | ~IApplicationCreator() override; | ||
| 166 | }; | 178 | }; |
| 167 | 179 | ||
| 168 | class IProcessWindingController final : public ServiceFramework<IProcessWindingController> { | 180 | class IProcessWindingController final : public ServiceFramework<IProcessWindingController> { |
| 169 | public: | 181 | public: |
| 170 | IProcessWindingController(); | 182 | IProcessWindingController(); |
| 183 | ~IProcessWindingController() override; | ||
| 171 | }; | 184 | }; |
| 172 | 185 | ||
| 173 | /// Registers all AM services with the specified service manager. | 186 | /// Registers all AM services with the specified service manager. |
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 7cebc918a..4296c255e 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp | |||
| @@ -222,4 +222,6 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | |||
| 222 | RegisterHandlers(functions); | 222 | RegisterHandlers(functions); |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | AppletAE::~AppletAE() = default; | ||
| 226 | |||
| 225 | } // namespace Service::AM | 227 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index bdc57b9bc..1ed77baa4 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h | |||
| @@ -18,7 +18,7 @@ namespace AM { | |||
| 18 | class AppletAE final : public ServiceFramework<AppletAE> { | 18 | class AppletAE final : public ServiceFramework<AppletAE> { |
| 19 | public: | 19 | public: |
| 20 | explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); | 20 | explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); |
| 21 | ~AppletAE() = default; | 21 | ~AppletAE() override; |
| 22 | 22 | ||
| 23 | private: | 23 | private: |
| 24 | void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); | 24 | void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index beea7d19b..e45cf6e20 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp | |||
| @@ -103,4 +103,6 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | |||
| 103 | RegisterHandlers(functions); | 103 | RegisterHandlers(functions); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | AppletOE::~AppletOE() = default; | ||
| 107 | |||
| 106 | } // namespace Service::AM | 108 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index c52e2a322..60cfdfd9d 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h | |||
| @@ -18,7 +18,7 @@ namespace AM { | |||
| 18 | class AppletOE final : public ServiceFramework<AppletOE> { | 18 | class AppletOE final : public ServiceFramework<AppletOE> { |
| 19 | public: | 19 | public: |
| 20 | explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); | 20 | explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); |
| 21 | ~AppletOE() = default; | 21 | ~AppletOE() override; |
| 22 | 22 | ||
| 23 | private: | 23 | private: |
| 24 | void OpenApplicationProxy(Kernel::HLERequestContext& ctx); | 24 | void OpenApplicationProxy(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp index af46e9494..0e3088bc8 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/am/idle.cpp | |||
| @@ -21,4 +21,6 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} { | |||
| 21 | RegisterHandlers(functions); | 21 | RegisterHandlers(functions); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | IdleSys::~IdleSys() = default; | ||
| 25 | |||
| 24 | } // namespace Service::AM | 26 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h index 1eb68d2c9..c44e856b1 100644 --- a/src/core/hle/service/am/idle.h +++ b/src/core/hle/service/am/idle.h | |||
| @@ -11,6 +11,7 @@ namespace Service::AM { | |||
| 11 | class IdleSys final : public ServiceFramework<IdleSys> { | 11 | class IdleSys final : public ServiceFramework<IdleSys> { |
| 12 | public: | 12 | public: |
| 13 | explicit IdleSys(); | 13 | explicit IdleSys(); |
| 14 | ~IdleSys() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::AM | 17 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp index 447fe8669..1c37f849f 100644 --- a/src/core/hle/service/am/omm.cpp +++ b/src/core/hle/service/am/omm.cpp | |||
| @@ -39,4 +39,6 @@ OMM::OMM() : ServiceFramework{"omm"} { | |||
| 39 | RegisterHandlers(functions); | 39 | RegisterHandlers(functions); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | OMM::~OMM() = default; | ||
| 43 | |||
| 42 | } // namespace Service::AM | 44 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h index 49e5d331c..59dc91b72 100644 --- a/src/core/hle/service/am/omm.h +++ b/src/core/hle/service/am/omm.h | |||
| @@ -11,6 +11,7 @@ namespace Service::AM { | |||
| 11 | class OMM final : public ServiceFramework<OMM> { | 11 | class OMM final : public ServiceFramework<OMM> { |
| 12 | public: | 12 | public: |
| 13 | explicit OMM(); | 13 | explicit OMM(); |
| 14 | ~OMM() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::AM | 17 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp index a05d433d0..003ee8667 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/am/spsm.cpp | |||
| @@ -27,4 +27,6 @@ SPSM::SPSM() : ServiceFramework{"spsm"} { | |||
| 27 | RegisterHandlers(functions); | 27 | RegisterHandlers(functions); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | SPSM::~SPSM() = default; | ||
| 31 | |||
| 30 | } // namespace Service::AM | 32 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h index 57dde62e1..3a0b979fa 100644 --- a/src/core/hle/service/am/spsm.h +++ b/src/core/hle/service/am/spsm.h | |||
| @@ -11,6 +11,7 @@ namespace Service::AM { | |||
| 11 | class SPSM final : public ServiceFramework<SPSM> { | 11 | class SPSM final : public ServiceFramework<SPSM> { |
| 12 | public: | 12 | public: |
| 13 | explicit SPSM(); | 13 | explicit SPSM(); |
| 14 | ~SPSM() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::AM | 17 | } // namespace Service::AM |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 6e7438580..d9eeac9ec 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -23,6 +23,8 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u") { | |||
| 23 | RegisterHandlers(functions); | 23 | RegisterHandlers(functions); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | AOC_U::~AOC_U() = default; | ||
| 27 | |||
| 26 | void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | 28 | void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { |
| 27 | IPC::ResponseBuilder rb{ctx, 4}; | 29 | IPC::ResponseBuilder rb{ctx, 4}; |
| 28 | rb.Push(RESULT_SUCCESS); | 30 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 17d48ef30..29ce8f488 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h | |||
| @@ -11,7 +11,7 @@ namespace Service::AOC { | |||
| 11 | class AOC_U final : public ServiceFramework<AOC_U> { | 11 | class AOC_U final : public ServiceFramework<AOC_U> { |
| 12 | public: | 12 | public: |
| 13 | AOC_U(); | 13 | AOC_U(); |
| 14 | ~AOC_U() = default; | 14 | ~AOC_U() override; |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
| 17 | void CountAddOnContent(Kernel::HLERequestContext& ctx); | 17 | void CountAddOnContent(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 4109cb7f7..f3c09bbb1 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | namespace Service::APM { | 10 | namespace Service::APM { |
| 11 | 11 | ||
| 12 | Module::Module() = default; | ||
| 13 | Module::~Module() = default; | ||
| 14 | |||
| 12 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 15 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 13 | auto module_ = std::make_shared<Module>(); | 16 | auto module_ = std::make_shared<Module>(); |
| 14 | std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); | 17 | std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 90a80d51b..4d7d5bb7c 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h | |||
| @@ -15,8 +15,8 @@ enum class PerformanceMode : u8 { | |||
| 15 | 15 | ||
| 16 | class Module final { | 16 | class Module final { |
| 17 | public: | 17 | public: |
| 18 | Module() = default; | 18 | Module(); |
| 19 | ~Module() = default; | 19 | ~Module(); |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | /// Registers all AM services with the specified service manager. | 22 | /// Registers all AM services with the specified service manager. |
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index 4cd8132f5..c22bd3859 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp | |||
| @@ -70,6 +70,8 @@ APM::APM(std::shared_ptr<Module> apm, const char* name) | |||
| 70 | RegisterHandlers(functions); | 70 | RegisterHandlers(functions); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | APM::~APM() = default; | ||
| 74 | |||
| 73 | void APM::OpenSession(Kernel::HLERequestContext& ctx) { | 75 | void APM::OpenSession(Kernel::HLERequestContext& ctx) { |
| 74 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 76 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 75 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| @@ -93,6 +95,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { | |||
| 93 | RegisterHandlers(functions); | 95 | RegisterHandlers(functions); |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 98 | APM_Sys::~APM_Sys() = default; | ||
| 99 | |||
| 96 | void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { | 100 | void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { |
| 97 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 101 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 98 | rb.Push(RESULT_SUCCESS); | 102 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index d14264ad7..773541aa4 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h | |||
| @@ -11,7 +11,7 @@ namespace Service::APM { | |||
| 11 | class APM final : public ServiceFramework<APM> { | 11 | class APM final : public ServiceFramework<APM> { |
| 12 | public: | 12 | public: |
| 13 | explicit APM(std::shared_ptr<Module> apm, const char* name); | 13 | explicit APM(std::shared_ptr<Module> apm, const char* name); |
| 14 | ~APM() = default; | 14 | ~APM() override; |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
| 17 | void OpenSession(Kernel::HLERequestContext& ctx); | 17 | void OpenSession(Kernel::HLERequestContext& ctx); |
| @@ -22,6 +22,7 @@ private: | |||
| 22 | class APM_Sys final : public ServiceFramework<APM_Sys> { | 22 | class APM_Sys final : public ServiceFramework<APM_Sys> { |
| 23 | public: | 23 | public: |
| 24 | explicit APM_Sys(); | 24 | explicit APM_Sys(); |
| 25 | ~APM_Sys() override; | ||
| 25 | 26 | ||
| 26 | private: | 27 | private: |
| 27 | void GetPerformanceEvent(Kernel::HLERequestContext& ctx); | 28 | void GetPerformanceEvent(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 37c3fdcac..b6b71f966 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp | |||
| @@ -42,4 +42,6 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} { | |||
| 42 | RegisterHandlers(functions); | 42 | RegisterHandlers(functions); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | AudCtl::~AudCtl() = default; | ||
| 46 | |||
| 45 | } // namespace Service::Audio | 47 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h index ed837bdf2..9d2d9e83b 100644 --- a/src/core/hle/service/audio/audctl.h +++ b/src/core/hle/service/audio/audctl.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Audio { | |||
| 11 | class AudCtl final : public ServiceFramework<AudCtl> { | 11 | class AudCtl final : public ServiceFramework<AudCtl> { |
| 12 | public: | 12 | public: |
| 13 | explicit AudCtl(); | 13 | explicit AudCtl(); |
| 14 | ~AudCtl() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Audio | 17 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp index b08c21a20..8fff3e4b4 100644 --- a/src/core/hle/service/audio/auddbg.cpp +++ b/src/core/hle/service/audio/auddbg.cpp | |||
| @@ -17,4 +17,6 @@ AudDbg::AudDbg(const char* name) : ServiceFramework{name} { | |||
| 17 | RegisterHandlers(functions); | 17 | RegisterHandlers(functions); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | AudDbg::~AudDbg() = default; | ||
| 21 | |||
| 20 | } // namespace Service::Audio | 22 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h index a2f540b75..6689f4759 100644 --- a/src/core/hle/service/audio/auddbg.h +++ b/src/core/hle/service/audio/auddbg.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Audio { | |||
| 11 | class AudDbg final : public ServiceFramework<AudDbg> { | 11 | class AudDbg final : public ServiceFramework<AudDbg> { |
| 12 | public: | 12 | public: |
| 13 | explicit AudDbg(const char* name); | 13 | explicit AudDbg(const char* name); |
| 14 | ~AudDbg() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Audio | 17 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp index a70d5bca4..ddd12f35e 100644 --- a/src/core/hle/service/audio/audin_a.cpp +++ b/src/core/hle/service/audio/audin_a.cpp | |||
| @@ -19,4 +19,6 @@ AudInA::AudInA() : ServiceFramework{"audin:a"} { | |||
| 19 | RegisterHandlers(functions); | 19 | RegisterHandlers(functions); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | AudInA::~AudInA() = default; | ||
| 23 | |||
| 22 | } // namespace Service::Audio | 24 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h index e4c75510f..e7623bc29 100644 --- a/src/core/hle/service/audio/audin_a.h +++ b/src/core/hle/service/audio/audin_a.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Audio { | |||
| 11 | class AudInA final : public ServiceFramework<AudInA> { | 11 | class AudInA final : public ServiceFramework<AudInA> { |
| 12 | public: | 12 | public: |
| 13 | explicit AudInA(); | 13 | explicit AudInA(); |
| 14 | ~AudInA() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Audio | 17 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index cbc49e55e..657010312 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -41,4 +41,6 @@ AudInU::AudInU() : ServiceFramework("audin:u") { | |||
| 41 | RegisterHandlers(functions); | 41 | RegisterHandlers(functions); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | AudInU::~AudInU() = default; | ||
| 45 | |||
| 44 | } // namespace Service::Audio | 46 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index 2e65efb5b..0538b9560 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h | |||
| @@ -15,7 +15,7 @@ namespace Service::Audio { | |||
| 15 | class AudInU final : public ServiceFramework<AudInU> { | 15 | class AudInU final : public ServiceFramework<AudInU> { |
| 16 | public: | 16 | public: |
| 17 | explicit AudInU(); | 17 | explicit AudInU(); |
| 18 | ~AudInU() = default; | 18 | ~AudInU() override; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | } // namespace Service::Audio | 21 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 6b5e15633..128df7db5 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/service/audio/audren_u.h" | 15 | #include "core/hle/service/audio/audren_u.h" |
| 16 | #include "core/hle/service/audio/codecctl.h" | 16 | #include "core/hle/service/audio/codecctl.h" |
| 17 | #include "core/hle/service/audio/hwopus.h" | 17 | #include "core/hle/service/audio/hwopus.h" |
| 18 | #include "core/hle/service/service.h" | ||
| 18 | 19 | ||
| 19 | namespace Service::Audio { | 20 | namespace Service::Audio { |
| 20 | 21 | ||
diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index 95e5691f7..f5bd3bf5f 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h | |||
| @@ -4,7 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | 7 | namespace Service::SM { |
| 8 | class ServiceManager; | ||
| 9 | } | ||
| 8 | 10 | ||
| 9 | namespace Service::Audio { | 11 | namespace Service::Audio { |
| 10 | 12 | ||
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp index bf8d40157..85febbca3 100644 --- a/src/core/hle/service/audio/audout_a.cpp +++ b/src/core/hle/service/audio/audout_a.cpp | |||
| @@ -21,4 +21,6 @@ AudOutA::AudOutA() : ServiceFramework{"audout:a"} { | |||
| 21 | RegisterHandlers(functions); | 21 | RegisterHandlers(functions); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | AudOutA::~AudOutA() = default; | ||
| 25 | |||
| 24 | } // namespace Service::Audio | 26 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h index 91a069152..d65b66e8e 100644 --- a/src/core/hle/service/audio/audout_a.h +++ b/src/core/hle/service/audio/audout_a.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Audio { | |||
| 11 | class AudOutA final : public ServiceFramework<AudOutA> { | 11 | class AudOutA final : public ServiceFramework<AudOutA> { |
| 12 | public: | 12 | public: |
| 13 | explicit AudOutA(); | 13 | explicit AudOutA(); |
| 14 | ~AudOutA() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Audio | 17 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 5f370bbdf..ff1edefbb 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -3,15 +3,20 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cstring> | ||
| 6 | #include <vector> | 7 | #include <vector> |
| 7 | 8 | ||
| 9 | #include "audio_core/audio_out.h" | ||
| 8 | #include "audio_core/codec.h" | 10 | #include "audio_core/codec.h" |
| 11 | #include "common/common_funcs.h" | ||
| 9 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/swap.h" | ||
| 10 | #include "core/core.h" | 14 | #include "core/core.h" |
| 11 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 12 | #include "core/hle/kernel/event.h" | 16 | #include "core/hle/kernel/event.h" |
| 13 | #include "core/hle/kernel/hle_ipc.h" | 17 | #include "core/hle/kernel/hle_ipc.h" |
| 14 | #include "core/hle/service/audio/audout_u.h" | 18 | #include "core/hle/service/audio/audout_u.h" |
| 19 | #include "core/memory.h" | ||
| 15 | 20 | ||
| 16 | namespace Service::Audio { | 21 | namespace Service::Audio { |
| 17 | 22 | ||
| @@ -25,6 +30,18 @@ enum { | |||
| 25 | constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; | 30 | constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; |
| 26 | constexpr int DefaultSampleRate{48000}; | 31 | constexpr int DefaultSampleRate{48000}; |
| 27 | 32 | ||
| 33 | struct AudoutParams { | ||
| 34 | s32_le sample_rate; | ||
| 35 | u16_le channel_count; | ||
| 36 | INSERT_PADDING_BYTES(2); | ||
| 37 | }; | ||
| 38 | static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); | ||
| 39 | |||
| 40 | enum class AudioState : u32 { | ||
| 41 | Started, | ||
| 42 | Stopped, | ||
| 43 | }; | ||
| 44 | |||
| 28 | class IAudioOut final : public ServiceFramework<IAudioOut> { | 45 | class IAudioOut final : public ServiceFramework<IAudioOut> { |
| 29 | public: | 46 | public: |
| 30 | IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) | 47 | IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) |
| @@ -173,7 +190,7 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { | |||
| 173 | 190 | ||
| 174 | ctx.WriteBuffer(DefaultDevice); | 191 | ctx.WriteBuffer(DefaultDevice); |
| 175 | 192 | ||
| 176 | IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); | 193 | IPC::ResponseBuilder rb{ctx, 3}; |
| 177 | 194 | ||
| 178 | rb.Push(RESULT_SUCCESS); | 195 | rb.Push(RESULT_SUCCESS); |
| 179 | rb.Push<u32>(1); // Amount of audio devices | 196 | rb.Push<u32>(1); // Amount of audio devices |
| @@ -218,4 +235,6 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") { | |||
| 218 | audio_core = std::make_unique<AudioCore::AudioOut>(); | 235 | audio_core = std::make_unique<AudioCore::AudioOut>(); |
| 219 | } | 236 | } |
| 220 | 237 | ||
| 238 | AudOutU::~AudOutU() = default; | ||
| 239 | |||
| 221 | } // namespace Service::Audio | 240 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index fd491f65d..dcaf64708 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h | |||
| @@ -4,33 +4,24 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "audio_core/audio_out.h" | ||
| 8 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 9 | 8 | ||
| 9 | namespace AudioCore { | ||
| 10 | class AudioOut; | ||
| 11 | } | ||
| 12 | |||
| 10 | namespace Kernel { | 13 | namespace Kernel { |
| 11 | class HLERequestContext; | 14 | class HLERequestContext; |
| 12 | } | 15 | } |
| 13 | 16 | ||
| 14 | namespace Service::Audio { | 17 | namespace Service::Audio { |
| 15 | 18 | ||
| 16 | struct AudoutParams { | ||
| 17 | s32_le sample_rate; | ||
| 18 | u16_le channel_count; | ||
| 19 | INSERT_PADDING_BYTES(2); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); | ||
| 22 | |||
| 23 | enum class AudioState : u32 { | ||
| 24 | Started, | ||
| 25 | Stopped, | ||
| 26 | }; | ||
| 27 | |||
| 28 | class IAudioOut; | 19 | class IAudioOut; |
| 29 | 20 | ||
| 30 | class AudOutU final : public ServiceFramework<AudOutU> { | 21 | class AudOutU final : public ServiceFramework<AudOutU> { |
| 31 | public: | 22 | public: |
| 32 | AudOutU(); | 23 | AudOutU(); |
| 33 | ~AudOutU() = default; | 24 | ~AudOutU() override; |
| 34 | 25 | ||
| 35 | private: | 26 | private: |
| 36 | std::shared_ptr<IAudioOut> audio_out_interface; | 27 | std::shared_ptr<IAudioOut> audio_out_interface; |
diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp index 016eabf53..ce1bfb48d 100644 --- a/src/core/hle/service/audio/audrec_a.cpp +++ b/src/core/hle/service/audio/audrec_a.cpp | |||
| @@ -17,4 +17,6 @@ AudRecA::AudRecA() : ServiceFramework{"audrec:a"} { | |||
| 17 | RegisterHandlers(functions); | 17 | RegisterHandlers(functions); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | AudRecA::~AudRecA() = default; | ||
| 21 | |||
| 20 | } // namespace Service::Audio | 22 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h index 9685047f2..384d24c69 100644 --- a/src/core/hle/service/audio/audrec_a.h +++ b/src/core/hle/service/audio/audrec_a.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Audio { | |||
| 11 | class AudRecA final : public ServiceFramework<AudRecA> { | 11 | class AudRecA final : public ServiceFramework<AudRecA> { |
| 12 | public: | 12 | public: |
| 13 | explicit AudRecA(); | 13 | explicit AudRecA(); |
| 14 | ~AudRecA() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Audio | 17 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index 74909415c..34974afa9 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp | |||
| @@ -36,4 +36,6 @@ AudRecU::AudRecU() : ServiceFramework("audrec:u") { | |||
| 36 | RegisterHandlers(functions); | 36 | RegisterHandlers(functions); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | AudRecU::~AudRecU() = default; | ||
| 40 | |||
| 39 | } // namespace Service::Audio | 41 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h index 46daa33a4..ca3d638e8 100644 --- a/src/core/hle/service/audio/audrec_u.h +++ b/src/core/hle/service/audio/audrec_u.h | |||
| @@ -15,7 +15,7 @@ namespace Service::Audio { | |||
| 15 | class AudRecU final : public ServiceFramework<AudRecU> { | 15 | class AudRecU final : public ServiceFramework<AudRecU> { |
| 16 | public: | 16 | public: |
| 17 | explicit AudRecU(); | 17 | explicit AudRecU(); |
| 18 | ~AudRecU() = default; | 18 | ~AudRecU() override; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | } // namespace Service::Audio | 21 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp index 616ff3dc4..edb66d985 100644 --- a/src/core/hle/service/audio/audren_a.cpp +++ b/src/core/hle/service/audio/audren_a.cpp | |||
| @@ -23,4 +23,6 @@ AudRenA::AudRenA() : ServiceFramework{"audren:a"} { | |||
| 23 | RegisterHandlers(functions); | 23 | RegisterHandlers(functions); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | AudRenA::~AudRenA() = default; | ||
| 27 | |||
| 26 | } // namespace Service::Audio | 28 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h index 5ecf2e184..81fef0ffe 100644 --- a/src/core/hle/service/audio/audren_a.h +++ b/src/core/hle/service/audio/audren_a.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Audio { | |||
| 11 | class AudRenA final : public ServiceFramework<AudRenA> { | 11 | class AudRenA final : public ServiceFramework<AudRenA> { |
| 12 | public: | 12 | public: |
| 13 | explicit AudRenA(); | 13 | explicit AudRenA(); |
| 14 | ~AudRenA() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Audio | 17 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 016db7c82..06ac6372d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -2,12 +2,14 @@ | |||
| 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> | ||
| 5 | #include <array> | 6 | #include <array> |
| 7 | #include <memory> | ||
| 6 | 8 | ||
| 9 | #include "audio_core/audio_renderer.h" | ||
| 7 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/common_funcs.h" | ||
| 8 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/core_timing_util.h" | ||
| 11 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| 12 | #include "core/hle/kernel/event.h" | 14 | #include "core/hle/kernel/event.h" |
| 13 | #include "core/hle/kernel/hle_ipc.h" | 15 | #include "core/hle/kernel/hle_ipc.h" |
| @@ -135,7 +137,7 @@ private: | |||
| 135 | constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; | 137 | constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; |
| 136 | ctx.WriteBuffer(audio_interface); | 138 | ctx.WriteBuffer(audio_interface); |
| 137 | 139 | ||
| 138 | IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); | 140 | IPC::ResponseBuilder rb{ctx, 3}; |
| 139 | rb.Push(RESULT_SUCCESS); | 141 | rb.Push(RESULT_SUCCESS); |
| 140 | rb.Push<u32>(1); | 142 | rb.Push<u32>(1); |
| 141 | } | 143 | } |
| @@ -149,7 +151,7 @@ private: | |||
| 149 | auto file_buffer = ctx.ReadBuffer(); | 151 | auto file_buffer = ctx.ReadBuffer(); |
| 150 | auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); | 152 | auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); |
| 151 | 153 | ||
| 152 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 154 | IPC::ResponseBuilder rb{ctx, 2}; |
| 153 | rb.Push(RESULT_SUCCESS); | 155 | rb.Push(RESULT_SUCCESS); |
| 154 | } | 156 | } |
| 155 | 157 | ||
| @@ -160,7 +162,7 @@ private: | |||
| 160 | constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; | 162 | constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; |
| 161 | ctx.WriteBuffer(audio_interface); | 163 | ctx.WriteBuffer(audio_interface); |
| 162 | 164 | ||
| 163 | IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); | 165 | IPC::ResponseBuilder rb{ctx, 3}; |
| 164 | rb.Push(RESULT_SUCCESS); | 166 | rb.Push(RESULT_SUCCESS); |
| 165 | rb.Push<u32>(1); | 167 | rb.Push<u32>(1); |
| 166 | } | 168 | } |
| @@ -198,6 +200,8 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { | |||
| 198 | RegisterHandlers(functions); | 200 | RegisterHandlers(functions); |
| 199 | } | 201 | } |
| 200 | 202 | ||
| 203 | AudRenU::~AudRenU() = default; | ||
| 204 | |||
| 201 | void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { | 205 | void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { |
| 202 | IPC::RequestParser rp{ctx}; | 206 | IPC::RequestParser rp{ctx}; |
| 203 | auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); | 207 | auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); |
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 8600ac6e4..c6bc3a90a 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "audio_core/audio_renderer.h" | ||
| 8 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 9 | 8 | ||
| 10 | namespace Kernel { | 9 | namespace Kernel { |
| @@ -16,7 +15,7 @@ namespace Service::Audio { | |||
| 16 | class AudRenU final : public ServiceFramework<AudRenU> { | 15 | class AudRenU final : public ServiceFramework<AudRenU> { |
| 17 | public: | 16 | public: |
| 18 | explicit AudRenU(); | 17 | explicit AudRenU(); |
| 19 | ~AudRenU() = default; | 18 | ~AudRenU() override; |
| 20 | 19 | ||
| 21 | private: | 20 | private: |
| 22 | void OpenAudioRenderer(Kernel::HLERequestContext& ctx); | 21 | void OpenAudioRenderer(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp index 212c8d448..c6864146d 100644 --- a/src/core/hle/service/audio/codecctl.cpp +++ b/src/core/hle/service/audio/codecctl.cpp | |||
| @@ -28,4 +28,6 @@ CodecCtl::CodecCtl() : ServiceFramework("codecctl") { | |||
| 28 | RegisterHandlers(functions); | 28 | RegisterHandlers(functions); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | CodecCtl::~CodecCtl() = default; | ||
| 32 | |||
| 31 | } // namespace Service::Audio | 33 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h index d9ac29b67..2fe75b6e2 100644 --- a/src/core/hle/service/audio/codecctl.h +++ b/src/core/hle/service/audio/codecctl.h | |||
| @@ -15,7 +15,7 @@ namespace Service::Audio { | |||
| 15 | class CodecCtl final : public ServiceFramework<CodecCtl> { | 15 | class CodecCtl final : public ServiceFramework<CodecCtl> { |
| 16 | public: | 16 | public: |
| 17 | explicit CodecCtl(); | 17 | explicit CodecCtl(); |
| 18 | ~CodecCtl() = default; | 18 | ~CodecCtl() override; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | } // namespace Service::Audio | 21 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 371cd4997..fc6067e59 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -3,7 +3,12 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <memory> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 6 | #include <opus.h> | 9 | #include <opus.h> |
| 10 | |||
| 11 | #include "common/common_funcs.h" | ||
| 7 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 8 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| 9 | #include "core/hle/kernel/hle_ipc.h" | 14 | #include "core/hle/kernel/hle_ipc.h" |
| @@ -56,7 +61,7 @@ private: | |||
| 56 | 61 | ||
| 57 | bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, | 62 | bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, |
| 58 | std::vector<opus_int16>& output) { | 63 | std::vector<opus_int16>& output) { |
| 59 | size_t raw_output_sz = output.size() * sizeof(opus_int16); | 64 | std::size_t raw_output_sz = output.size() * sizeof(opus_int16); |
| 60 | if (sizeof(OpusHeader) > input.size()) | 65 | if (sizeof(OpusHeader) > input.size()) |
| 61 | return false; | 66 | return false; |
| 62 | OpusHeader hdr{}; | 67 | OpusHeader hdr{}; |
| @@ -91,7 +96,7 @@ private: | |||
| 91 | u32 channel_count; | 96 | u32 channel_count; |
| 92 | }; | 97 | }; |
| 93 | 98 | ||
| 94 | static size_t WorkerBufferSize(u32 channel_count) { | 99 | static std::size_t WorkerBufferSize(u32 channel_count) { |
| 95 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 100 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); |
| 96 | return opus_decoder_get_size(static_cast<int>(channel_count)); | 101 | return opus_decoder_get_size(static_cast<int>(channel_count)); |
| 97 | } | 102 | } |
| @@ -124,7 +129,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { | |||
| 124 | "Invalid sample rate"); | 129 | "Invalid sample rate"); |
| 125 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 130 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); |
| 126 | 131 | ||
| 127 | size_t worker_sz = WorkerBufferSize(channel_count); | 132 | std::size_t worker_sz = WorkerBufferSize(channel_count); |
| 128 | ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); | 133 | ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); |
| 129 | std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ | 134 | std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ |
| 130 | static_cast<OpusDecoder*>(operator new(worker_sz))}; | 135 | static_cast<OpusDecoder*>(operator new(worker_sz))}; |
| @@ -151,4 +156,6 @@ HwOpus::HwOpus() : ServiceFramework("hwopus") { | |||
| 151 | RegisterHandlers(functions); | 156 | RegisterHandlers(functions); |
| 152 | } | 157 | } |
| 153 | 158 | ||
| 159 | HwOpus::~HwOpus() = default; | ||
| 160 | |||
| 154 | } // namespace Service::Audio | 161 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 5258d59f3..602ede8ba 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h | |||
| @@ -11,7 +11,7 @@ namespace Service::Audio { | |||
| 11 | class HwOpus final : public ServiceFramework<HwOpus> { | 11 | class HwOpus final : public ServiceFramework<HwOpus> { |
| 12 | public: | 12 | public: |
| 13 | explicit HwOpus(); | 13 | explicit HwOpus(); |
| 14 | ~HwOpus() = default; | 14 | ~HwOpus() override; |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
| 17 | void OpenOpusDecoder(Kernel::HLERequestContext& ctx); | 17 | void OpenOpusDecoder(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 20ce692dc..179aa4949 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp | |||
| @@ -13,4 +13,6 @@ BCAT::BCAT(std::shared_ptr<Module> module, const char* name) | |||
| 13 | }; | 13 | }; |
| 14 | RegisterHandlers(functions); | 14 | RegisterHandlers(functions); |
| 15 | } | 15 | } |
| 16 | |||
| 17 | BCAT::~BCAT() = default; | ||
| 16 | } // namespace Service::BCAT | 18 | } // namespace Service::BCAT |
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index 6632996a0..802bd689a 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h | |||
| @@ -11,6 +11,7 @@ namespace Service::BCAT { | |||
| 11 | class BCAT final : public Module::Interface { | 11 | class BCAT final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit BCAT(std::shared_ptr<Module> module, const char* name); | 13 | explicit BCAT(std::shared_ptr<Module> module, const char* name); |
| 14 | ~BCAT() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::BCAT | 17 | } // namespace Service::BCAT |
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 35e024c3d..6e7b795fb 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -42,6 +42,8 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { | |||
| 42 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 42 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 43 | : ServiceFramework(name), module(std::move(module)) {} | 43 | : ServiceFramework(name), module(std::move(module)) {} |
| 44 | 44 | ||
| 45 | Module::Interface::~Interface() = default; | ||
| 46 | |||
| 45 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 47 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 46 | auto module = std::make_shared<Module>(); | 48 | auto module = std::make_shared<Module>(); |
| 47 | std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager); | 49 | std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h index 62f6f5f9d..f0d63cab0 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/module.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Interface : public ServiceFramework<Interface> { | 13 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 14 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | void CreateBcatService(Kernel::HLERequestContext& ctx); | 18 | void CreateBcatService(Kernel::HLERequestContext& ctx); |
| 18 | 19 | ||
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 299b9474f..b436ce4e6 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -13,6 +13,8 @@ namespace Service::Fatal { | |||
| 13 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 13 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 14 | : ServiceFramework(name), module(std::move(module)) {} | 14 | : ServiceFramework(name), module(std::move(module)) {} |
| 15 | 15 | ||
| 16 | Module::Interface::~Interface() = default; | ||
| 17 | |||
| 16 | void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { | 18 | void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { |
| 17 | IPC::RequestParser rp(ctx); | 19 | IPC::RequestParser rp(ctx); |
| 18 | u32 error_code = rp.Pop<u32>(); | 20 | u32 error_code = rp.Pop<u32>(); |
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index ca607e236..4d9a5be52 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Interface : public ServiceFramework<Interface> { | 13 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 14 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); | 18 | void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); |
| 18 | void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); | 19 | void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index a5254ac2f..9e5f872ff 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp | |||
| @@ -9,4 +9,6 @@ namespace Service::Fatal { | |||
| 9 | Fatal_P::Fatal_P(std::shared_ptr<Module> module) | 9 | Fatal_P::Fatal_P(std::shared_ptr<Module> module) |
| 10 | : Module::Interface(std::move(module), "fatal:p") {} | 10 | : Module::Interface(std::move(module), "fatal:p") {} |
| 11 | 11 | ||
| 12 | Fatal_P::~Fatal_P() = default; | ||
| 13 | |||
| 12 | } // namespace Service::Fatal | 14 | } // namespace Service::Fatal |
diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index bfd8c8b74..6e9c5979f 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Fatal { | |||
| 11 | class Fatal_P final : public Module::Interface { | 11 | class Fatal_P final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Fatal_P(std::shared_ptr<Module> module); | 13 | explicit Fatal_P(std::shared_ptr<Module> module); |
| 14 | ~Fatal_P() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Fatal | 17 | } // namespace Service::Fatal |
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index f0631329e..befc307cf 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp | |||
| @@ -15,4 +15,6 @@ Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(m | |||
| 15 | RegisterHandlers(functions); | 15 | RegisterHandlers(functions); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | Fatal_U::~Fatal_U() = default; | ||
| 19 | |||
| 18 | } // namespace Service::Fatal | 20 | } // namespace Service::Fatal |
diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 9b1a9e97a..72cb6d076 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Fatal { | |||
| 11 | class Fatal_U final : public Module::Interface { | 11 | class Fatal_U final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Fatal_U(std::shared_ptr<Module> module); | 13 | explicit Fatal_U(std::shared_ptr<Module> module); |
| 14 | ~Fatal_U() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Fatal | 17 | } // namespace Service::Fatal |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 881c39e31..d349ee686 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "core/file_sys/bis_factory.h" | 10 | #include "core/file_sys/bis_factory.h" |
| 11 | #include "core/file_sys/errors.h" | 11 | #include "core/file_sys/errors.h" |
| 12 | #include "core/file_sys/mode.h" | 12 | #include "core/file_sys/mode.h" |
| 13 | #include "core/file_sys/registered_cache.h" | ||
| 13 | #include "core/file_sys/romfs_factory.h" | 14 | #include "core/file_sys/romfs_factory.h" |
| 14 | #include "core/file_sys/savedata_factory.h" | 15 | #include "core/file_sys/savedata_factory.h" |
| 15 | #include "core/file_sys/sdmc_factory.h" | 16 | #include "core/file_sys/sdmc_factory.h" |
| @@ -39,6 +40,8 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, | |||
| 39 | VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_) | 40 | VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_) |
| 40 | : backing(std::move(backing_)) {} | 41 | : backing(std::move(backing_)) {} |
| 41 | 42 | ||
| 43 | VfsDirectoryServiceWrapper::~VfsDirectoryServiceWrapper() = default; | ||
| 44 | |||
| 42 | std::string VfsDirectoryServiceWrapper::GetName() const { | 45 | std::string VfsDirectoryServiceWrapper::GetName() const { |
| 43 | return backing->GetName(); | 46 | return backing->GetName(); |
| 44 | } | 47 | } |
| @@ -60,17 +63,20 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 | |||
| 60 | 63 | ||
| 61 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { | 64 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { |
| 62 | std::string path(FileUtil::SanitizePath(path_)); | 65 | std::string path(FileUtil::SanitizePath(path_)); |
| 63 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 64 | if (path.empty()) { | 66 | if (path.empty()) { |
| 65 | // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... | 67 | // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... |
| 66 | return RESULT_SUCCESS; | 68 | return RESULT_SUCCESS; |
| 67 | } | 69 | } |
| 68 | if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) | 70 | |
| 71 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 72 | if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) { | ||
| 69 | return FileSys::ERROR_PATH_NOT_FOUND; | 73 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 74 | } | ||
| 70 | if (!dir->DeleteFile(FileUtil::GetFilename(path))) { | 75 | if (!dir->DeleteFile(FileUtil::GetFilename(path))) { |
| 71 | // TODO(DarkLordZach): Find a better error code for this | 76 | // TODO(DarkLordZach): Find a better error code for this |
| 72 | return ResultCode(-1); | 77 | return ResultCode(-1); |
| 73 | } | 78 | } |
| 79 | |||
| 74 | return RESULT_SUCCESS; | 80 | return RESULT_SUCCESS; |
| 75 | } | 81 | } |
| 76 | 82 | ||
| @@ -191,7 +197,7 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s | |||
| 191 | auto dir = GetDirectoryRelativeWrapped(backing, path); | 197 | auto dir = GetDirectoryRelativeWrapped(backing, path); |
| 192 | if (dir == nullptr) { | 198 | if (dir == nullptr) { |
| 193 | // TODO(DarkLordZach): Find a better error code for this | 199 | // TODO(DarkLordZach): Find a better error code for this |
| 194 | return ResultCode(-1); | 200 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 195 | } | 201 | } |
| 196 | return MakeResult(dir); | 202 | return MakeResult(dir); |
| 197 | } | 203 | } |
| @@ -304,6 +310,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { | |||
| 304 | return sdmc_factory->Open(); | 310 | return sdmc_factory->Open(); |
| 305 | } | 311 | } |
| 306 | 312 | ||
| 313 | std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { | ||
| 314 | return std::make_shared<FileSys::RegisteredCacheUnion>( | ||
| 315 | std::vector<std::shared_ptr<FileSys::RegisteredCache>>{ | ||
| 316 | GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); | ||
| 317 | } | ||
| 318 | |||
| 307 | std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { | 319 | std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { |
| 308 | LOG_TRACE(Service_FS, "Opening System NAND Contents"); | 320 | LOG_TRACE(Service_FS, "Opening System NAND Contents"); |
| 309 | 321 | ||
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 9ba0e2eab..aab65a2b8 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | namespace FileSys { | 13 | namespace FileSys { |
| 14 | class BISFactory; | 14 | class BISFactory; |
| 15 | class RegisteredCache; | 15 | class RegisteredCache; |
| 16 | class RegisteredCacheUnion; | ||
| 16 | class RomFSFactory; | 17 | class RomFSFactory; |
| 17 | class SaveDataFactory; | 18 | class SaveDataFactory; |
| 18 | class SDMCFactory; | 19 | class SDMCFactory; |
| @@ -45,6 +46,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | |||
| 45 | FileSys::SaveDataDescriptor save_struct); | 46 | FileSys::SaveDataDescriptor save_struct); |
| 46 | ResultVal<FileSys::VirtualDir> OpenSDMC(); | 47 | ResultVal<FileSys::VirtualDir> OpenSDMC(); |
| 47 | 48 | ||
| 49 | std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); | ||
| 50 | |||
| 48 | std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); | 51 | std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); |
| 49 | std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); | 52 | std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); |
| 50 | std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); | 53 | std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); |
| @@ -61,6 +64,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::Virtu | |||
| 61 | class VfsDirectoryServiceWrapper { | 64 | class VfsDirectoryServiceWrapper { |
| 62 | public: | 65 | public: |
| 63 | explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); | 66 | explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); |
| 67 | ~VfsDirectoryServiceWrapper(); | ||
| 64 | 68 | ||
| 65 | /** | 69 | /** |
| 66 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | 70 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp index 0ab9c2606..fb487d5bc 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.cpp +++ b/src/core/hle/service/filesystem/fsp_ldr.cpp | |||
| @@ -19,4 +19,6 @@ FSP_LDR::FSP_LDR() : ServiceFramework{"fsp:ldr"} { | |||
| 19 | RegisterHandlers(functions); | 19 | RegisterHandlers(functions); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | FSP_LDR::~FSP_LDR() = default; | ||
| 23 | |||
| 22 | } // namespace Service::FileSystem | 24 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h index fa8e11b4c..8210b7729 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.h +++ b/src/core/hle/service/filesystem/fsp_ldr.h | |||
| @@ -11,6 +11,7 @@ namespace Service::FileSystem { | |||
| 11 | class FSP_LDR final : public ServiceFramework<FSP_LDR> { | 11 | class FSP_LDR final : public ServiceFramework<FSP_LDR> { |
| 12 | public: | 12 | public: |
| 13 | explicit FSP_LDR(); | 13 | explicit FSP_LDR(); |
| 14 | ~FSP_LDR() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::FileSystem | 17 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp index 32b0ae454..378201610 100644 --- a/src/core/hle/service/filesystem/fsp_pr.cpp +++ b/src/core/hle/service/filesystem/fsp_pr.cpp | |||
| @@ -20,4 +20,6 @@ FSP_PR::FSP_PR() : ServiceFramework{"fsp:pr"} { | |||
| 20 | RegisterHandlers(functions); | 20 | RegisterHandlers(functions); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | FSP_PR::~FSP_PR() = default; | ||
| 24 | |||
| 23 | } // namespace Service::FileSystem | 25 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h index 62edcd08a..556ae5ce9 100644 --- a/src/core/hle/service/filesystem/fsp_pr.h +++ b/src/core/hle/service/filesystem/fsp_pr.h | |||
| @@ -11,6 +11,7 @@ namespace Service::FileSystem { | |||
| 11 | class FSP_PR final : public ServiceFramework<FSP_PR> { | 11 | class FSP_PR final : public ServiceFramework<FSP_PR> { |
| 12 | public: | 12 | public: |
| 13 | explicit FSP_PR(); | 13 | explicit FSP_PR(); |
| 14 | ~FSP_PR() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::FileSystem | 17 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 5759299fe..cabaf5a55 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -26,6 +26,17 @@ | |||
| 26 | 26 | ||
| 27 | namespace Service::FileSystem { | 27 | namespace Service::FileSystem { |
| 28 | 28 | ||
| 29 | enum class FileSystemType : u8 { | ||
| 30 | Invalid0 = 0, | ||
| 31 | Invalid1 = 1, | ||
| 32 | Logo = 2, | ||
| 33 | ContentControl = 3, | ||
| 34 | ContentManual = 4, | ||
| 35 | ContentMeta = 5, | ||
| 36 | ContentData = 6, | ||
| 37 | ApplicationPackage = 7, | ||
| 38 | }; | ||
| 39 | |||
| 29 | class IStorage final : public ServiceFramework<IStorage> { | 40 | class IStorage final : public ServiceFramework<IStorage> { |
| 30 | public: | 41 | public: |
| 31 | explicit IStorage(FileSys::VirtualFile backend_) | 42 | explicit IStorage(FileSys::VirtualFile backend_) |
| @@ -420,7 +431,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 420 | {0, nullptr, "MountContent"}, | 431 | {0, nullptr, "MountContent"}, |
| 421 | {1, &FSP_SRV::Initialize, "Initialize"}, | 432 | {1, &FSP_SRV::Initialize, "Initialize"}, |
| 422 | {2, nullptr, "OpenDataFileSystemByCurrentProcess"}, | 433 | {2, nullptr, "OpenDataFileSystemByCurrentProcess"}, |
| 423 | {7, nullptr, "OpenFileSystemWithPatch"}, | 434 | {7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"}, |
| 424 | {8, nullptr, "OpenFileSystemWithId"}, | 435 | {8, nullptr, "OpenFileSystemWithId"}, |
| 425 | {9, nullptr, "OpenDataFileSystemByApplicationId"}, | 436 | {9, nullptr, "OpenDataFileSystemByApplicationId"}, |
| 426 | {11, nullptr, "OpenBisFileSystem"}, | 437 | {11, nullptr, "OpenBisFileSystem"}, |
| @@ -444,7 +455,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 444 | {34, nullptr, "GetCacheStorageSize"}, | 455 | {34, nullptr, "GetCacheStorageSize"}, |
| 445 | {51, &FSP_SRV::MountSaveData, "MountSaveData"}, | 456 | {51, &FSP_SRV::MountSaveData, "MountSaveData"}, |
| 446 | {52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"}, | 457 | {52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"}, |
| 447 | {53, nullptr, "OpenReadOnlySaveDataFileSystem"}, | 458 | {53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"}, |
| 448 | {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"}, | 459 | {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"}, |
| 449 | {58, nullptr, "ReadSaveDataFileSystemExtraData"}, | 460 | {58, nullptr, "ReadSaveDataFileSystemExtraData"}, |
| 450 | {59, nullptr, "WriteSaveDataFileSystemExtraData"}, | 461 | {59, nullptr, "WriteSaveDataFileSystemExtraData"}, |
| @@ -509,6 +520,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 509 | RegisterHandlers(functions); | 520 | RegisterHandlers(functions); |
| 510 | } | 521 | } |
| 511 | 522 | ||
| 523 | FSP_SRV::~FSP_SRV() = default; | ||
| 524 | |||
| 512 | void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { | 525 | void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { |
| 513 | LOG_WARNING(Service_FS, "(STUBBED) called"); | 526 | LOG_WARNING(Service_FS, "(STUBBED) called"); |
| 514 | 527 | ||
| @@ -516,6 +529,16 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 516 | rb.Push(RESULT_SUCCESS); | 529 | rb.Push(RESULT_SUCCESS); |
| 517 | } | 530 | } |
| 518 | 531 | ||
| 532 | void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { | ||
| 533 | IPC::RequestParser rp{ctx}; | ||
| 534 | |||
| 535 | const auto type = rp.PopRaw<FileSystemType>(); | ||
| 536 | const auto title_id = rp.PopRaw<u64>(); | ||
| 537 | |||
| 538 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | ||
| 539 | rb.Push(ResultCode(-1)); | ||
| 540 | } | ||
| 541 | |||
| 519 | void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { | 542 | void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { |
| 520 | LOG_DEBUG(Service_FS, "called"); | 543 | LOG_DEBUG(Service_FS, "called"); |
| 521 | 544 | ||
| @@ -563,6 +586,11 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { | |||
| 563 | rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); | 586 | rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); |
| 564 | } | 587 | } |
| 565 | 588 | ||
| 589 | void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) { | ||
| 590 | LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); | ||
| 591 | MountSaveData(ctx); | ||
| 592 | } | ||
| 593 | |||
| 566 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 594 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { |
| 567 | LOG_WARNING(Service_FS, "(STUBBED) called"); | 595 | LOG_WARNING(Service_FS, "(STUBBED) called"); |
| 568 | 596 | ||
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index f073ac523..4aa0358cb 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -16,13 +16,15 @@ namespace Service::FileSystem { | |||
| 16 | class FSP_SRV final : public ServiceFramework<FSP_SRV> { | 16 | class FSP_SRV final : public ServiceFramework<FSP_SRV> { |
| 17 | public: | 17 | public: |
| 18 | explicit FSP_SRV(); | 18 | explicit FSP_SRV(); |
| 19 | ~FSP_SRV() = default; | 19 | ~FSP_SRV() override; |
| 20 | 20 | ||
| 21 | private: | 21 | private: |
| 22 | void Initialize(Kernel::HLERequestContext& ctx); | 22 | void Initialize(Kernel::HLERequestContext& ctx); |
| 23 | void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx); | ||
| 23 | void MountSdCard(Kernel::HLERequestContext& ctx); | 24 | void MountSdCard(Kernel::HLERequestContext& ctx); |
| 24 | void CreateSaveData(Kernel::HLERequestContext& ctx); | 25 | void CreateSaveData(Kernel::HLERequestContext& ctx); |
| 25 | void MountSaveData(Kernel::HLERequestContext& ctx); | 26 | void MountSaveData(Kernel::HLERequestContext& ctx); |
| 27 | void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); | ||
| 26 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | 28 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); |
| 27 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 29 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 28 | void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); | 30 | void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index f2b0e509a..d9225d624 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -118,6 +118,8 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { | |||
| 118 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 118 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 119 | : ServiceFramework(name), module(std::move(module)) {} | 119 | : ServiceFramework(name), module(std::move(module)) {} |
| 120 | 120 | ||
| 121 | Module::Interface::~Interface() = default; | ||
| 122 | |||
| 121 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 123 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 122 | auto module = std::make_shared<Module>(); | 124 | auto module = std::make_shared<Module>(); |
| 123 | std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); | 125 | std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index c1b36518a..e762840cb 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Interface : public ServiceFramework<Interface> { | 13 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 14 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | void CreateFriendService(Kernel::HLERequestContext& ctx); | 18 | void CreateFriendService(Kernel::HLERequestContext& ctx); |
| 18 | 19 | ||
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp index 27c6a09e2..5a6840af5 100644 --- a/src/core/hle/service/friend/interface.cpp +++ b/src/core/hle/service/friend/interface.cpp | |||
| @@ -16,4 +16,6 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name) | |||
| 16 | RegisterHandlers(functions); | 16 | RegisterHandlers(functions); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | Friend::~Friend() = default; | ||
| 20 | |||
| 19 | } // namespace Service::Friend | 21 | } // namespace Service::Friend |
diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/interface.h index 89dae8471..1963def39 100644 --- a/src/core/hle/service/friend/interface.h +++ b/src/core/hle/service/friend/interface.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Friend { | |||
| 11 | class Friend final : public Module::Interface { | 11 | class Friend final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Friend(std::shared_ptr<Module> module, const char* name); | 13 | explicit Friend(std::shared_ptr<Module> module, const char* name); |
| 14 | ~Friend() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Friend | 17 | } // namespace Service::Friend |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 0d31abe8b..7c6b0a4e6 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 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 <atomic> | ||
| 6 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 6 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| @@ -78,7 +77,7 @@ private: | |||
| 78 | SharedMemory mem{}; | 77 | SharedMemory mem{}; |
| 79 | std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory)); | 78 | std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory)); |
| 80 | 79 | ||
| 81 | if (is_device_reload_pending.exchange(false)) | 80 | if (Settings::values.is_device_reload_pending.exchange(false)) |
| 82 | LoadInputDevices(); | 81 | LoadInputDevices(); |
| 83 | 82 | ||
| 84 | // Set up controllers as neon red+blue Joy-Con attached to console | 83 | // Set up controllers as neon red+blue Joy-Con attached to console |
| @@ -90,7 +89,7 @@ private: | |||
| 90 | controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; | 89 | controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; |
| 91 | controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; | 90 | controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; |
| 92 | 91 | ||
| 93 | for (size_t controller = 0; controller < mem.controllers.size(); controller++) { | 92 | for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) { |
| 94 | for (auto& layout : mem.controllers[controller].layouts) { | 93 | for (auto& layout : mem.controllers[controller].layouts) { |
| 95 | layout.header.num_entries = HID_NUM_ENTRIES; | 94 | layout.header.num_entries = HID_NUM_ENTRIES; |
| 96 | layout.header.max_entry_index = HID_NUM_ENTRIES - 1; | 95 | layout.header.max_entry_index = HID_NUM_ENTRIES - 1; |
| @@ -267,7 +266,6 @@ private: | |||
| 267 | CoreTiming::EventType* pad_update_event; | 266 | CoreTiming::EventType* pad_update_event; |
| 268 | 267 | ||
| 269 | // Stored input state info | 268 | // Stored input state info |
| 270 | std::atomic<bool> is_device_reload_pending{true}; | ||
| 271 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | 269 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> |
| 272 | buttons; | 270 | buttons; |
| 273 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; | 271 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; |
| @@ -315,7 +313,7 @@ public: | |||
| 315 | {64, nullptr, "DeactivateJoySixAxisSensor"}, | 313 | {64, nullptr, "DeactivateJoySixAxisSensor"}, |
| 316 | {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, | 314 | {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, |
| 317 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, | 315 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, |
| 318 | {67, nullptr, "StopSixAxisSensor"}, | 316 | {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, |
| 319 | {68, nullptr, "IsSixAxisSensorFusionEnabled"}, | 317 | {68, nullptr, "IsSixAxisSensorFusionEnabled"}, |
| 320 | {69, nullptr, "EnableSixAxisSensorFusion"}, | 318 | {69, nullptr, "EnableSixAxisSensorFusion"}, |
| 321 | {70, nullptr, "SetSixAxisSensorFusionParameters"}, | 319 | {70, nullptr, "SetSixAxisSensorFusionParameters"}, |
| @@ -331,7 +329,7 @@ public: | |||
| 331 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, | 329 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, |
| 332 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, | 330 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, |
| 333 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | 331 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, |
| 334 | {91, nullptr, "ActivateGesture"}, | 332 | {91, &Hid::ActivateGesture, "ActivateGesture"}, |
| 335 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, | 333 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, |
| 336 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, | 334 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, |
| 337 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, | 335 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, |
| @@ -340,7 +338,7 @@ public: | |||
| 340 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, | 338 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, |
| 341 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, | 339 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, |
| 342 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, | 340 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, |
| 343 | {109, nullptr, "ActivateNpadWithRevision"}, | 341 | {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, |
| 344 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, | 342 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, |
| 345 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, | 343 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, |
| 346 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, | 344 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, |
| @@ -366,8 +364,8 @@ public: | |||
| 366 | {208, nullptr, "GetActualVibrationGcErmCommand"}, | 364 | {208, nullptr, "GetActualVibrationGcErmCommand"}, |
| 367 | {209, nullptr, "BeginPermitVibrationSession"}, | 365 | {209, nullptr, "BeginPermitVibrationSession"}, |
| 368 | {210, nullptr, "EndPermitVibrationSession"}, | 366 | {210, nullptr, "EndPermitVibrationSession"}, |
| 369 | {300, nullptr, "ActivateConsoleSixAxisSensor"}, | 367 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, |
| 370 | {301, nullptr, "StartConsoleSixAxisSensor"}, | 368 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, |
| 371 | {302, nullptr, "StopConsoleSixAxisSensor"}, | 369 | {302, nullptr, "StopConsoleSixAxisSensor"}, |
| 372 | {303, nullptr, "ActivateSevenSixAxisSensor"}, | 370 | {303, nullptr, "ActivateSevenSixAxisSensor"}, |
| 373 | {304, nullptr, "StartSevenSixAxisSensor"}, | 371 | {304, nullptr, "StartSevenSixAxisSensor"}, |
| @@ -581,6 +579,36 @@ private: | |||
| 581 | rb.Push(RESULT_SUCCESS); | 579 | rb.Push(RESULT_SUCCESS); |
| 582 | LOG_WARNING(Service_HID, "(STUBBED) called"); | 580 | LOG_WARNING(Service_HID, "(STUBBED) called"); |
| 583 | } | 581 | } |
| 582 | |||
| 583 | void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 584 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 585 | rb.Push(RESULT_SUCCESS); | ||
| 586 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 587 | } | ||
| 588 | |||
| 589 | void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 590 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 591 | rb.Push(RESULT_SUCCESS); | ||
| 592 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 593 | } | ||
| 594 | |||
| 595 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 596 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 597 | rb.Push(RESULT_SUCCESS); | ||
| 598 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 599 | } | ||
| 600 | |||
| 601 | void ActivateGesture(Kernel::HLERequestContext& ctx) { | ||
| 602 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 603 | rb.Push(RESULT_SUCCESS); | ||
| 604 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 605 | } | ||
| 606 | |||
| 607 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { | ||
| 608 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 609 | rb.Push(RESULT_SUCCESS); | ||
| 610 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 611 | } | ||
| 584 | }; | 612 | }; |
| 585 | 613 | ||
| 586 | class HidDbg final : public ServiceFramework<HidDbg> { | 614 | class HidDbg final : public ServiceFramework<HidDbg> { |
| @@ -797,7 +825,9 @@ public: | |||
| 797 | } | 825 | } |
| 798 | }; | 826 | }; |
| 799 | 827 | ||
| 800 | void ReloadInputDevices() {} | 828 | void ReloadInputDevices() { |
| 829 | Settings::values.is_device_reload_pending.store(true); | ||
| 830 | } | ||
| 801 | 831 | ||
| 802 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 832 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 803 | std::make_shared<Hid>()->InstallAsService(service_manager); | 833 | std::make_shared<Hid>()->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index aaf311912..e587ad0d8 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -33,6 +33,8 @@ IRS::IRS() : ServiceFramework{"irs"} { | |||
| 33 | RegisterHandlers(functions); | 33 | RegisterHandlers(functions); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | IRS::~IRS() = default; | ||
| 37 | |||
| 36 | IRS_SYS::IRS_SYS() : ServiceFramework{"irs:sys"} { | 38 | IRS_SYS::IRS_SYS() : ServiceFramework{"irs:sys"} { |
| 37 | // clang-format off | 39 | // clang-format off |
| 38 | static const FunctionInfo functions[] = { | 40 | static const FunctionInfo functions[] = { |
| @@ -46,4 +48,6 @@ IRS_SYS::IRS_SYS() : ServiceFramework{"irs:sys"} { | |||
| 46 | RegisterHandlers(functions); | 48 | RegisterHandlers(functions); |
| 47 | } | 49 | } |
| 48 | 50 | ||
| 51 | IRS_SYS::~IRS_SYS() = default; | ||
| 52 | |||
| 49 | } // namespace Service::HID | 53 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index a8be701c7..6fb16b45d 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h | |||
| @@ -11,11 +11,13 @@ namespace Service::HID { | |||
| 11 | class IRS final : public ServiceFramework<IRS> { | 11 | class IRS final : public ServiceFramework<IRS> { |
| 12 | public: | 12 | public: |
| 13 | explicit IRS(); | 13 | explicit IRS(); |
| 14 | ~IRS() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | class IRS_SYS final : public ServiceFramework<IRS_SYS> { | 17 | class IRS_SYS final : public ServiceFramework<IRS_SYS> { |
| 17 | public: | 18 | public: |
| 18 | explicit IRS_SYS(); | 19 | explicit IRS_SYS(); |
| 20 | ~IRS_SYS() override; | ||
| 19 | }; | 21 | }; |
| 20 | 22 | ||
| 21 | } // namespace Service::HID | 23 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp index 49f733f60..c8e9125f6 100644 --- a/src/core/hle/service/hid/xcd.cpp +++ b/src/core/hle/service/hid/xcd.cpp | |||
| @@ -34,4 +34,6 @@ XCD_SYS::XCD_SYS() : ServiceFramework{"xcd:sys"} { | |||
| 34 | RegisterHandlers(functions); | 34 | RegisterHandlers(functions); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | XCD_SYS::~XCD_SYS() = default; | ||
| 38 | |||
| 37 | } // namespace Service::HID | 39 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/xcd.h b/src/core/hle/service/hid/xcd.h index 232a044df..fd506d303 100644 --- a/src/core/hle/service/hid/xcd.h +++ b/src/core/hle/service/hid/xcd.h | |||
| @@ -11,6 +11,7 @@ namespace Service::HID { | |||
| 11 | class XCD_SYS final : public ServiceFramework<XCD_SYS> { | 11 | class XCD_SYS final : public ServiceFramework<XCD_SYS> { |
| 12 | public: | 12 | public: |
| 13 | explicit XCD_SYS(); | 13 | explicit XCD_SYS(); |
| 14 | ~XCD_SYS() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::HID | 17 | } // namespace Service::HID |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 098da2a41..c89157a4d 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -99,7 +99,7 @@ private: | |||
| 99 | std::string thread; | 99 | std::string thread; |
| 100 | while (addr < end_addr) { | 100 | while (addr < end_addr) { |
| 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; | 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; |
| 102 | const size_t length{Memory::Read8(addr++)}; | 102 | const std::size_t length{Memory::Read8(addr++)}; |
| 103 | 103 | ||
| 104 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { | 104 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { |
| 105 | ++addr; | 105 | ++addr; |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 4f7543af5..f8d2127d9 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -14,6 +14,8 @@ namespace Service::NFP { | |||
| 14 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 14 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 15 | : ServiceFramework(name), module(std::move(module)) {} | 15 | : ServiceFramework(name), module(std::move(module)) {} |
| 16 | 16 | ||
| 17 | Module::Interface::~Interface() = default; | ||
| 18 | |||
| 17 | class IUser final : public ServiceFramework<IUser> { | 19 | class IUser final : public ServiceFramework<IUser> { |
| 18 | public: | 20 | public: |
| 19 | IUser() : ServiceFramework("IUser") { | 21 | IUser() : ServiceFramework("IUser") { |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 0cd7be3d5..77df343c4 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Interface : public ServiceFramework<Interface> { | 13 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 14 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | void CreateUserInterface(Kernel::HLERequestContext& ctx); | 18 | void CreateUserInterface(Kernel::HLERequestContext& ctx); |
| 18 | 19 | ||
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index b608fe693..784a87c1b 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp | |||
| @@ -14,4 +14,6 @@ NFP_User::NFP_User(std::shared_ptr<Module> module) | |||
| 14 | RegisterHandlers(functions); | 14 | RegisterHandlers(functions); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | NFP_User::~NFP_User() = default; | ||
| 18 | |||
| 17 | } // namespace Service::NFP | 19 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 700043114..65d9aaf48 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h | |||
| @@ -11,6 +11,7 @@ namespace Service::NFP { | |||
| 11 | class NFP_User final : public Module::Interface { | 11 | class NFP_User final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit NFP_User(std::shared_ptr<Module> module); | 13 | explicit NFP_User(std::shared_ptr<Module> module); |
| 14 | ~NFP_User() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::NFP | 17 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index ed4f5f539..10611ed6a 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | {1, &IRequest::GetResult, "GetResult"}, | 31 | {1, &IRequest::GetResult, "GetResult"}, |
| 32 | {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, | 32 | {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, |
| 33 | {3, &IRequest::Cancel, "Cancel"}, | 33 | {3, &IRequest::Cancel, "Cancel"}, |
| 34 | {4, nullptr, "Submit"}, | 34 | {4, &IRequest::Submit, "Submit"}, |
| 35 | {5, nullptr, "SetRequirement"}, | 35 | {5, nullptr, "SetRequirement"}, |
| 36 | {6, nullptr, "SetRequirementPreset"}, | 36 | {6, nullptr, "SetRequirementPreset"}, |
| 37 | {8, nullptr, "SetPriority"}, | 37 | {8, nullptr, "SetPriority"}, |
| @@ -61,6 +61,12 @@ public: | |||
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | private: | 63 | private: |
| 64 | void Submit(Kernel::HLERequestContext& ctx) { | ||
| 65 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 66 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 67 | rb.Push(RESULT_SUCCESS); | ||
| 68 | } | ||
| 69 | |||
| 64 | void GetRequestState(Kernel::HLERequestContext& ctx) { | 70 | void GetRequestState(Kernel::HLERequestContext& ctx) { |
| 65 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 71 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 66 | IPC::ResponseBuilder rb{ctx, 3}; | 72 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -114,10 +120,11 @@ public: | |||
| 114 | 120 | ||
| 115 | private: | 121 | private: |
| 116 | void GetClientId(Kernel::HLERequestContext& ctx) { | 122 | void GetClientId(Kernel::HLERequestContext& ctx) { |
| 123 | static constexpr u32 client_id = 1; | ||
| 117 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 124 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 118 | IPC::ResponseBuilder rb{ctx, 4}; | 125 | IPC::ResponseBuilder rb{ctx, 4}; |
| 119 | rb.Push(RESULT_SUCCESS); | 126 | rb.Push(RESULT_SUCCESS); |
| 120 | rb.Push<u64>(0); | 127 | rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid |
| 121 | } | 128 | } |
| 122 | void CreateScanRequest(Kernel::HLERequestContext& ctx) { | 129 | void CreateScanRequest(Kernel::HLERequestContext& ctx) { |
| 123 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 130 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| @@ -141,10 +148,16 @@ private: | |||
| 141 | rb.Push(RESULT_SUCCESS); | 148 | rb.Push(RESULT_SUCCESS); |
| 142 | } | 149 | } |
| 143 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { | 150 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 144 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 151 | ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size"); |
| 152 | u128 uuid{}; | ||
| 153 | auto buffer = ctx.ReadBuffer(); | ||
| 154 | std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); | ||
| 155 | |||
| 156 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | ||
| 145 | 157 | ||
| 146 | rb.Push(RESULT_SUCCESS); | 158 | rb.Push(RESULT_SUCCESS); |
| 147 | rb.PushIpcInterface<INetworkProfile>(); | 159 | rb.PushIpcInterface<INetworkProfile>(); |
| 160 | rb.PushRaw<u128>(uuid); | ||
| 148 | 161 | ||
| 149 | LOG_DEBUG(Service_NIFM, "called"); | 162 | LOG_DEBUG(Service_NIFM, "called"); |
| 150 | } | 163 | } |
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index bd05b0a70..c1737defa 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp | |||
| @@ -2,6 +2,10 @@ | |||
| 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 <chrono> | ||
| 6 | #include <ctime> | ||
| 7 | #include "core/hle/ipc_helpers.h" | ||
| 8 | #include "core/hle/kernel/event.h" | ||
| 5 | #include "core/hle/service/nim/nim.h" | 9 | #include "core/hle/service/nim/nim.h" |
| 6 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 7 | #include "core/hle/service/sm/sm.h" | 11 | #include "core/hle/service/sm/sm.h" |
| @@ -100,19 +104,111 @@ public: | |||
| 100 | } | 104 | } |
| 101 | }; | 105 | }; |
| 102 | 106 | ||
| 107 | class IEnsureNetworkClockAvailabilityService final | ||
| 108 | : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { | ||
| 109 | public: | ||
| 110 | IEnsureNetworkClockAvailabilityService() | ||
| 111 | : ServiceFramework("IEnsureNetworkClockAvailabilityService") { | ||
| 112 | static const FunctionInfo functions[] = { | ||
| 113 | {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, | ||
| 114 | {1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent, | ||
| 115 | "GetFinishNotificationEvent"}, | ||
| 116 | {2, &IEnsureNetworkClockAvailabilityService::GetResult, "GetResult"}, | ||
| 117 | {3, &IEnsureNetworkClockAvailabilityService::Cancel, "Cancel"}, | ||
| 118 | {4, &IEnsureNetworkClockAvailabilityService::IsProcessing, "IsProcessing"}, | ||
| 119 | {5, &IEnsureNetworkClockAvailabilityService::GetServerTime, "GetServerTime"}, | ||
| 120 | }; | ||
| 121 | RegisterHandlers(functions); | ||
| 122 | |||
| 123 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 124 | finished_event = | ||
| 125 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, | ||
| 126 | "IEnsureNetworkClockAvailabilityService:FinishEvent"); | ||
| 127 | } | ||
| 128 | |||
| 129 | private: | ||
| 130 | Kernel::SharedPtr<Kernel::Event> finished_event; | ||
| 131 | |||
| 132 | void StartTask(Kernel::HLERequestContext& ctx) { | ||
| 133 | // No need to connect to the internet, just finish the task straight away. | ||
| 134 | finished_event->Signal(); | ||
| 135 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 136 | rb.Push(RESULT_SUCCESS); | ||
| 137 | LOG_DEBUG(Service_NIM, "called"); | ||
| 138 | } | ||
| 139 | |||
| 140 | void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) { | ||
| 141 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 142 | rb.Push(RESULT_SUCCESS); | ||
| 143 | rb.PushCopyObjects(finished_event); | ||
| 144 | LOG_DEBUG(Service_NIM, "called"); | ||
| 145 | } | ||
| 146 | |||
| 147 | void GetResult(Kernel::HLERequestContext& ctx) { | ||
| 148 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 149 | rb.Push(RESULT_SUCCESS); | ||
| 150 | LOG_DEBUG(Service_NIM, "called"); | ||
| 151 | } | ||
| 152 | |||
| 153 | void Cancel(Kernel::HLERequestContext& ctx) { | ||
| 154 | finished_event->Clear(); | ||
| 155 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 156 | rb.Push(RESULT_SUCCESS); | ||
| 157 | LOG_DEBUG(Service_NIM, "called"); | ||
| 158 | } | ||
| 159 | |||
| 160 | void IsProcessing(Kernel::HLERequestContext& ctx) { | ||
| 161 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 162 | rb.Push(RESULT_SUCCESS); | ||
| 163 | rb.PushRaw<u32>(0); // We instantly process the request | ||
| 164 | LOG_DEBUG(Service_NIM, "called"); | ||
| 165 | } | ||
| 166 | |||
| 167 | void GetServerTime(Kernel::HLERequestContext& ctx) { | ||
| 168 | const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>( | ||
| 169 | std::chrono::system_clock::now().time_since_epoch()) | ||
| 170 | .count()}; | ||
| 171 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 172 | rb.Push(RESULT_SUCCESS); | ||
| 173 | rb.PushRaw<s64>(server_time); | ||
| 174 | LOG_DEBUG(Service_NIM, "called"); | ||
| 175 | } | ||
| 176 | }; | ||
| 177 | |||
| 103 | class NTC final : public ServiceFramework<NTC> { | 178 | class NTC final : public ServiceFramework<NTC> { |
| 104 | public: | 179 | public: |
| 105 | explicit NTC() : ServiceFramework{"ntc"} { | 180 | explicit NTC() : ServiceFramework{"ntc"} { |
| 106 | // clang-format off | 181 | // clang-format off |
| 107 | static const FunctionInfo functions[] = { | 182 | static const FunctionInfo functions[] = { |
| 108 | {0, nullptr, "OpenEnsureNetworkClockAvailabilityService"}, | 183 | {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, |
| 109 | {100, nullptr, "SuspendAutonomicTimeCorrection"}, | 184 | {100, &NTC::SuspendAutonomicTimeCorrection, "SuspendAutonomicTimeCorrection"}, |
| 110 | {101, nullptr, "ResumeAutonomicTimeCorrection"}, | 185 | {101, &NTC::ResumeAutonomicTimeCorrection, "ResumeAutonomicTimeCorrection"}, |
| 111 | }; | 186 | }; |
| 112 | // clang-format on | 187 | // clang-format on |
| 113 | 188 | ||
| 114 | RegisterHandlers(functions); | 189 | RegisterHandlers(functions); |
| 115 | } | 190 | } |
| 191 | |||
| 192 | private: | ||
| 193 | void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) { | ||
| 194 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 195 | rb.Push(RESULT_SUCCESS); | ||
| 196 | rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); | ||
| 197 | LOG_DEBUG(Service_NIM, "called"); | ||
| 198 | } | ||
| 199 | |||
| 200 | // TODO(ogniK): Do we need these? | ||
| 201 | void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) { | ||
| 202 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 203 | rb.Push(RESULT_SUCCESS); | ||
| 204 | LOG_WARNING(Service_NIM, "(STUBBED) called"); | ||
| 205 | } | ||
| 206 | |||
| 207 | void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) { | ||
| 208 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 209 | rb.Push(RESULT_SUCCESS); | ||
| 210 | LOG_WARNING(Service_NIM, "(STUBBED) called"); | ||
| 211 | } | ||
| 116 | }; | 212 | }; |
| 117 | 213 | ||
| 118 | void InstallInterfaces(SM::ServiceManager& sm) { | 214 | void InstallInterfaces(SM::ServiceManager& sm) { |
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 51638793d..1069d103f 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -2,12 +2,30 @@ | |||
| 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 <vector> | ||
| 8 | |||
| 9 | #include <FontChineseSimplified.h> | ||
| 10 | #include <FontChineseTraditional.h> | ||
| 11 | #include <FontExtendedChineseSimplified.h> | ||
| 12 | #include <FontKorean.h> | ||
| 13 | #include <FontNintendoExtended.h> | ||
| 14 | #include <FontStandard.h> | ||
| 15 | |||
| 16 | #include "common/assert.h" | ||
| 5 | #include "common/common_paths.h" | 17 | #include "common/common_paths.h" |
| 18 | #include "common/common_types.h" | ||
| 6 | #include "common/file_util.h" | 19 | #include "common/file_util.h" |
| 20 | #include "common/logging/log.h" | ||
| 21 | #include "common/swap.h" | ||
| 7 | #include "core/core.h" | 22 | #include "core/core.h" |
| 8 | #include "core/file_sys/bis_factory.h" | 23 | #include "core/file_sys/content_archive.h" |
| 24 | #include "core/file_sys/nca_metadata.h" | ||
| 25 | #include "core/file_sys/registered_cache.h" | ||
| 9 | #include "core/file_sys/romfs.h" | 26 | #include "core/file_sys/romfs.h" |
| 10 | #include "core/hle/ipc_helpers.h" | 27 | #include "core/hle/ipc_helpers.h" |
| 28 | #include "core/hle/kernel/shared_memory.h" | ||
| 11 | #include "core/hle/service/filesystem/filesystem.h" | 29 | #include "core/hle/service/filesystem/filesystem.h" |
| 12 | #include "core/hle/service/ns/pl_u.h" | 30 | #include "core/hle/service/ns/pl_u.h" |
| 13 | 31 | ||
| @@ -26,49 +44,41 @@ struct FontRegion { | |||
| 26 | u32 size; | 44 | u32 size; |
| 27 | }; | 45 | }; |
| 28 | 46 | ||
| 29 | static constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ | 47 | constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ |
| 30 | std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), | 48 | std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), |
| 31 | std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), | 49 | std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), |
| 32 | std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), | 50 | std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), |
| 33 | std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), | 51 | std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), |
| 34 | std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), | 52 | std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), |
| 35 | std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), | 53 | std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), |
| 36 | std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf")}; | 54 | std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"), |
| 55 | }; | ||
| 37 | 56 | ||
| 38 | static constexpr std::array<const char*, 7> SHARED_FONTS_TTF{"FontStandard.ttf", | 57 | constexpr std::array<const char*, 7> SHARED_FONTS_TTF{ |
| 39 | "FontChineseSimplified.ttf", | 58 | "FontStandard.ttf", |
| 40 | "FontExtendedChineseSimplified.ttf", | 59 | "FontChineseSimplified.ttf", |
| 41 | "FontChineseTraditional.ttf", | 60 | "FontExtendedChineseSimplified.ttf", |
| 42 | "FontKorean.ttf", | 61 | "FontChineseTraditional.ttf", |
| 43 | "FontNintendoExtended.ttf", | 62 | "FontKorean.ttf", |
| 44 | "FontNintendoExtended2.ttf"}; | 63 | "FontNintendoExtended.ttf", |
| 64 | "FontNintendoExtended2.ttf", | ||
| 65 | }; | ||
| 45 | 66 | ||
| 46 | // The below data is specific to shared font data dumped from Switch on f/w 2.2 | 67 | // The below data is specific to shared font data dumped from Switch on f/w 2.2 |
| 47 | // Virtual address and offsets/sizes likely will vary by dump | 68 | // Virtual address and offsets/sizes likely will vary by dump |
| 48 | static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; | 69 | constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; |
| 49 | static constexpr u32 EXPECTED_RESULT{ | 70 | constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be |
| 50 | 0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be | 71 | constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be |
| 51 | static constexpr u32 EXPECTED_MAGIC{ | 72 | constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; |
| 52 | 0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be | 73 | constexpr FontRegion EMPTY_REGION{0, 0}; |
| 53 | static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; | ||
| 54 | static constexpr FontRegion EMPTY_REGION{0, 0}; | ||
| 55 | std::vector<FontRegion> | ||
| 56 | SHARED_FONT_REGIONS{}; // Automatically populated based on shared_fonts dump or system archives | ||
| 57 | |||
| 58 | const FontRegion& GetSharedFontRegion(size_t index) { | ||
| 59 | if (index >= SHARED_FONT_REGIONS.size() || SHARED_FONT_REGIONS.empty()) { | ||
| 60 | // No font fallback | ||
| 61 | return EMPTY_REGION; | ||
| 62 | } | ||
| 63 | return SHARED_FONT_REGIONS.at(index); | ||
| 64 | } | ||
| 65 | 74 | ||
| 66 | enum class LoadState : u32 { | 75 | enum class LoadState : u32 { |
| 67 | Loading = 0, | 76 | Loading = 0, |
| 68 | Done = 1, | 77 | Done = 1, |
| 69 | }; | 78 | }; |
| 70 | 79 | ||
| 71 | void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, size_t& offset) { | 80 | static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, |
| 81 | std::size_t& offset) { | ||
| 72 | ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, | 82 | ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, |
| 73 | "Shared fonts exceeds 17mb!"); | 83 | "Shared fonts exceeds 17mb!"); |
| 74 | ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); | 84 | ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); |
| @@ -85,7 +95,7 @@ void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, s | |||
| 85 | } | 95 | } |
| 86 | 96 | ||
| 87 | static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, | 97 | static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, |
| 88 | size_t& offset) { | 98 | std::size_t& offset) { |
| 89 | ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); | 99 | ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); |
| 90 | const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; | 100 | const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; |
| 91 | std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header | 101 | std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header |
| @@ -95,28 +105,52 @@ static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& out | |||
| 95 | offset += input.size() + (sizeof(u32) * 2); | 105 | offset += input.size() + (sizeof(u32) * 2); |
| 96 | } | 106 | } |
| 97 | 107 | ||
| 108 | // Helper function to make BuildSharedFontsRawRegions a bit nicer | ||
| 98 | static u32 GetU32Swapped(const u8* data) { | 109 | static u32 GetU32Swapped(const u8* data) { |
| 99 | u32 value; | 110 | u32 value; |
| 100 | std::memcpy(&value, data, sizeof(value)); | 111 | std::memcpy(&value, data, sizeof(value)); |
| 101 | return Common::swap32(value); // Helper function to make BuildSharedFontsRawRegions a bit nicer | 112 | return Common::swap32(value); |
| 102 | } | 113 | } |
| 103 | 114 | ||
| 104 | void BuildSharedFontsRawRegions(const std::vector<u8>& input) { | 115 | struct PL_U::Impl { |
| 105 | unsigned cur_offset = 0; // As we can derive the xor key we can just populate the offsets based | 116 | const FontRegion& GetSharedFontRegion(std::size_t index) const { |
| 106 | // on the shared memory dump | 117 | if (index >= shared_font_regions.size() || shared_font_regions.empty()) { |
| 107 | for (size_t i = 0; i < SHARED_FONTS.size(); i++) { | 118 | // No font fallback |
| 108 | // Out of shared fonts/Invalid font | 119 | return EMPTY_REGION; |
| 109 | if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) | 120 | } |
| 110 | break; | 121 | return shared_font_regions.at(index); |
| 111 | const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ | ||
| 112 | EXPECTED_MAGIC; // Derive key withing inverse xor | ||
| 113 | const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; | ||
| 114 | SHARED_FONT_REGIONS.push_back(FontRegion{cur_offset + 8, SIZE}); | ||
| 115 | cur_offset += SIZE + 8; | ||
| 116 | } | 122 | } |
| 117 | } | ||
| 118 | 123 | ||
| 119 | PL_U::PL_U() : ServiceFramework("pl:u") { | 124 | void BuildSharedFontsRawRegions(const std::vector<u8>& input) { |
| 125 | // As we can derive the xor key we can just populate the offsets | ||
| 126 | // based on the shared memory dump | ||
| 127 | unsigned cur_offset = 0; | ||
| 128 | |||
| 129 | for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) { | ||
| 130 | // Out of shared fonts/invalid font | ||
| 131 | if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | |||
| 135 | // Derive key withing inverse xor | ||
| 136 | const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC; | ||
| 137 | const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; | ||
| 138 | shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE}); | ||
| 139 | cur_offset += SIZE + 8; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Handle to shared memory region designated for a shared font | ||
| 144 | Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; | ||
| 145 | |||
| 146 | /// Backing memory for the shared font data | ||
| 147 | std::shared_ptr<std::vector<u8>> shared_font; | ||
| 148 | |||
| 149 | // Automatically populated based on shared_fonts dump or system archives. | ||
| 150 | std::vector<FontRegion> shared_font_regions; | ||
| 151 | }; | ||
| 152 | |||
| 153 | PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | ||
| 120 | static const FunctionInfo functions[] = { | 154 | static const FunctionInfo functions[] = { |
| 121 | {0, &PL_U::RequestLoad, "RequestLoad"}, | 155 | {0, &PL_U::RequestLoad, "RequestLoad"}, |
| 122 | {1, &PL_U::GetLoadState, "GetLoadState"}, | 156 | {1, &PL_U::GetLoadState, "GetLoadState"}, |
| @@ -128,11 +162,11 @@ PL_U::PL_U() : ServiceFramework("pl:u") { | |||
| 128 | RegisterHandlers(functions); | 162 | RegisterHandlers(functions); |
| 129 | // Attempt to load shared font data from disk | 163 | // Attempt to load shared font data from disk |
| 130 | const auto nand = FileSystem::GetSystemNANDContents(); | 164 | const auto nand = FileSystem::GetSystemNANDContents(); |
| 131 | size_t offset = 0; | 165 | std::size_t offset = 0; |
| 132 | // Rebuild shared fonts from data ncas | 166 | // Rebuild shared fonts from data ncas |
| 133 | if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), | 167 | if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), |
| 134 | FileSys::ContentRecordType::Data)) { | 168 | FileSys::ContentRecordType::Data)) { |
| 135 | shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); | 169 | impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); |
| 136 | for (auto font : SHARED_FONTS) { | 170 | for (auto font : SHARED_FONTS) { |
| 137 | const auto nca = | 171 | const auto nca = |
| 138 | nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); | 172 | nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); |
| @@ -168,12 +202,12 @@ PL_U::PL_U() : ServiceFramework("pl:u") { | |||
| 168 | static_cast<u32>(offset + 8), | 202 | static_cast<u32>(offset + 8), |
| 169 | static_cast<u32>((font_data_u32.size() * sizeof(u32)) - | 203 | static_cast<u32>((font_data_u32.size() * sizeof(u32)) - |
| 170 | 8)}; // Font offset and size do not account for the header | 204 | 8)}; // Font offset and size do not account for the header |
| 171 | DecryptSharedFont(font_data_u32, *shared_font, offset); | 205 | DecryptSharedFont(font_data_u32, *impl->shared_font, offset); |
| 172 | SHARED_FONT_REGIONS.push_back(region); | 206 | impl->shared_font_regions.push_back(region); |
| 173 | } | 207 | } |
| 174 | 208 | ||
| 175 | } else { | 209 | } else { |
| 176 | shared_font = std::make_shared<std::vector<u8>>( | 210 | impl->shared_font = std::make_shared<std::vector<u8>>( |
| 177 | SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size | 211 | SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size |
| 178 | 212 | ||
| 179 | const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); | 213 | const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); |
| @@ -197,8 +231,8 @@ PL_U::PL_U() : ServiceFramework("pl:u") { | |||
| 197 | static_cast<u32>(offset + 8), | 231 | static_cast<u32>(offset + 8), |
| 198 | static_cast<u32>(ttf_bytes.size())}; // Font offset and size do not account | 232 | static_cast<u32>(ttf_bytes.size())}; // Font offset and size do not account |
| 199 | // for the header | 233 | // for the header |
| 200 | EncryptSharedFont(ttf_bytes, *shared_font, offset); | 234 | EncryptSharedFont(ttf_bytes, *impl->shared_font, offset); |
| 201 | SHARED_FONT_REGIONS.push_back(region); | 235 | impl->shared_font_regions.push_back(region); |
| 202 | } else { | 236 | } else { |
| 203 | LOG_WARNING(Service_NS, "Unable to load font: {}", font_ttf); | 237 | LOG_WARNING(Service_NS, "Unable to load font: {}", font_ttf); |
| 204 | } | 238 | } |
| @@ -213,14 +247,35 @@ PL_U::PL_U() : ServiceFramework("pl:u") { | |||
| 213 | if (file.IsOpen()) { | 247 | if (file.IsOpen()) { |
| 214 | // Read shared font data | 248 | // Read shared font data |
| 215 | ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); | 249 | ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); |
| 216 | file.ReadBytes(shared_font->data(), shared_font->size()); | 250 | file.ReadBytes(impl->shared_font->data(), impl->shared_font->size()); |
| 217 | BuildSharedFontsRawRegions(*shared_font); | 251 | impl->BuildSharedFontsRawRegions(*impl->shared_font); |
| 218 | } else { | 252 | } else { |
| 219 | LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); | 253 | LOG_WARNING(Service_NS, |
| 254 | "Shared Font file missing. Loading open source replacement from memory"); | ||
| 255 | |||
| 256 | // clang-format off | ||
| 257 | const std::vector<std::vector<u8>> open_source_shared_fonts_ttf = { | ||
| 258 | {std::begin(FontChineseSimplified), std::end(FontChineseSimplified)}, | ||
| 259 | {std::begin(FontChineseTraditional), std::end(FontChineseTraditional)}, | ||
| 260 | {std::begin(FontExtendedChineseSimplified), std::end(FontExtendedChineseSimplified)}, | ||
| 261 | {std::begin(FontKorean), std::end(FontKorean)}, | ||
| 262 | {std::begin(FontNintendoExtended), std::end(FontNintendoExtended)}, | ||
| 263 | {std::begin(FontStandard), std::end(FontStandard)}, | ||
| 264 | }; | ||
| 265 | // clang-format on | ||
| 266 | |||
| 267 | for (const std::vector<u8>& font_ttf : open_source_shared_fonts_ttf) { | ||
| 268 | const FontRegion region{static_cast<u32>(offset + 8), | ||
| 269 | static_cast<u32>(font_ttf.size())}; | ||
| 270 | EncryptSharedFont(font_ttf, *impl->shared_font, offset); | ||
| 271 | impl->shared_font_regions.push_back(region); | ||
| 272 | } | ||
| 220 | } | 273 | } |
| 221 | } | 274 | } |
| 222 | } | 275 | } |
| 223 | 276 | ||
| 277 | PL_U::~PL_U() = default; | ||
| 278 | |||
| 224 | void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { | 279 | void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { |
| 225 | IPC::RequestParser rp{ctx}; | 280 | IPC::RequestParser rp{ctx}; |
| 226 | const u32 shared_font_type{rp.Pop<u32>()}; | 281 | const u32 shared_font_type{rp.Pop<u32>()}; |
| @@ -247,7 +302,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { | |||
| 247 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); | 302 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); |
| 248 | IPC::ResponseBuilder rb{ctx, 3}; | 303 | IPC::ResponseBuilder rb{ctx, 3}; |
| 249 | rb.Push(RESULT_SUCCESS); | 304 | rb.Push(RESULT_SUCCESS); |
| 250 | rb.Push<u32>(GetSharedFontRegion(font_id).size); | 305 | rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); |
| 251 | } | 306 | } |
| 252 | 307 | ||
| 253 | void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | 308 | void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { |
| @@ -257,17 +312,18 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | |||
| 257 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); | 312 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); |
| 258 | IPC::ResponseBuilder rb{ctx, 3}; | 313 | IPC::ResponseBuilder rb{ctx, 3}; |
| 259 | rb.Push(RESULT_SUCCESS); | 314 | rb.Push(RESULT_SUCCESS); |
| 260 | rb.Push<u32>(GetSharedFontRegion(font_id).offset); | 315 | rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); |
| 261 | } | 316 | } |
| 262 | 317 | ||
| 263 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | 318 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { |
| 264 | // Map backing memory for the font data | 319 | // Map backing memory for the font data |
| 265 | Core::CurrentProcess()->vm_manager.MapMemoryBlock( | 320 | Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, |
| 266 | SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared); | 321 | SHARED_FONT_MEM_SIZE, |
| 322 | Kernel::MemoryState::Shared); | ||
| 267 | 323 | ||
| 268 | // Create shared font memory object | 324 | // Create shared font memory object |
| 269 | auto& kernel = Core::System::GetInstance().Kernel(); | 325 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 270 | shared_font_mem = Kernel::SharedMemory::Create( | 326 | impl->shared_font_mem = Kernel::SharedMemory::Create( |
| 271 | kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, | 327 | kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, |
| 272 | Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, | 328 | Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, |
| 273 | "PL_U:shared_font_mem"); | 329 | "PL_U:shared_font_mem"); |
| @@ -275,7 +331,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | |||
| 275 | LOG_DEBUG(Service_NS, "called"); | 331 | LOG_DEBUG(Service_NS, "called"); |
| 276 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 332 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 277 | rb.Push(RESULT_SUCCESS); | 333 | rb.Push(RESULT_SUCCESS); |
| 278 | rb.PushCopyObjects(shared_font_mem); | 334 | rb.PushCopyObjects(impl->shared_font_mem); |
| 279 | } | 335 | } |
| 280 | 336 | ||
| 281 | void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { | 337 | void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { |
| @@ -288,9 +344,9 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { | |||
| 288 | std::vector<u32> font_sizes; | 344 | std::vector<u32> font_sizes; |
| 289 | 345 | ||
| 290 | // TODO(ogniK): Have actual priority order | 346 | // TODO(ogniK): Have actual priority order |
| 291 | for (size_t i = 0; i < SHARED_FONT_REGIONS.size(); i++) { | 347 | for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) { |
| 292 | font_codes.push_back(static_cast<u32>(i)); | 348 | font_codes.push_back(static_cast<u32>(i)); |
| 293 | auto region = GetSharedFontRegion(i); | 349 | auto region = impl->GetSharedFontRegion(i); |
| 294 | font_offsets.push_back(region.offset); | 350 | font_offsets.push_back(region.offset); |
| 295 | font_sizes.push_back(region.size); | 351 | font_sizes.push_back(region.size); |
| 296 | } | 352 | } |
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h index fcc2acab7..253f26a2a 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/pl_u.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include "core/hle/kernel/shared_memory.h" | ||
| 9 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 10 | 9 | ||
| 11 | namespace Service::NS { | 10 | namespace Service::NS { |
| @@ -13,7 +12,7 @@ namespace Service::NS { | |||
| 13 | class PL_U final : public ServiceFramework<PL_U> { | 12 | class PL_U final : public ServiceFramework<PL_U> { |
| 14 | public: | 13 | public: |
| 15 | PL_U(); | 14 | PL_U(); |
| 16 | ~PL_U() = default; | 15 | ~PL_U() override; |
| 17 | 16 | ||
| 18 | private: | 17 | private: |
| 19 | void RequestLoad(Kernel::HLERequestContext& ctx); | 18 | void RequestLoad(Kernel::HLERequestContext& ctx); |
| @@ -23,11 +22,8 @@ private: | |||
| 23 | void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); | 22 | void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); |
| 24 | void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx); | 23 | void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx); |
| 25 | 24 | ||
| 26 | /// Handle to shared memory region designated for a shared font | 25 | struct Impl; |
| 27 | Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; | 26 | std::unique_ptr<Impl> impl; |
| 28 | |||
| 29 | /// Backing memory for the shared font data | ||
| 30 | std::shared_ptr<std::vector<u8>> shared_font; | ||
| 31 | }; | 27 | }; |
| 32 | 28 | ||
| 33 | } // namespace Service::NS | 29 | } // namespace Service::NS |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 0b37098e1..92acc57b1 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -13,6 +13,9 @@ | |||
| 13 | 13 | ||
| 14 | namespace Service::Nvidia::Devices { | 14 | namespace Service::Nvidia::Devices { |
| 15 | 15 | ||
| 16 | nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | ||
| 17 | nvdisp_disp0 ::~nvdisp_disp0() = default; | ||
| 18 | |||
| 16 | u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 19 | u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 17 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | 20 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); |
| 18 | return 0; | 21 | return 0; |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 6f0697b58..a45086e45 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | |||
| @@ -17,8 +17,8 @@ class nvmap; | |||
| 17 | 17 | ||
| 18 | class nvdisp_disp0 final : public nvdevice { | 18 | class nvdisp_disp0 final : public nvdevice { |
| 19 | public: | 19 | public: |
| 20 | explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | 20 | explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); |
| 21 | ~nvdisp_disp0() = default; | 21 | ~nvdisp_disp0(); |
| 22 | 22 | ||
| 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 24 | 24 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 75487c4e8..d8b8037a8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.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 <cstring> | 5 | #include <cstring> |
| 6 | #include <utility> | ||
| 7 | |||
| 6 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 10 | #include "core/core.h" |
| @@ -14,6 +16,9 @@ | |||
| 14 | 16 | ||
| 15 | namespace Service::Nvidia::Devices { | 17 | namespace Service::Nvidia::Devices { |
| 16 | 18 | ||
| 19 | nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | ||
| 20 | nvhost_as_gpu::~nvhost_as_gpu() = default; | ||
| 21 | |||
| 17 | u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 22 | u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 18 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 23 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 19 | command.raw, input.size(), output.size()); | 24 | command.raw, input.size(), output.size()); |
| @@ -66,7 +71,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& | |||
| 66 | } | 71 | } |
| 67 | 72 | ||
| 68 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { | 73 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { |
| 69 | size_t num_entries = input.size() / sizeof(IoctlRemapEntry); | 74 | std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry); |
| 70 | 75 | ||
| 71 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); | 76 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); |
| 72 | 77 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 9f8999d9c..eb14b1da8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <unordered_map> | 8 | #include <unordered_map> |
| 9 | #include <utility> | ||
| 10 | #include <vector> | 9 | #include <vector> |
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| @@ -18,8 +17,8 @@ class nvmap; | |||
| 18 | 17 | ||
| 19 | class nvhost_as_gpu final : public nvdevice { | 18 | class nvhost_as_gpu final : public nvdevice { |
| 20 | public: | 19 | public: |
| 21 | explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | 20 | explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev); |
| 22 | ~nvhost_as_gpu() override = default; | 21 | ~nvhost_as_gpu() override; |
| 23 | 22 | ||
| 24 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 25 | 24 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 5685eb2be..b39fb9ef9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -11,6 +11,9 @@ | |||
| 11 | 11 | ||
| 12 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 13 | 13 | ||
| 14 | nvhost_ctrl::nvhost_ctrl() = default; | ||
| 15 | nvhost_ctrl::~nvhost_ctrl() = default; | ||
| 16 | |||
| 14 | u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 17 | u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 15 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 18 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 16 | command.raw, input.size(), output.size()); | 19 | command.raw, input.size(), output.size()); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 6b496e9fe..6d0de2212 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | |||
| @@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices { | |||
| 13 | 13 | ||
| 14 | class nvhost_ctrl final : public nvdevice { | 14 | class nvhost_ctrl final : public nvdevice { |
| 15 | public: | 15 | public: |
| 16 | nvhost_ctrl() = default; | 16 | nvhost_ctrl(); |
| 17 | ~nvhost_ctrl() override = default; | 17 | ~nvhost_ctrl() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | 20 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index ae421247d..7a88ae029 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | namespace Service::Nvidia::Devices { | 10 | namespace Service::Nvidia::Devices { |
| 11 | 11 | ||
| 12 | nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default; | ||
| 13 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; | ||
| 14 | |||
| 12 | u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 15 | u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 13 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 16 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 14 | command.raw, input.size(), output.size()); | 17 | command.raw, input.size(), output.size()); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index f09113e67..3bbf028ad 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | |||
| @@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices { | |||
| 13 | 13 | ||
| 14 | class nvhost_ctrl_gpu final : public nvdevice { | 14 | class nvhost_ctrl_gpu final : public nvdevice { |
| 15 | public: | 15 | public: |
| 16 | nvhost_ctrl_gpu() = default; | 16 | nvhost_ctrl_gpu(); |
| 17 | ~nvhost_ctrl_gpu() override = default; | 17 | ~nvhost_ctrl_gpu() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | 20 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 4cdf7f613..874d5e1c3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -8,11 +8,15 @@ | |||
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 9 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 10 | #include "core/memory.h" | 10 | #include "core/memory.h" |
| 11 | #include "video_core/command_processor.h" | ||
| 11 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 12 | #include "video_core/memory_manager.h" | 13 | #include "video_core/memory_manager.h" |
| 13 | 14 | ||
| 14 | namespace Service::Nvidia::Devices { | 15 | namespace Service::Nvidia::Devices { |
| 15 | 16 | ||
| 17 | nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | ||
| 18 | nvhost_gpu::~nvhost_gpu() = default; | ||
| 19 | |||
| 16 | u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 20 | u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 17 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 21 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 18 | command.raw, input.size(), output.size()); | 22 | command.raw, input.size(), output.size()); |
| @@ -134,17 +138,16 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp | |||
| 134 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", | 138 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", |
| 135 | params.address, params.num_entries, params.flags); | 139 | params.address, params.num_entries, params.flags); |
| 136 | 140 | ||
| 137 | ASSERT_MSG(input.size() == | 141 | ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + |
| 138 | sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(IoctlGpfifoEntry), | 142 | params.num_entries * sizeof(Tegra::CommandListHeader), |
| 139 | "Incorrect input size"); | 143 | "Incorrect input size"); |
| 140 | 144 | ||
| 141 | std::vector<IoctlGpfifoEntry> entries(params.num_entries); | 145 | std::vector<Tegra::CommandListHeader> entries(params.num_entries); |
| 142 | std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], | 146 | std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], |
| 143 | params.num_entries * sizeof(IoctlGpfifoEntry)); | 147 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 144 | for (auto entry : entries) { | 148 | |
| 145 | Tegra::GPUVAddr va_addr = entry.Address(); | 149 | Core::System::GetInstance().GPU().ProcessCommandLists(entries); |
| 146 | Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz); | 150 | |
| 147 | } | ||
| 148 | params.fence_out.id = 0; | 151 | params.fence_out.id = 0; |
| 149 | params.fence_out.value = 0; | 152 | params.fence_out.value = 0; |
| 150 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); | 153 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); |
| @@ -160,14 +163,12 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 160 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", | 163 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", |
| 161 | params.address, params.num_entries, params.flags); | 164 | params.address, params.num_entries, params.flags); |
| 162 | 165 | ||
| 163 | std::vector<IoctlGpfifoEntry> entries(params.num_entries); | 166 | std::vector<Tegra::CommandListHeader> entries(params.num_entries); |
| 164 | Memory::ReadBlock(params.address, entries.data(), | 167 | Memory::ReadBlock(params.address, entries.data(), |
| 165 | params.num_entries * sizeof(IoctlGpfifoEntry)); | 168 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 169 | |||
| 170 | Core::System::GetInstance().GPU().ProcessCommandLists(entries); | ||
| 166 | 171 | ||
| 167 | for (auto entry : entries) { | ||
| 168 | Tegra::GPUVAddr va_addr = entry.Address(); | ||
| 169 | Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz); | ||
| 170 | } | ||
| 171 | params.fence_out.id = 0; | 172 | params.fence_out.id = 0; |
| 172 | params.fence_out.value = 0; | 173 | params.fence_out.value = 0; |
| 173 | std::memcpy(output.data(), ¶ms, output.size()); | 174 | std::memcpy(output.data(), ¶ms, output.size()); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 03b7356d0..62beb5c0c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 12 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 13 | #include "video_core/memory_manager.h" | ||
| 14 | 13 | ||
| 15 | namespace Service::Nvidia::Devices { | 14 | namespace Service::Nvidia::Devices { |
| 16 | 15 | ||
| @@ -21,8 +20,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); | |||
| 21 | 20 | ||
| 22 | class nvhost_gpu final : public nvdevice { | 21 | class nvhost_gpu final : public nvdevice { |
| 23 | public: | 22 | public: |
| 24 | explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | 23 | explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev); |
| 25 | ~nvhost_gpu() override = default; | 24 | ~nvhost_gpu() override; |
| 26 | 25 | ||
| 27 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 26 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 28 | 27 | ||
| @@ -151,22 +150,6 @@ private: | |||
| 151 | }; | 150 | }; |
| 152 | static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); | 151 | static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); |
| 153 | 152 | ||
| 154 | struct IoctlGpfifoEntry { | ||
| 155 | u32_le entry0; // gpu_va_lo | ||
| 156 | union { | ||
| 157 | u32_le entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F) | ||
| 158 | BitField<0, 8, u32_le> gpu_va_hi; | ||
| 159 | BitField<8, 2, u32_le> unk1; | ||
| 160 | BitField<10, 21, u32_le> sz; | ||
| 161 | BitField<31, 1, u32_le> unk2; | ||
| 162 | }; | ||
| 163 | |||
| 164 | Tegra::GPUVAddr Address() const { | ||
| 165 | return (static_cast<Tegra::GPUVAddr>(gpu_va_hi) << 32) | entry0; | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size"); | ||
| 169 | |||
| 170 | struct IoctlSubmitGpfifo { | 153 | struct IoctlSubmitGpfifo { |
| 171 | u64_le address; // pointer to gpfifo entry structs | 154 | u64_le address; // pointer to gpfifo entry structs |
| 172 | u32_le num_entries; // number of fence objects being submitted | 155 | u32_le num_entries; // number of fence objects being submitted |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 364619e67..46dbbc37c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -10,6 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::Nvidia::Devices { | 11 | namespace Service::Nvidia::Devices { |
| 12 | 12 | ||
| 13 | nvhost_nvdec::nvhost_nvdec() = default; | ||
| 14 | nvhost_nvdec::~nvhost_nvdec() = default; | ||
| 15 | |||
| 13 | u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 16 | u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 14 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 17 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 15 | command.raw, input.size(), output.size()); | 18 | command.raw, input.size(), output.size()); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 6ad74421b..0e7b284f8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h | |||
| @@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices { | |||
| 13 | 13 | ||
| 14 | class nvhost_nvdec final : public nvdevice { | 14 | class nvhost_nvdec final : public nvdevice { |
| 15 | public: | 15 | public: |
| 16 | nvhost_nvdec() = default; | 16 | nvhost_nvdec(); |
| 17 | ~nvhost_nvdec() override = default; | 17 | ~nvhost_nvdec() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | 20 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 51f01077b..c67f934f6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp | |||
| @@ -10,6 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::Nvidia::Devices { | 11 | namespace Service::Nvidia::Devices { |
| 12 | 12 | ||
| 13 | nvhost_nvjpg::nvhost_nvjpg() = default; | ||
| 14 | nvhost_nvjpg::~nvhost_nvjpg() = default; | ||
| 15 | |||
| 13 | u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 16 | u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 14 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 17 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 15 | command.raw, input.size(), output.size()); | 18 | command.raw, input.size(), output.size()); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 2b0eb43ee..89fd5e95e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h | |||
| @@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices { | |||
| 13 | 13 | ||
| 14 | class nvhost_nvjpg final : public nvdevice { | 14 | class nvhost_nvjpg final : public nvdevice { |
| 15 | public: | 15 | public: |
| 16 | nvhost_nvjpg() = default; | 16 | nvhost_nvjpg(); |
| 17 | ~nvhost_nvjpg() override = default; | 17 | ~nvhost_nvjpg() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | 20 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index fcb488d50..727b9fee4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -10,6 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::Nvidia::Devices { | 11 | namespace Service::Nvidia::Devices { |
| 12 | 12 | ||
| 13 | nvhost_vic::nvhost_vic() = default; | ||
| 14 | nvhost_vic::~nvhost_vic() = default; | ||
| 15 | |||
| 13 | u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | 16 | u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 14 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 17 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 15 | command.raw, input.size(), output.size()); | 18 | command.raw, input.size(), output.size()); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index c7d681e52..fc24c3f9c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices { | |||
| 13 | 13 | ||
| 14 | class nvhost_vic final : public nvdevice { | 14 | class nvhost_vic final : public nvdevice { |
| 15 | public: | 15 | public: |
| 16 | nvhost_vic() = default; | 16 | nvhost_vic(); |
| 17 | ~nvhost_vic() override = default; | 17 | ~nvhost_vic() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | 20 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index e9305bfb3..a2287cc1b 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -11,6 +11,9 @@ | |||
| 11 | 11 | ||
| 12 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 13 | 13 | ||
| 14 | nvmap::nvmap() = default; | ||
| 15 | nvmap::~nvmap() = default; | ||
| 16 | |||
| 14 | VAddr nvmap::GetObjectAddress(u32 handle) const { | 17 | VAddr nvmap::GetObjectAddress(u32 handle) const { |
| 15 | auto object = GetObject(handle); | 18 | auto object = GetObject(handle); |
| 16 | ASSERT(object); | 19 | ASSERT(object); |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index f2eec6409..396230c19 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -16,8 +16,8 @@ namespace Service::Nvidia::Devices { | |||
| 16 | 16 | ||
| 17 | class nvmap final : public nvdevice { | 17 | class nvmap final : public nvdevice { |
| 18 | public: | 18 | public: |
| 19 | nvmap() = default; | 19 | nvmap(); |
| 20 | ~nvmap() override = default; | 20 | ~nvmap() override; |
| 21 | 21 | ||
| 22 | /// Returns the allocated address of an nvmap object given its handle. | 22 | /// Returns the allocated address of an nvmap object given its handle. |
| 23 | VAddr GetObjectAddress(u32 handle) const; | 23 | VAddr GetObjectAddress(u32 handle) const; |
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 634ab9196..ac3859353 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp | |||
| @@ -112,4 +112,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) | |||
| 112 | query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event"); | 112 | query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event"); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | NVDRV::~NVDRV() = default; | ||
| 116 | |||
| 115 | } // namespace Service::Nvidia | 117 | } // namespace Service::Nvidia |
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 1c3529bb6..d340893c2 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h | |||
| @@ -14,7 +14,7 @@ namespace Service::Nvidia { | |||
| 14 | class NVDRV final : public ServiceFramework<NVDRV> { | 14 | class NVDRV final : public ServiceFramework<NVDRV> { |
| 15 | public: | 15 | public: |
| 16 | NVDRV(std::shared_ptr<Module> nvdrv, const char* name); | 16 | NVDRV(std::shared_ptr<Module> nvdrv, const char* name); |
| 17 | ~NVDRV() = default; | 17 | ~NVDRV(); |
| 18 | 18 | ||
| 19 | private: | 19 | private: |
| 20 | void Open(Kernel::HLERequestContext& ctx); | 20 | void Open(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 2de39822f..6e4b8f2c6 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -45,6 +45,8 @@ Module::Module() { | |||
| 45 | devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); | 45 | devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | Module::~Module() = default; | ||
| 49 | |||
| 48 | u32 Module::Open(const std::string& device_name) { | 50 | u32 Module::Open(const std::string& device_name) { |
| 49 | ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}", | 51 | ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}", |
| 50 | device_name); | 52 | device_name); |
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 99eb1128a..53564f696 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h | |||
| @@ -30,7 +30,7 @@ static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); | |||
| 30 | class Module final { | 30 | class Module final { |
| 31 | public: | 31 | public: |
| 32 | Module(); | 32 | Module(); |
| 33 | ~Module() = default; | 33 | ~Module(); |
| 34 | 34 | ||
| 35 | /// Returns a pointer to one of the available devices, identified by its name. | 35 | /// Returns a pointer to one of the available devices, identified by its name. |
| 36 | template <typename T> | 36 | template <typename T> |
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp index 0e8e21bad..b7b8b7a1b 100644 --- a/src/core/hle/service/nvdrv/nvmemp.cpp +++ b/src/core/hle/service/nvdrv/nvmemp.cpp | |||
| @@ -16,6 +16,8 @@ NVMEMP::NVMEMP() : ServiceFramework("nvmemp") { | |||
| 16 | RegisterHandlers(functions); | 16 | RegisterHandlers(functions); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | NVMEMP::~NVMEMP() = default; | ||
| 20 | |||
| 19 | void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) { | 21 | void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) { |
| 20 | UNIMPLEMENTED(); | 22 | UNIMPLEMENTED(); |
| 21 | } | 23 | } |
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index dfdcabf4a..5a4dfc1f9 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h | |||
| @@ -11,7 +11,7 @@ namespace Service::Nvidia { | |||
| 11 | class NVMEMP final : public ServiceFramework<NVMEMP> { | 11 | class NVMEMP final : public ServiceFramework<NVMEMP> { |
| 12 | public: | 12 | public: |
| 13 | NVMEMP(); | 13 | NVMEMP(); |
| 14 | ~NVMEMP() = default; | 14 | ~NVMEMP(); |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
| 17 | void Cmd0(Kernel::HLERequestContext& ctx); | 17 | void Cmd0(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 8d8962276..fd98d541d 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -9,8 +9,7 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/service/nvflinger/buffer_queue.h" | 10 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 11 | 11 | ||
| 12 | namespace Service { | 12 | namespace Service::NVFlinger { |
| 13 | namespace NVFlinger { | ||
| 14 | 13 | ||
| 15 | BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { | 14 | BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { |
| 16 | auto& kernel = Core::System::GetInstance().Kernel(); | 15 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -18,6 +17,8 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { | |||
| 18 | Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); | 17 | Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); |
| 19 | } | 18 | } |
| 20 | 19 | ||
| 20 | BufferQueue::~BufferQueue() = default; | ||
| 21 | |||
| 21 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | 22 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { |
| 22 | Buffer buffer{}; | 23 | Buffer buffer{}; |
| 23 | buffer.slot = slot; | 24 | buffer.slot = slot; |
| @@ -102,5 +103,4 @@ u32 BufferQueue::Query(QueryType type) { | |||
| 102 | return 0; | 103 | return 0; |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | } // namespace NVFlinger | 106 | } // namespace Service::NVFlinger |
| 106 | } // namespace Service | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index db2e17c0c..50b767732 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h | |||
| @@ -15,8 +15,7 @@ namespace CoreTiming { | |||
| 15 | struct EventType; | 15 | struct EventType; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Service { | 18 | namespace Service::NVFlinger { |
| 19 | namespace NVFlinger { | ||
| 20 | 19 | ||
| 21 | struct IGBPBuffer { | 20 | struct IGBPBuffer { |
| 22 | u32_le magic; | 21 | u32_le magic; |
| @@ -46,7 +45,7 @@ public: | |||
| 46 | }; | 45 | }; |
| 47 | 46 | ||
| 48 | BufferQueue(u32 id, u64 layer_id); | 47 | BufferQueue(u32 id, u64 layer_id); |
| 49 | ~BufferQueue() = default; | 48 | ~BufferQueue(); |
| 50 | 49 | ||
| 51 | enum class BufferTransformFlags : u32 { | 50 | enum class BufferTransformFlags : u32 { |
| 52 | /// No transform flags are set | 51 | /// No transform flags are set |
| @@ -98,5 +97,4 @@ private: | |||
| 98 | Kernel::SharedPtr<Kernel::Event> buffer_wait_event; | 97 | Kernel::SharedPtr<Kernel::Event> buffer_wait_event; |
| 99 | }; | 98 | }; |
| 100 | 99 | ||
| 101 | } // namespace NVFlinger | 100 | } // namespace Service::NVFlinger |
| 102 | } // namespace Service | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 06040da6f..d47b6f659 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | namespace Service::NVFlinger { | 24 | namespace Service::NVFlinger { |
| 25 | 25 | ||
| 26 | constexpr size_t SCREEN_REFRESH_RATE = 60; | 26 | constexpr std::size_t SCREEN_REFRESH_RATE = 60; |
| 27 | constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); | 27 | constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); |
| 28 | 28 | ||
| 29 | NVFlinger::NVFlinger() { | 29 | NVFlinger::NVFlinger() { |
| @@ -160,10 +160,13 @@ void NVFlinger::Compose() { | |||
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} | 162 | Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} |
| 163 | Layer::~Layer() = default; | ||
| 163 | 164 | ||
| 164 | Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { | 165 | Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { |
| 165 | auto& kernel = Core::System::GetInstance().Kernel(); | 166 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 166 | vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event"); | 167 | vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event"); |
| 167 | } | 168 | } |
| 168 | 169 | ||
| 170 | Display::~Display() = default; | ||
| 171 | |||
| 169 | } // namespace Service::NVFlinger | 172 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index f7112949f..3dc69e69b 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -26,7 +26,7 @@ class BufferQueue; | |||
| 26 | 26 | ||
| 27 | struct Layer { | 27 | struct Layer { |
| 28 | Layer(u64 id, std::shared_ptr<BufferQueue> queue); | 28 | Layer(u64 id, std::shared_ptr<BufferQueue> queue); |
| 29 | ~Layer() = default; | 29 | ~Layer(); |
| 30 | 30 | ||
| 31 | u64 id; | 31 | u64 id; |
| 32 | std::shared_ptr<BufferQueue> buffer_queue; | 32 | std::shared_ptr<BufferQueue> buffer_queue; |
| @@ -34,7 +34,7 @@ struct Layer { | |||
| 34 | 34 | ||
| 35 | struct Display { | 35 | struct Display { |
| 36 | Display(u64 id, std::string name); | 36 | Display(u64 id, std::string name); |
| 37 | ~Display() = default; | 37 | ~Display(); |
| 38 | 38 | ||
| 39 | u64 id; | 39 | u64 id; |
| 40 | std::string name; | 40 | std::string name; |
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index 6cc3b1992..4fd185f69 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp | |||
| @@ -142,6 +142,8 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext | |||
| 142 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 142 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 143 | : ServiceFramework(name), module(std::move(module)) {} | 143 | : ServiceFramework(name), module(std::move(module)) {} |
| 144 | 144 | ||
| 145 | Module::Interface::~Interface() = default; | ||
| 146 | |||
| 145 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 147 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 146 | auto module = std::make_shared<Module>(); | 148 | auto module = std::make_shared<Module>(); |
| 147 | std::make_shared<PCTL>(module, "pctl")->InstallAsService(service_manager); | 149 | std::make_shared<PCTL>(module, "pctl")->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/module.h index e7d492760..3e449110d 100644 --- a/src/core/hle/service/pctl/module.h +++ b/src/core/hle/service/pctl/module.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Interface : public ServiceFramework<Interface> { | 13 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 14 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | void CreateService(Kernel::HLERequestContext& ctx); | 18 | void CreateService(Kernel::HLERequestContext& ctx); |
| 18 | void CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx); | 19 | void CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index de2741d66..af9d1433a 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp | |||
| @@ -14,4 +14,6 @@ PCTL::PCTL(std::shared_ptr<Module> module, const char* name) | |||
| 14 | }; | 14 | }; |
| 15 | RegisterHandlers(functions); | 15 | RegisterHandlers(functions); |
| 16 | } | 16 | } |
| 17 | |||
| 18 | PCTL::~PCTL() = default; | ||
| 17 | } // namespace Service::PCTL | 19 | } // namespace Service::PCTL |
diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 8ddf69128..c33ea80b6 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h | |||
| @@ -11,6 +11,7 @@ namespace Service::PCTL { | |||
| 11 | class PCTL final : public Module::Interface { | 11 | class PCTL final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit PCTL(std::shared_ptr<Module> module, const char* name); | 13 | explicit PCTL(std::shared_ptr<Module> module, const char* name); |
| 14 | ~PCTL() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::PCTL | 17 | } // namespace Service::PCTL |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 3c43b8d8c..6a9eccfb5 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -1,36 +1,47 @@ | |||
| 1 | #include <cinttypes> | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 2 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 3 | #include "core/hle/ipc_helpers.h" | 6 | #include "core/hle/ipc_helpers.h" |
| 4 | #include "core/hle/kernel/event.h" | ||
| 5 | #include "core/hle/service/prepo/prepo.h" | 7 | #include "core/hle/service/prepo/prepo.h" |
| 8 | #include "core/hle/service/service.h" | ||
| 6 | 9 | ||
| 7 | namespace Service::PlayReport { | 10 | namespace Service::PlayReport { |
| 8 | PlayReport::PlayReport(const char* name) : ServiceFramework(name) { | ||
| 9 | static const FunctionInfo functions[] = { | ||
| 10 | {10100, nullptr, "SaveReport"}, | ||
| 11 | {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, | ||
| 12 | {10200, nullptr, "RequestImmediateTransmission"}, | ||
| 13 | {10300, nullptr, "GetTransmissionStatus"}, | ||
| 14 | {20100, nullptr, "SaveSystemReport"}, | ||
| 15 | {20200, nullptr, "SetOperationMode"}, | ||
| 16 | {20101, nullptr, "SaveSystemReportWithUser"}, | ||
| 17 | {30100, nullptr, "ClearStorage"}, | ||
| 18 | {40100, nullptr, "IsUserAgreementCheckEnabled"}, | ||
| 19 | {40101, nullptr, "SetUserAgreementCheckEnabled"}, | ||
| 20 | {90100, nullptr, "GetStorageUsage"}, | ||
| 21 | {90200, nullptr, "GetStatistics"}, | ||
| 22 | {90201, nullptr, "GetThroughputHistory"}, | ||
| 23 | {90300, nullptr, "GetLastUploadError"}, | ||
| 24 | }; | ||
| 25 | RegisterHandlers(functions); | ||
| 26 | }; | ||
| 27 | 11 | ||
| 28 | void PlayReport::SaveReportWithUser(Kernel::HLERequestContext& ctx) { | 12 | class PlayReport final : public ServiceFramework<PlayReport> { |
| 29 | // TODO(ogniK): Do we want to add play report? | 13 | public: |
| 30 | LOG_WARNING(Service_PREPO, "(STUBBED) called"); | 14 | explicit PlayReport(const char* name) : ServiceFramework{name} { |
| 15 | // clang-format off | ||
| 16 | static const FunctionInfo functions[] = { | ||
| 17 | {10100, nullptr, "SaveReport"}, | ||
| 18 | {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, | ||
| 19 | {10200, nullptr, "RequestImmediateTransmission"}, | ||
| 20 | {10300, nullptr, "GetTransmissionStatus"}, | ||
| 21 | {20100, nullptr, "SaveSystemReport"}, | ||
| 22 | {20200, nullptr, "SetOperationMode"}, | ||
| 23 | {20101, nullptr, "SaveSystemReportWithUser"}, | ||
| 24 | {30100, nullptr, "ClearStorage"}, | ||
| 25 | {40100, nullptr, "IsUserAgreementCheckEnabled"}, | ||
| 26 | {40101, nullptr, "SetUserAgreementCheckEnabled"}, | ||
| 27 | {90100, nullptr, "GetStorageUsage"}, | ||
| 28 | {90200, nullptr, "GetStatistics"}, | ||
| 29 | {90201, nullptr, "GetThroughputHistory"}, | ||
| 30 | {90300, nullptr, "GetLastUploadError"}, | ||
| 31 | }; | ||
| 32 | // clang-format on | ||
| 33 | |||
| 34 | RegisterHandlers(functions); | ||
| 35 | } | ||
| 36 | |||
| 37 | private: | ||
| 38 | void SaveReportWithUser(Kernel::HLERequestContext& ctx) { | ||
| 39 | // TODO(ogniK): Do we want to add play report? | ||
| 40 | LOG_WARNING(Service_PREPO, "(STUBBED) called"); | ||
| 31 | 41 | ||
| 32 | IPC::ResponseBuilder rb{ctx, 2}; | 42 | IPC::ResponseBuilder rb{ctx, 2}; |
| 33 | rb.Push(RESULT_SUCCESS); | 43 | rb.Push(RESULT_SUCCESS); |
| 44 | } | ||
| 34 | }; | 45 | }; |
| 35 | 46 | ||
| 36 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 47 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h index f5a6aba6d..0e7b01331 100644 --- a/src/core/hle/service/prepo/prepo.h +++ b/src/core/hle/service/prepo/prepo.h | |||
| @@ -4,22 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | namespace Service::SM { |
| 8 | #include <string> | 8 | class ServiceManager; |
| 9 | #include "core/hle/kernel/event.h" | 9 | } |
| 10 | #include "core/hle/service/service.h" | ||
| 11 | 10 | ||
| 12 | namespace Service::PlayReport { | 11 | namespace Service::PlayReport { |
| 13 | 12 | ||
| 14 | class PlayReport final : public ServiceFramework<PlayReport> { | ||
| 15 | public: | ||
| 16 | explicit PlayReport(const char* name); | ||
| 17 | ~PlayReport() = default; | ||
| 18 | |||
| 19 | private: | ||
| 20 | void SaveReportWithUser(Kernel::HLERequestContext& ctx); | ||
| 21 | }; | ||
| 22 | |||
| 23 | void InstallInterfaces(SM::ServiceManager& service_manager); | 13 | void InstallInterfaces(SM::ServiceManager& service_manager); |
| 24 | 14 | ||
| 25 | } // namespace Service::PlayReport | 15 | } // namespace Service::PlayReport |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 8fb907072..62f049660 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hle/ipc_helpers.h" | 12 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/client_port.h" | 13 | #include "core/hle/kernel/client_port.h" |
| 14 | #include "core/hle/kernel/handle_table.h" | 14 | #include "core/hle/kernel/handle_table.h" |
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 15 | #include "core/hle/kernel/process.h" | 16 | #include "core/hle/kernel/process.h" |
| 16 | #include "core/hle/kernel/server_port.h" | 17 | #include "core/hle/kernel/server_port.h" |
| 17 | #include "core/hle/kernel/thread.h" | 18 | #include "core/hle/kernel/thread.h" |
| @@ -49,6 +50,7 @@ | |||
| 49 | #include "core/hle/service/nim/nim.h" | 50 | #include "core/hle/service/nim/nim.h" |
| 50 | #include "core/hle/service/ns/ns.h" | 51 | #include "core/hle/service/ns/ns.h" |
| 51 | #include "core/hle/service/nvdrv/nvdrv.h" | 52 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 53 | #include "core/hle/service/nvflinger/nvflinger.h" | ||
| 52 | #include "core/hle/service/pcie/pcie.h" | 54 | #include "core/hle/service/pcie/pcie.h" |
| 53 | #include "core/hle/service/pctl/pctl.h" | 55 | #include "core/hle/service/pctl/pctl.h" |
| 54 | #include "core/hle/service/pcv/pcv.h" | 56 | #include "core/hle/service/pcv/pcv.h" |
| @@ -57,7 +59,6 @@ | |||
| 57 | #include "core/hle/service/psc/psc.h" | 59 | #include "core/hle/service/psc/psc.h" |
| 58 | #include "core/hle/service/service.h" | 60 | #include "core/hle/service/service.h" |
| 59 | #include "core/hle/service/set/settings.h" | 61 | #include "core/hle/service/set/settings.h" |
| 60 | #include "core/hle/service/sm/controller.h" | ||
| 61 | #include "core/hle/service/sm/sm.h" | 62 | #include "core/hle/service/sm/sm.h" |
| 62 | #include "core/hle/service/sockets/sockets.h" | 63 | #include "core/hle/service/sockets/sockets.h" |
| 63 | #include "core/hle/service/spl/module.h" | 64 | #include "core/hle/service/spl/module.h" |
| @@ -73,8 +74,6 @@ using Kernel::SharedPtr; | |||
| 73 | 74 | ||
| 74 | namespace Service { | 75 | namespace Service { |
| 75 | 76 | ||
| 76 | std::unordered_map<std::string, SharedPtr<ClientPort>> g_kernel_named_ports; | ||
| 77 | |||
| 78 | /** | 77 | /** |
| 79 | * Creates a function string for logging, complete with the name (or header code, depending | 78 | * Creates a function string for logging, complete with the name (or header code, depending |
| 80 | * on what's passed in) the port name, and all the cmd_buff arguments. | 79 | * on what's passed in) the port name, and all the cmd_buff arguments. |
| @@ -114,7 +113,7 @@ void ServiceFrameworkBase::InstallAsNamedPort() { | |||
| 114 | std::tie(server_port, client_port) = | 113 | std::tie(server_port, client_port) = |
| 115 | ServerPort::CreatePortPair(kernel, max_sessions, service_name); | 114 | ServerPort::CreatePortPair(kernel, max_sessions, service_name); |
| 116 | server_port->SetHleHandler(shared_from_this()); | 115 | server_port->SetHleHandler(shared_from_this()); |
| 117 | AddNamedPort(service_name, std::move(client_port)); | 116 | kernel.AddNamedPort(service_name, std::move(client_port)); |
| 118 | } | 117 | } |
| 119 | 118 | ||
| 120 | Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { | 119 | Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { |
| @@ -130,9 +129,9 @@ Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { | |||
| 130 | return client_port; | 129 | return client_port; |
| 131 | } | 130 | } |
| 132 | 131 | ||
| 133 | void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) { | 132 | void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { |
| 134 | handlers.reserve(handlers.size() + n); | 133 | handlers.reserve(handlers.size() + n); |
| 135 | for (size_t i = 0; i < n; ++i) { | 134 | for (std::size_t i = 0; i < n; ++i) { |
| 136 | // Usually this array is sorted by id already, so hint to insert at the end | 135 | // Usually this array is sorted by id already, so hint to insert at the end |
| 137 | handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); | 136 | handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); |
| 138 | } | 137 | } |
| @@ -197,11 +196,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co | |||
| 197 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 196 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 198 | // Module interface | 197 | // Module interface |
| 199 | 198 | ||
| 200 | // TODO(yuriks): Move to kernel | ||
| 201 | void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { | ||
| 202 | g_kernel_named_ports.emplace(std::move(name), std::move(port)); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Initialize ServiceManager | 199 | /// Initialize ServiceManager |
| 206 | void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { | 200 | void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { |
| 207 | // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it | 201 | // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it |
| @@ -264,7 +258,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys | |||
| 264 | 258 | ||
| 265 | /// Shutdown ServiceManager | 259 | /// Shutdown ServiceManager |
| 266 | void Shutdown() { | 260 | void Shutdown() { |
| 267 | g_kernel_named_ports.clear(); | ||
| 268 | LOG_DEBUG(Service, "shutdown OK"); | 261 | LOG_DEBUG(Service, "shutdown OK"); |
| 269 | } | 262 | } |
| 270 | } // namespace Service | 263 | } // namespace Service |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index cd9c74f3d..2fc57a82e 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <unordered_map> | ||
| 10 | #include <boost/container/flat_map.hpp> | 9 | #include <boost/container/flat_map.hpp> |
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/hle_ipc.h" | 11 | #include "core/hle/kernel/hle_ipc.h" |
| @@ -89,7 +88,7 @@ private: | |||
| 89 | ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); | 88 | ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); |
| 90 | ~ServiceFrameworkBase(); | 89 | ~ServiceFrameworkBase(); |
| 91 | 90 | ||
| 92 | void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); | 91 | void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); |
| 93 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); | 92 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); |
| 94 | 93 | ||
| 95 | /// Identifier string used to connect to the service. | 94 | /// Identifier string used to connect to the service. |
| @@ -153,7 +152,7 @@ protected: | |||
| 153 | : ServiceFrameworkBase(service_name, max_sessions, Invoker) {} | 152 | : ServiceFrameworkBase(service_name, max_sessions, Invoker) {} |
| 154 | 153 | ||
| 155 | /// Registers handlers in the service. | 154 | /// Registers handlers in the service. |
| 156 | template <size_t N> | 155 | template <std::size_t N> |
| 157 | void RegisterHandlers(const FunctionInfo (&functions)[N]) { | 156 | void RegisterHandlers(const FunctionInfo (&functions)[N]) { |
| 158 | RegisterHandlers(functions, N); | 157 | RegisterHandlers(functions, N); |
| 159 | } | 158 | } |
| @@ -162,7 +161,7 @@ protected: | |||
| 162 | * Registers handlers in the service. Usually prefer using the other RegisterHandlers | 161 | * Registers handlers in the service. Usually prefer using the other RegisterHandlers |
| 163 | * overload in order to avoid needing to specify the array size. | 162 | * overload in order to avoid needing to specify the array size. |
| 164 | */ | 163 | */ |
| 165 | void RegisterHandlers(const FunctionInfo* functions, size_t n) { | 164 | void RegisterHandlers(const FunctionInfo* functions, std::size_t n) { |
| 166 | RegisterHandlersBase(functions, n); | 165 | RegisterHandlersBase(functions, n); |
| 167 | } | 166 | } |
| 168 | 167 | ||
| @@ -187,10 +186,4 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, | |||
| 187 | /// Shutdown ServiceManager | 186 | /// Shutdown ServiceManager |
| 188 | void Shutdown(); | 187 | void Shutdown(); |
| 189 | 188 | ||
| 190 | /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. | ||
| 191 | extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; | ||
| 192 | |||
| 193 | /// Adds a port to the named port table | ||
| 194 | void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port); | ||
| 195 | |||
| 196 | } // namespace Service | 189 | } // namespace Service |
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 92b0640e8..9e5af7839 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -32,21 +32,21 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ | |||
| 32 | LanguageCode::ZH_HANT, | 32 | LanguageCode::ZH_HANT, |
| 33 | }}; | 33 | }}; |
| 34 | 34 | ||
| 35 | constexpr size_t pre4_0_0_max_entries = 0xF; | 35 | constexpr std::size_t pre4_0_0_max_entries = 0xF; |
| 36 | constexpr size_t post4_0_0_max_entries = 0x40; | 36 | constexpr std::size_t post4_0_0_max_entries = 0x40; |
| 37 | 37 | ||
| 38 | LanguageCode GetLanguageCodeFromIndex(size_t index) { | 38 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { |
| 39 | return available_language_codes.at(index); | 39 | return available_language_codes.at(index); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | template <size_t size> | 42 | template <std::size_t size> |
| 43 | static std::array<LanguageCode, size> MakeLanguageCodeSubset() { | 43 | static std::array<LanguageCode, size> MakeLanguageCodeSubset() { |
| 44 | std::array<LanguageCode, size> arr; | 44 | std::array<LanguageCode, size> arr; |
| 45 | std::copy_n(available_language_codes.begin(), size, arr.begin()); | 45 | std::copy_n(available_language_codes.begin(), size, arr.begin()); |
| 46 | return arr; | 46 | return arr; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, size_t max_size) { | 49 | static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) { |
| 50 | IPC::ResponseBuilder rb{ctx, 3}; | 50 | IPC::ResponseBuilder rb{ctx, 3}; |
| 51 | rb.Push(RESULT_SUCCESS); | 51 | rb.Push(RESULT_SUCCESS); |
| 52 | if (available_language_codes.size() > max_size) | 52 | if (available_language_codes.size() > max_size) |
| @@ -112,4 +112,6 @@ SET::SET() : ServiceFramework("set") { | |||
| 112 | RegisterHandlers(functions); | 112 | RegisterHandlers(functions); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | SET::~SET() = default; | ||
| 116 | |||
| 115 | } // namespace Service::Set | 117 | } // namespace Service::Set |
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 669e740b7..266f13e46 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h | |||
| @@ -28,12 +28,12 @@ enum class LanguageCode : u64 { | |||
| 28 | ZH_HANS = 0x00736E61482D687A, | 28 | ZH_HANS = 0x00736E61482D687A, |
| 29 | ZH_HANT = 0x00746E61482D687A, | 29 | ZH_HANT = 0x00746E61482D687A, |
| 30 | }; | 30 | }; |
| 31 | LanguageCode GetLanguageCodeFromIndex(size_t idx); | 31 | LanguageCode GetLanguageCodeFromIndex(std::size_t idx); |
| 32 | 32 | ||
| 33 | class SET final : public ServiceFramework<SET> { | 33 | class SET final : public ServiceFramework<SET> { |
| 34 | public: | 34 | public: |
| 35 | explicit SET(); | 35 | explicit SET(); |
| 36 | ~SET() = default; | 36 | ~SET() override; |
| 37 | 37 | ||
| 38 | private: | 38 | private: |
| 39 | void GetLanguageCode(Kernel::HLERequestContext& ctx); | 39 | void GetLanguageCode(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 7066ef725..5af356d10 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp | |||
| @@ -44,4 +44,6 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { | |||
| 44 | RegisterHandlers(functions); | 44 | RegisterHandlers(functions); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | SET_CAL::~SET_CAL() = default; | ||
| 48 | |||
| 47 | } // namespace Service::Set | 49 | } // namespace Service::Set |
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h index bb50336aa..583036eac 100644 --- a/src/core/hle/service/set/set_cal.h +++ b/src/core/hle/service/set/set_cal.h | |||
| @@ -11,7 +11,7 @@ namespace Service::Set { | |||
| 11 | class SET_CAL final : public ServiceFramework<SET_CAL> { | 11 | class SET_CAL final : public ServiceFramework<SET_CAL> { |
| 12 | public: | 12 | public: |
| 13 | explicit SET_CAL(); | 13 | explicit SET_CAL(); |
| 14 | ~SET_CAL() = default; | 14 | ~SET_CAL(); |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | } // namespace Service::Set | 17 | } // namespace Service::Set |
diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp index c9f938716..cac6af86d 100644 --- a/src/core/hle/service/set/set_fd.cpp +++ b/src/core/hle/service/set/set_fd.cpp | |||
| @@ -20,4 +20,6 @@ SET_FD::SET_FD() : ServiceFramework("set:fd") { | |||
| 20 | RegisterHandlers(functions); | 20 | RegisterHandlers(functions); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | SET_FD::~SET_FD() = default; | ||
| 24 | |||
| 23 | } // namespace Service::Set | 25 | } // namespace Service::Set |
diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h index dbd850bc7..216e65f1f 100644 --- a/src/core/hle/service/set/set_fd.h +++ b/src/core/hle/service/set/set_fd.h | |||
| @@ -11,7 +11,7 @@ namespace Service::Set { | |||
| 11 | class SET_FD final : public ServiceFramework<SET_FD> { | 11 | class SET_FD final : public ServiceFramework<SET_FD> { |
| 12 | public: | 12 | public: |
| 13 | explicit SET_FD(); | 13 | explicit SET_FD(); |
| 14 | ~SET_FD() = default; | 14 | ~SET_FD() override; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | } // namespace Service::Set | 17 | } // namespace Service::Set |
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index 1cef73216..cdf328a26 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp | |||
| @@ -57,4 +57,6 @@ Controller::Controller() : ServiceFramework("IpcController") { | |||
| 57 | RegisterHandlers(functions); | 57 | RegisterHandlers(functions); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | Controller::~Controller() = default; | ||
| 61 | |||
| 60 | } // namespace Service::SM | 62 | } // namespace Service::SM |
diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/controller.h index a4de52cd2..dc66c9e37 100644 --- a/src/core/hle/service/sm/controller.h +++ b/src/core/hle/service/sm/controller.h | |||
| @@ -11,7 +11,7 @@ namespace Service::SM { | |||
| 11 | class Controller final : public ServiceFramework<Controller> { | 11 | class Controller final : public ServiceFramework<Controller> { |
| 12 | public: | 12 | public: |
| 13 | Controller(); | 13 | Controller(); |
| 14 | ~Controller() = default; | 14 | ~Controller() override; |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
| 17 | void ConvertSessionToDomain(Kernel::HLERequestContext& ctx); | 17 | void ConvertSessionToDomain(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index b240d7eed..464e79d01 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -15,6 +15,11 @@ | |||
| 15 | 15 | ||
| 16 | namespace Service::SM { | 16 | namespace Service::SM { |
| 17 | 17 | ||
| 18 | constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); | ||
| 19 | constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); | ||
| 20 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); | ||
| 21 | |||
| 22 | ServiceManager::ServiceManager() = default; | ||
| 18 | ServiceManager::~ServiceManager() = default; | 23 | ServiceManager::~ServiceManager() = default; |
| 19 | 24 | ||
| 20 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | 25 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { |
| @@ -23,10 +28,10 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | |||
| 23 | 28 | ||
| 24 | static ResultCode ValidateServiceName(const std::string& name) { | 29 | static ResultCode ValidateServiceName(const std::string& name) { |
| 25 | if (name.size() <= 0 || name.size() > 8) { | 30 | if (name.size() <= 0 || name.size() > 8) { |
| 26 | return ERR_INVALID_NAME_SIZE; | 31 | return ERR_INVALID_NAME; |
| 27 | } | 32 | } |
| 28 | if (name.find('\0') != std::string::npos) { | 33 | if (name.find('\0') != std::string::npos) { |
| 29 | return ERR_NAME_CONTAINS_NUL; | 34 | return ERR_INVALID_NAME; |
| 30 | } | 35 | } |
| 31 | return RESULT_SUCCESS; | 36 | return RESULT_SUCCESS; |
| 32 | } | 37 | } |
| @@ -103,7 +108,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 103 | 108 | ||
| 104 | auto client_port = service_manager->GetServicePort(name); | 109 | auto client_port = service_manager->GetServicePort(name); |
| 105 | if (client_port.Failed()) { | 110 | if (client_port.Failed()) { |
| 106 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 111 | IPC::ResponseBuilder rb{ctx, 2}; |
| 107 | rb.Push(client_port.Code()); | 112 | rb.Push(client_port.Code()); |
| 108 | LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); | 113 | LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); |
| 109 | if (name.length() == 0) | 114 | if (name.length() == 0) |
| @@ -116,8 +121,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 116 | ASSERT(session.Succeeded()); | 121 | ASSERT(session.Succeeded()); |
| 117 | if (session.Succeeded()) { | 122 | if (session.Succeeded()) { |
| 118 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); | 123 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); |
| 119 | IPC::ResponseBuilder rb = | 124 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; |
| 120 | rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles); | ||
| 121 | rb.Push(session.Code()); | 125 | rb.Push(session.Code()); |
| 122 | rb.PushMoveObjects(std::move(session).Unwrap()); | 126 | rb.PushMoveObjects(std::move(session).Unwrap()); |
| 123 | } | 127 | } |
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index e8ea62f08..da2c51082 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h | |||
| @@ -36,16 +36,11 @@ private: | |||
| 36 | std::shared_ptr<ServiceManager> service_manager; | 36 | std::shared_ptr<ServiceManager> service_manager; |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1); | ||
| 40 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); | ||
| 41 | constexpr ResultCode ERR_INVALID_NAME_SIZE(-1); | ||
| 42 | constexpr ResultCode ERR_NAME_CONTAINS_NUL(-1); | ||
| 43 | constexpr ResultCode ERR_ALREADY_REGISTERED(-1); | ||
| 44 | |||
| 45 | class ServiceManager { | 39 | class ServiceManager { |
| 46 | public: | 40 | public: |
| 47 | static void InstallInterfaces(std::shared_ptr<ServiceManager> self); | 41 | static void InstallInterfaces(std::shared_ptr<ServiceManager> self); |
| 48 | 42 | ||
| 43 | ServiceManager(); | ||
| 49 | ~ServiceManager(); | 44 | ~ServiceManager(); |
| 50 | 45 | ||
| 51 | ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, | 46 | ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 3211a8346..4342f3b2d 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -109,6 +109,8 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 109 | RegisterHandlers(functions); | 109 | RegisterHandlers(functions); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | BSD::~BSD() = default; | ||
| 113 | |||
| 112 | BSDCFG::BSDCFG() : ServiceFramework{"bsdcfg"} { | 114 | BSDCFG::BSDCFG() : ServiceFramework{"bsdcfg"} { |
| 113 | // clang-format off | 115 | // clang-format off |
| 114 | static const FunctionInfo functions[] = { | 116 | static const FunctionInfo functions[] = { |
| @@ -131,4 +133,6 @@ BSDCFG::BSDCFG() : ServiceFramework{"bsdcfg"} { | |||
| 131 | RegisterHandlers(functions); | 133 | RegisterHandlers(functions); |
| 132 | } | 134 | } |
| 133 | 135 | ||
| 136 | BSDCFG::~BSDCFG() = default; | ||
| 137 | |||
| 134 | } // namespace Service::Sockets | 138 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index c1da59b24..0fe0e65c6 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -12,7 +12,7 @@ namespace Service::Sockets { | |||
| 12 | class BSD final : public ServiceFramework<BSD> { | 12 | class BSD final : public ServiceFramework<BSD> { |
| 13 | public: | 13 | public: |
| 14 | explicit BSD(const char* name); | 14 | explicit BSD(const char* name); |
| 15 | ~BSD() = default; | 15 | ~BSD() override; |
| 16 | 16 | ||
| 17 | private: | 17 | private: |
| 18 | void RegisterClient(Kernel::HLERequestContext& ctx); | 18 | void RegisterClient(Kernel::HLERequestContext& ctx); |
| @@ -29,6 +29,7 @@ private: | |||
| 29 | class BSDCFG final : public ServiceFramework<BSDCFG> { | 29 | class BSDCFG final : public ServiceFramework<BSDCFG> { |
| 30 | public: | 30 | public: |
| 31 | explicit BSDCFG(); | 31 | explicit BSDCFG(); |
| 32 | ~BSDCFG() override; | ||
| 32 | }; | 33 | }; |
| 33 | 34 | ||
| 34 | } // namespace Service::Sockets | 35 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp index d53c25eec..abbeb4c50 100644 --- a/src/core/hle/service/sockets/ethc.cpp +++ b/src/core/hle/service/sockets/ethc.cpp | |||
| @@ -21,6 +21,8 @@ ETHC_C::ETHC_C() : ServiceFramework{"ethc:c"} { | |||
| 21 | RegisterHandlers(functions); | 21 | RegisterHandlers(functions); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | ETHC_C::~ETHC_C() = default; | ||
| 25 | |||
| 24 | ETHC_I::ETHC_I() : ServiceFramework{"ethc:i"} { | 26 | ETHC_I::ETHC_I() : ServiceFramework{"ethc:i"} { |
| 25 | // clang-format off | 27 | // clang-format off |
| 26 | static const FunctionInfo functions[] = { | 28 | static const FunctionInfo functions[] = { |
| @@ -35,4 +37,6 @@ ETHC_I::ETHC_I() : ServiceFramework{"ethc:i"} { | |||
| 35 | RegisterHandlers(functions); | 37 | RegisterHandlers(functions); |
| 36 | } | 38 | } |
| 37 | 39 | ||
| 40 | ETHC_I::~ETHC_I() = default; | ||
| 41 | |||
| 38 | } // namespace Service::Sockets | 42 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/ethc.h b/src/core/hle/service/sockets/ethc.h index 9a3c88100..da2c7f741 100644 --- a/src/core/hle/service/sockets/ethc.h +++ b/src/core/hle/service/sockets/ethc.h | |||
| @@ -11,11 +11,13 @@ namespace Service::Sockets { | |||
| 11 | class ETHC_C final : public ServiceFramework<ETHC_C> { | 11 | class ETHC_C final : public ServiceFramework<ETHC_C> { |
| 12 | public: | 12 | public: |
| 13 | explicit ETHC_C(); | 13 | explicit ETHC_C(); |
| 14 | ~ETHC_C() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | class ETHC_I final : public ServiceFramework<ETHC_I> { | 17 | class ETHC_I final : public ServiceFramework<ETHC_I> { |
| 17 | public: | 18 | public: |
| 18 | explicit ETHC_I(); | 19 | explicit ETHC_I(); |
| 20 | ~ETHC_I() override; | ||
| 19 | }; | 21 | }; |
| 20 | 22 | ||
| 21 | } // namespace Service::Sockets | 23 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index 8682dc2e0..e6d73065e 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp | |||
| @@ -29,4 +29,6 @@ NSD::NSD(const char* name) : ServiceFramework(name) { | |||
| 29 | RegisterHandlers(functions); | 29 | RegisterHandlers(functions); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | NSD::~NSD() = default; | ||
| 33 | |||
| 32 | } // namespace Service::Sockets | 34 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h index 3b7edfc43..d842e3232 100644 --- a/src/core/hle/service/sockets/nsd.h +++ b/src/core/hle/service/sockets/nsd.h | |||
| @@ -12,7 +12,7 @@ namespace Service::Sockets { | |||
| 12 | class NSD final : public ServiceFramework<NSD> { | 12 | class NSD final : public ServiceFramework<NSD> { |
| 13 | public: | 13 | public: |
| 14 | explicit NSD(const char* name); | 14 | explicit NSD(const char* name); |
| 15 | ~NSD() = default; | 15 | ~NSD() override; |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | } // namespace Service::Sockets | 18 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index d235c4cfd..13ab1d31e 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp | |||
| @@ -34,4 +34,6 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") { | |||
| 34 | RegisterHandlers(functions); | 34 | RegisterHandlers(functions); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | SFDNSRES::~SFDNSRES() = default; | ||
| 38 | |||
| 37 | } // namespace Service::Sockets | 39 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 62c7e35bf..eda432903 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h | |||
| @@ -12,7 +12,7 @@ namespace Service::Sockets { | |||
| 12 | class SFDNSRES final : public ServiceFramework<SFDNSRES> { | 12 | class SFDNSRES final : public ServiceFramework<SFDNSRES> { |
| 13 | public: | 13 | public: |
| 14 | explicit SFDNSRES(); | 14 | explicit SFDNSRES(); |
| 15 | ~SFDNSRES() = default; | 15 | ~SFDNSRES() override; |
| 16 | 16 | ||
| 17 | private: | 17 | private: |
| 18 | void GetAddrInfo(Kernel::HLERequestContext& ctx); | 18 | void GetAddrInfo(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp index b9e6b799d..674928798 100644 --- a/src/core/hle/service/spl/csrng.cpp +++ b/src/core/hle/service/spl/csrng.cpp | |||
| @@ -13,4 +13,6 @@ CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(modul | |||
| 13 | RegisterHandlers(functions); | 13 | RegisterHandlers(functions); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | CSRNG::~CSRNG() = default; | ||
| 17 | |||
| 16 | } // namespace Service::SPL | 18 | } // namespace Service::SPL |
diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h index 3f849b5a7..764d5ceb0 100644 --- a/src/core/hle/service/spl/csrng.h +++ b/src/core/hle/service/spl/csrng.h | |||
| @@ -11,6 +11,7 @@ namespace Service::SPL { | |||
| 11 | class CSRNG final : public Module::Interface { | 11 | class CSRNG final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit CSRNG(std::shared_ptr<Module> module); | 13 | explicit CSRNG(std::shared_ptr<Module> module); |
| 14 | ~CSRNG() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::SPL | 17 | } // namespace Service::SPL |
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 3f5a342a7..44a6717d0 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp | |||
| @@ -16,10 +16,12 @@ namespace Service::SPL { | |||
| 16 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 16 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 17 | : ServiceFramework(name), module(std::move(module)) {} | 17 | : ServiceFramework(name), module(std::move(module)) {} |
| 18 | 18 | ||
| 19 | Module::Interface::~Interface() = default; | ||
| 20 | |||
| 19 | void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { | 21 | void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { |
| 20 | IPC::RequestParser rp{ctx}; | 22 | IPC::RequestParser rp{ctx}; |
| 21 | 23 | ||
| 22 | size_t size = ctx.GetWriteBufferSize(); | 24 | std::size_t size = ctx.GetWriteBufferSize(); |
| 23 | 25 | ||
| 24 | std::vector<u8> data(size); | 26 | std::vector<u8> data(size); |
| 25 | std::generate(data.begin(), data.end(), std::rand); | 27 | std::generate(data.begin(), data.end(), std::rand); |
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h index f24d998e8..48fda6099 100644 --- a/src/core/hle/service/spl/module.h +++ b/src/core/hle/service/spl/module.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Interface : public ServiceFramework<Interface> { | 13 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 14 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | ||
| 16 | 17 | ||
| 17 | void GetRandomBytes(Kernel::HLERequestContext& ctx); | 18 | void GetRandomBytes(Kernel::HLERequestContext& ctx); |
| 18 | 19 | ||
diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp index bb1e03342..70cb41905 100644 --- a/src/core/hle/service/spl/spl.cpp +++ b/src/core/hle/service/spl/spl.cpp | |||
| @@ -42,4 +42,6 @@ SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), | |||
| 42 | RegisterHandlers(functions); | 42 | RegisterHandlers(functions); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | SPL::~SPL() = default; | ||
| 46 | |||
| 45 | } // namespace Service::SPL | 47 | } // namespace Service::SPL |
diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h index 69c4c1747..3637d1623 100644 --- a/src/core/hle/service/spl/spl.h +++ b/src/core/hle/service/spl/spl.h | |||
| @@ -11,6 +11,7 @@ namespace Service::SPL { | |||
| 11 | class SPL final : public Module::Interface { | 11 | class SPL final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit SPL(std::shared_ptr<Module> module); | 13 | explicit SPL(std::shared_ptr<Module> module); |
| 14 | ~SPL() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::SPL | 17 | } // namespace Service::SPL |
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index 40aea6090..fe0a318ee 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/ipc_helpers.h" | 5 | #include "core/hle/ipc_helpers.h" |
| 6 | #include "core/hle/kernel/hle_ipc.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | #include "core/hle/service/sm/sm.h" | ||
| 6 | #include "core/hle/service/ssl/ssl.h" | 9 | #include "core/hle/service/ssl/ssl.h" |
| 7 | 10 | ||
| 8 | namespace Service::SSL { | 11 | namespace Service::SSL { |
| @@ -68,7 +71,7 @@ private: | |||
| 68 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 71 | LOG_WARNING(Service_SSL, "(STUBBED) called"); |
| 69 | IPC::RequestParser rp{ctx}; | 72 | IPC::RequestParser rp{ctx}; |
| 70 | 73 | ||
| 71 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 74 | IPC::ResponseBuilder rb{ctx, 2}; |
| 72 | rb.Push(RESULT_SUCCESS); | 75 | rb.Push(RESULT_SUCCESS); |
| 73 | } | 76 | } |
| 74 | 77 | ||
| @@ -81,36 +84,43 @@ private: | |||
| 81 | } | 84 | } |
| 82 | }; | 85 | }; |
| 83 | 86 | ||
| 84 | void SSL::CreateContext(Kernel::HLERequestContext& ctx) { | 87 | class SSL final : public ServiceFramework<SSL> { |
| 85 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 88 | public: |
| 89 | explicit SSL() : ServiceFramework{"ssl"} { | ||
| 90 | // clang-format off | ||
| 91 | static const FunctionInfo functions[] = { | ||
| 92 | {0, &SSL::CreateContext, "CreateContext"}, | ||
| 93 | {1, nullptr, "GetContextCount"}, | ||
| 94 | {2, nullptr, "GetCertificates"}, | ||
| 95 | {3, nullptr, "GetCertificateBufSize"}, | ||
| 96 | {4, nullptr, "DebugIoctl"}, | ||
| 97 | {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, | ||
| 98 | {6, nullptr, "FlushSessionCache"}, | ||
| 99 | }; | ||
| 100 | // clang-format on | ||
| 86 | 101 | ||
| 87 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 102 | RegisterHandlers(functions); |
| 88 | rb.Push(RESULT_SUCCESS); | 103 | } |
| 89 | rb.PushIpcInterface<ISslContext>(); | ||
| 90 | } | ||
| 91 | 104 | ||
| 92 | SSL::SSL() : ServiceFramework("ssl") { | 105 | private: |
| 93 | static const FunctionInfo functions[] = { | 106 | void CreateContext(Kernel::HLERequestContext& ctx) { |
| 94 | {0, &SSL::CreateContext, "CreateContext"}, | 107 | LOG_WARNING(Service_SSL, "(STUBBED) called"); |
| 95 | {1, nullptr, "GetContextCount"}, | ||
| 96 | {2, nullptr, "GetCertificates"}, | ||
| 97 | {3, nullptr, "GetCertificateBufSize"}, | ||
| 98 | {4, nullptr, "DebugIoctl"}, | ||
| 99 | {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, | ||
| 100 | {6, nullptr, "FlushSessionCache"}, | ||
| 101 | }; | ||
| 102 | RegisterHandlers(functions); | ||
| 103 | } | ||
| 104 | 108 | ||
| 105 | void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) { | 109 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 106 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 110 | rb.Push(RESULT_SUCCESS); |
| 107 | IPC::RequestParser rp{ctx}; | 111 | rb.PushIpcInterface<ISslContext>(); |
| 108 | u32 unk1 = rp.Pop<u32>(); // Probably minor/major? | 112 | } |
| 109 | u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does | ||
| 110 | 113 | ||
| 111 | IPC::ResponseBuilder rb{ctx, 2}; | 114 | void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { |
| 112 | rb.Push(RESULT_SUCCESS); | 115 | LOG_WARNING(Service_SSL, "(STUBBED) called"); |
| 113 | } | 116 | IPC::RequestParser rp{ctx}; |
| 117 | u32 unk1 = rp.Pop<u32>(); // Probably minor/major? | ||
| 118 | u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does | ||
| 119 | |||
| 120 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 121 | rb.Push(RESULT_SUCCESS); | ||
| 122 | } | ||
| 123 | }; | ||
| 114 | 124 | ||
| 115 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 125 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 116 | std::make_shared<SSL>()->InstallAsService(service_manager); | 126 | std::make_shared<SSL>()->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h index 8fef13022..5cb04c3b9 100644 --- a/src/core/hle/service/ssl/ssl.h +++ b/src/core/hle/service/ssl/ssl.h | |||
| @@ -4,20 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | 7 | namespace Service::SM { |
| 8 | class ServiceManager; | ||
| 9 | } | ||
| 8 | 10 | ||
| 9 | namespace Service::SSL { | 11 | namespace Service::SSL { |
| 10 | 12 | ||
| 11 | class SSL final : public ServiceFramework<SSL> { | ||
| 12 | public: | ||
| 13 | explicit SSL(); | ||
| 14 | ~SSL() = default; | ||
| 15 | |||
| 16 | private: | ||
| 17 | void CreateContext(Kernel::HLERequestContext& ctx); | ||
| 18 | void SetInterfaceVersion(Kernel::HLERequestContext& ctx); | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// Registers all SSL services with the specified service manager. | 13 | /// Registers all SSL services with the specified service manager. |
| 22 | void InstallInterfaces(SM::ServiceManager& service_manager); | 14 | void InstallInterfaces(SM::ServiceManager& service_manager); |
| 23 | 15 | ||
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 048d5b077..18a5d71d5 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp | |||
| @@ -29,4 +29,6 @@ Time::Time(std::shared_ptr<Module> time, const char* name) | |||
| 29 | RegisterHandlers(functions); | 29 | RegisterHandlers(functions); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | Time::~Time() = default; | ||
| 33 | |||
| 32 | } // namespace Service::Time | 34 | } // namespace Service::Time |
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h index 183a53db1..cd6b44dec 100644 --- a/src/core/hle/service/time/interface.h +++ b/src/core/hle/service/time/interface.h | |||
| @@ -11,6 +11,7 @@ namespace Service::Time { | |||
| 11 | class Time final : public Module::Interface { | 11 | class Time final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Time(std::shared_ptr<Module> time, const char* name); | 13 | explicit Time(std::shared_ptr<Module> time, const char* name); |
| 14 | ~Time() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::Time | 17 | } // namespace Service::Time |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 2172c681b..28fd8debc 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -210,6 +210,8 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c | |||
| 210 | Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) | 210 | Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) |
| 211 | : ServiceFramework(name), time(std::move(time)) {} | 211 | : ServiceFramework(name), time(std::move(time)) {} |
| 212 | 212 | ||
| 213 | Module::Interface::~Interface() = default; | ||
| 214 | |||
| 213 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 215 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 214 | auto time = std::make_shared<Module>(); | 216 | auto time = std::make_shared<Module>(); |
| 215 | std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager); | 217 | std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 8dde28a94..5659ecad3 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h | |||
| @@ -58,6 +58,7 @@ public: | |||
| 58 | class Interface : public ServiceFramework<Interface> { | 58 | class Interface : public ServiceFramework<Interface> { |
| 59 | public: | 59 | public: |
| 60 | explicit Interface(std::shared_ptr<Module> time, const char* name); | 60 | explicit Interface(std::shared_ptr<Module> time, const char* name); |
| 61 | ~Interface() override; | ||
| 61 | 62 | ||
| 62 | void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); | 63 | void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); |
| 63 | void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); | 64 | void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 993f1e65a..2ee60f1ec 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -4,25 +4,28 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstring> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <type_traits> | 9 | #include <type_traits> |
| 9 | #include <utility> | 10 | #include <utility> |
| 10 | #include <boost/optional.hpp> | 11 | #include <boost/optional.hpp> |
| 11 | #include "common/alignment.h" | 12 | #include "common/alignment.h" |
| 13 | #include "common/assert.h" | ||
| 14 | #include "common/common_funcs.h" | ||
| 15 | #include "common/logging/log.h" | ||
| 12 | #include "common/math_util.h" | 16 | #include "common/math_util.h" |
| 13 | #include "common/scope_exit.h" | 17 | #include "common/swap.h" |
| 14 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 15 | #include "core/hle/ipc_helpers.h" | 19 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/event.h" | 20 | #include "core/hle/kernel/event.h" |
| 17 | #include "core/hle/service/nvdrv/nvdrv.h" | 21 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 18 | #include "core/hle/service/nvflinger/buffer_queue.h" | 22 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 23 | #include "core/hle/service/nvflinger/nvflinger.h" | ||
| 19 | #include "core/hle/service/vi/vi.h" | 24 | #include "core/hle/service/vi/vi.h" |
| 20 | #include "core/hle/service/vi/vi_m.h" | 25 | #include "core/hle/service/vi/vi_m.h" |
| 21 | #include "core/hle/service/vi/vi_s.h" | 26 | #include "core/hle/service/vi/vi_s.h" |
| 22 | #include "core/hle/service/vi/vi_u.h" | 27 | #include "core/hle/service/vi/vi_u.h" |
| 23 | #include "core/settings.h" | 28 | #include "core/settings.h" |
| 24 | #include "video_core/renderer_base.h" | ||
| 25 | #include "video_core/video_core.h" | ||
| 26 | 29 | ||
| 27 | namespace Service::VI { | 30 | namespace Service::VI { |
| 28 | 31 | ||
| @@ -38,7 +41,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); | |||
| 38 | class Parcel { | 41 | class Parcel { |
| 39 | public: | 42 | public: |
| 40 | // This default size was chosen arbitrarily. | 43 | // This default size was chosen arbitrarily. |
| 41 | static constexpr size_t DefaultBufferSize = 0x40; | 44 | static constexpr std::size_t DefaultBufferSize = 0x40; |
| 42 | Parcel() : buffer(DefaultBufferSize) {} | 45 | Parcel() : buffer(DefaultBufferSize) {} |
| 43 | explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} | 46 | explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} |
| 44 | virtual ~Parcel() = default; | 47 | virtual ~Parcel() = default; |
| @@ -66,7 +69,7 @@ public: | |||
| 66 | return val; | 69 | return val; |
| 67 | } | 70 | } |
| 68 | 71 | ||
| 69 | std::vector<u8> ReadBlock(size_t length) { | 72 | std::vector<u8> ReadBlock(std::size_t length) { |
| 70 | ASSERT(read_index + length <= buffer.size()); | 73 | ASSERT(read_index + length <= buffer.size()); |
| 71 | const u8* const begin = buffer.data() + read_index; | 74 | const u8* const begin = buffer.data() + read_index; |
| 72 | const u8* const end = begin + length; | 75 | const u8* const end = begin + length; |
| @@ -156,8 +159,8 @@ private: | |||
| 156 | static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); | 159 | static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); |
| 157 | 160 | ||
| 158 | std::vector<u8> buffer; | 161 | std::vector<u8> buffer; |
| 159 | size_t read_index = 0; | 162 | std::size_t read_index = 0; |
| 160 | size_t write_index = 0; | 163 | std::size_t write_index = 0; |
| 161 | }; | 164 | }; |
| 162 | 165 | ||
| 163 | class NativeWindow : public Parcel { | 166 | class NativeWindow : public Parcel { |
| @@ -514,7 +517,7 @@ private: | |||
| 514 | ctx.SleepClientThread( | 517 | ctx.SleepClientThread( |
| 515 | Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, | 518 | Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, |
| 516 | [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 519 | [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, |
| 517 | ThreadWakeupReason reason) { | 520 | Kernel::ThreadWakeupReason reason) { |
| 518 | // Repeat TransactParcel DequeueBuffer when a buffer is available | 521 | // Repeat TransactParcel DequeueBuffer when a buffer is available |
| 519 | auto buffer_queue = nv_flinger->GetBufferQueue(id); | 522 | auto buffer_queue = nv_flinger->GetBufferQueue(id); |
| 520 | boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); | 523 | boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); |
| @@ -647,7 +650,7 @@ private: | |||
| 647 | u64 layer_id = rp.Pop<u64>(); | 650 | u64 layer_id = rp.Pop<u64>(); |
| 648 | u64 z_value = rp.Pop<u64>(); | 651 | u64 z_value = rp.Pop<u64>(); |
| 649 | 652 | ||
| 650 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 653 | IPC::ResponseBuilder rb{ctx, 2}; |
| 651 | rb.Push(RESULT_SUCCESS); | 654 | rb.Push(RESULT_SUCCESS); |
| 652 | } | 655 | } |
| 653 | 656 | ||
| @@ -655,7 +658,7 @@ private: | |||
| 655 | IPC::RequestParser rp{ctx}; | 658 | IPC::RequestParser rp{ctx}; |
| 656 | u64 layer_id = rp.Pop<u64>(); | 659 | u64 layer_id = rp.Pop<u64>(); |
| 657 | bool visibility = rp.Pop<bool>(); | 660 | bool visibility = rp.Pop<bool>(); |
| 658 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 661 | IPC::ResponseBuilder rb{ctx, 2}; |
| 659 | rb.Push(RESULT_SUCCESS); | 662 | rb.Push(RESULT_SUCCESS); |
| 660 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, | 663 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, |
| 661 | visibility); | 664 | visibility); |
| @@ -744,7 +747,7 @@ private: | |||
| 744 | IPC::RequestParser rp{ctx}; | 747 | IPC::RequestParser rp{ctx}; |
| 745 | u64 display = rp.Pop<u64>(); | 748 | u64 display = rp.Pop<u64>(); |
| 746 | 749 | ||
| 747 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 750 | IPC::ResponseBuilder rb{ctx, 2}; |
| 748 | rb.Push(RESULT_SUCCESS); | 751 | rb.Push(RESULT_SUCCESS); |
| 749 | } | 752 | } |
| 750 | 753 | ||
| @@ -758,7 +761,7 @@ private: | |||
| 758 | 761 | ||
| 759 | u64 layer_id = nv_flinger->CreateLayer(display); | 762 | u64 layer_id = nv_flinger->CreateLayer(display); |
| 760 | 763 | ||
| 761 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 764 | IPC::ResponseBuilder rb{ctx, 4}; |
| 762 | rb.Push(RESULT_SUCCESS); | 765 | rb.Push(RESULT_SUCCESS); |
| 763 | rb.Push(layer_id); | 766 | rb.Push(layer_id); |
| 764 | } | 767 | } |
| @@ -769,7 +772,7 @@ private: | |||
| 769 | u32 stack = rp.Pop<u32>(); | 772 | u32 stack = rp.Pop<u32>(); |
| 770 | u64 layer_id = rp.Pop<u64>(); | 773 | u64 layer_id = rp.Pop<u64>(); |
| 771 | 774 | ||
| 772 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 775 | IPC::ResponseBuilder rb{ctx, 2}; |
| 773 | rb.Push(RESULT_SUCCESS); | 776 | rb.Push(RESULT_SUCCESS); |
| 774 | } | 777 | } |
| 775 | 778 | ||
| @@ -777,7 +780,7 @@ private: | |||
| 777 | IPC::RequestParser rp{ctx}; | 780 | IPC::RequestParser rp{ctx}; |
| 778 | u64 layer_id = rp.Pop<u64>(); | 781 | u64 layer_id = rp.Pop<u64>(); |
| 779 | bool visibility = rp.Pop<bool>(); | 782 | bool visibility = rp.Pop<bool>(); |
| 780 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 783 | IPC::ResponseBuilder rb{ctx, 2}; |
| 781 | rb.Push(RESULT_SUCCESS); | 784 | rb.Push(RESULT_SUCCESS); |
| 782 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, | 785 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, |
| 783 | visibility); | 786 | visibility); |
| @@ -834,7 +837,7 @@ private: | |||
| 834 | 837 | ||
| 835 | ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); | 838 | ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); |
| 836 | 839 | ||
| 837 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 840 | IPC::ResponseBuilder rb{ctx, 4}; |
| 838 | rb.Push(RESULT_SUCCESS); | 841 | rb.Push(RESULT_SUCCESS); |
| 839 | rb.Push<u64>(nv_flinger->OpenDisplay(name)); | 842 | rb.Push<u64>(nv_flinger->OpenDisplay(name)); |
| 840 | } | 843 | } |
| @@ -844,7 +847,7 @@ private: | |||
| 844 | IPC::RequestParser rp{ctx}; | 847 | IPC::RequestParser rp{ctx}; |
| 845 | u64 display_id = rp.Pop<u64>(); | 848 | u64 display_id = rp.Pop<u64>(); |
| 846 | 849 | ||
| 847 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 850 | IPC::ResponseBuilder rb{ctx, 2}; |
| 848 | rb.Push(RESULT_SUCCESS); | 851 | rb.Push(RESULT_SUCCESS); |
| 849 | } | 852 | } |
| 850 | 853 | ||
| @@ -853,7 +856,7 @@ private: | |||
| 853 | IPC::RequestParser rp{ctx}; | 856 | IPC::RequestParser rp{ctx}; |
| 854 | u64 display_id = rp.Pop<u64>(); | 857 | u64 display_id = rp.Pop<u64>(); |
| 855 | 858 | ||
| 856 | IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); | 859 | IPC::ResponseBuilder rb{ctx, 6}; |
| 857 | rb.Push(RESULT_SUCCESS); | 860 | rb.Push(RESULT_SUCCESS); |
| 858 | 861 | ||
| 859 | if (Settings::values.use_docked_mode) { | 862 | if (Settings::values.use_docked_mode) { |
| @@ -871,7 +874,7 @@ private: | |||
| 871 | u32 scaling_mode = rp.Pop<u32>(); | 874 | u32 scaling_mode = rp.Pop<u32>(); |
| 872 | u64 unknown = rp.Pop<u64>(); | 875 | u64 unknown = rp.Pop<u64>(); |
| 873 | 876 | ||
| 874 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 877 | IPC::ResponseBuilder rb{ctx, 2}; |
| 875 | rb.Push(RESULT_SUCCESS); | 878 | rb.Push(RESULT_SUCCESS); |
| 876 | } | 879 | } |
| 877 | 880 | ||
| @@ -879,7 +882,7 @@ private: | |||
| 879 | IPC::RequestParser rp{ctx}; | 882 | IPC::RequestParser rp{ctx}; |
| 880 | DisplayInfo display_info; | 883 | DisplayInfo display_info; |
| 881 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); | 884 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); |
| 882 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 885 | IPC::ResponseBuilder rb{ctx, 4}; |
| 883 | rb.Push(RESULT_SUCCESS); | 886 | rb.Push(RESULT_SUCCESS); |
| 884 | rb.Push<u64>(1); | 887 | rb.Push<u64>(1); |
| 885 | LOG_WARNING(Service_VI, "(STUBBED) called"); | 888 | LOG_WARNING(Service_VI, "(STUBBED) called"); |
| @@ -900,7 +903,7 @@ private: | |||
| 900 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); | 903 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); |
| 901 | 904 | ||
| 902 | NativeWindow native_window{buffer_queue_id}; | 905 | NativeWindow native_window{buffer_queue_id}; |
| 903 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 906 | IPC::ResponseBuilder rb{ctx, 4}; |
| 904 | rb.Push(RESULT_SUCCESS); | 907 | rb.Push(RESULT_SUCCESS); |
| 905 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); | 908 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); |
| 906 | } | 909 | } |
| @@ -919,7 +922,7 @@ private: | |||
| 919 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); | 922 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); |
| 920 | 923 | ||
| 921 | NativeWindow native_window{buffer_queue_id}; | 924 | NativeWindow native_window{buffer_queue_id}; |
| 922 | IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); | 925 | IPC::ResponseBuilder rb{ctx, 6}; |
| 923 | rb.Push(RESULT_SUCCESS); | 926 | rb.Push(RESULT_SUCCESS); |
| 924 | rb.Push(layer_id); | 927 | rb.Push(layer_id); |
| 925 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); | 928 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); |
| @@ -931,7 +934,7 @@ private: | |||
| 931 | IPC::RequestParser rp{ctx}; | 934 | IPC::RequestParser rp{ctx}; |
| 932 | u64 layer_id = rp.Pop<u64>(); | 935 | u64 layer_id = rp.Pop<u64>(); |
| 933 | 936 | ||
| 934 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 937 | IPC::ResponseBuilder rb{ctx, 2}; |
| 935 | rb.Push(RESULT_SUCCESS); | 938 | rb.Push(RESULT_SUCCESS); |
| 936 | } | 939 | } |
| 937 | 940 | ||
| @@ -942,7 +945,7 @@ private: | |||
| 942 | 945 | ||
| 943 | auto vsync_event = nv_flinger->GetVsyncEvent(display_id); | 946 | auto vsync_event = nv_flinger->GetVsyncEvent(display_id); |
| 944 | 947 | ||
| 945 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0); | 948 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 946 | rb.Push(RESULT_SUCCESS); | 949 | rb.Push(RESULT_SUCCESS); |
| 947 | rb.PushCopyObjects(vsync_event); | 950 | rb.PushCopyObjects(vsync_event); |
| 948 | } | 951 | } |
| @@ -984,6 +987,8 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, | |||
| 984 | std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) | 987 | std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) |
| 985 | : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} | 988 | : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} |
| 986 | 989 | ||
| 990 | Module::Interface::~Interface() = default; | ||
| 991 | |||
| 987 | void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { | 992 | void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { |
| 988 | LOG_WARNING(Service_VI, "(STUBBED) called"); | 993 | LOG_WARNING(Service_VI, "(STUBBED) called"); |
| 989 | 994 | ||
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 92f5b6059..e3963502a 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h | |||
| @@ -4,11 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/nvflinger/nvflinger.h" | ||
| 8 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 9 | 8 | ||
| 10 | namespace CoreTiming { | 9 | namespace Service::NVFlinger { |
| 11 | struct EventType; | 10 | class NVFlinger; |
| 12 | } | 11 | } |
| 13 | 12 | ||
| 14 | namespace Service::VI { | 13 | namespace Service::VI { |
| @@ -26,6 +25,7 @@ public: | |||
| 26 | public: | 25 | public: |
| 27 | explicit Interface(std::shared_ptr<Module> module, const char* name, | 26 | explicit Interface(std::shared_ptr<Module> module, const char* name, |
| 28 | std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); | 27 | std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); |
| 28 | ~Interface() override; | ||
| 29 | 29 | ||
| 30 | void GetDisplayService(Kernel::HLERequestContext& ctx); | 30 | void GetDisplayService(Kernel::HLERequestContext& ctx); |
| 31 | 31 | ||
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index d47da565b..207c06b16 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp | |||
| @@ -15,4 +15,6 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> | |||
| 15 | RegisterHandlers(functions); | 15 | RegisterHandlers(functions); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | VI_M::~VI_M() = default; | ||
| 19 | |||
| 18 | } // namespace Service::VI | 20 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 6abb9b3a3..487d58d50 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h | |||
| @@ -11,6 +11,7 @@ namespace Service::VI { | |||
| 11 | class VI_M final : public Module::Interface { | 11 | class VI_M final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); | 13 | explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); |
| 14 | ~VI_M() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::VI | 17 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 8f82e797f..920e6a1f6 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp | |||
| @@ -15,4 +15,6 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> | |||
| 15 | RegisterHandlers(functions); | 15 | RegisterHandlers(functions); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | VI_S::~VI_S() = default; | ||
| 19 | |||
| 18 | } // namespace Service::VI | 20 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 8f16f804f..bbc31148f 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h | |||
| @@ -11,6 +11,7 @@ namespace Service::VI { | |||
| 11 | class VI_S final : public Module::Interface { | 11 | class VI_S final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); | 13 | explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); |
| 14 | ~VI_S() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::VI | 17 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index b84aed1d5..d81e410d6 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp | |||
| @@ -14,4 +14,6 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> | |||
| 14 | RegisterHandlers(functions); | 14 | RegisterHandlers(functions); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | VI_U::~VI_U() = default; | ||
| 18 | |||
| 17 | } // namespace Service::VI | 19 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index e9b4f76b2..b92f28c92 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h | |||
| @@ -11,6 +11,7 @@ namespace Service::VI { | |||
| 11 | class VI_U final : public Module::Interface { | 11 | class VI_U final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); | 13 | explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); |
| 14 | ~VI_U() override; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | } // namespace Service::VI | 17 | } // namespace Service::VI |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 921b899e2..2b8f78136 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/file_sys/content_archive.h" | 10 | #include "core/file_sys/content_archive.h" |
| 11 | #include "core/file_sys/control_metadata.h" | 11 | #include "core/file_sys/control_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | ||
| 12 | #include "core/file_sys/romfs_factory.h" | 13 | #include "core/file_sys/romfs_factory.h" |
| 13 | #include "core/gdbstub/gdbstub.h" | 14 | #include "core/gdbstub/gdbstub.h" |
| 14 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| @@ -21,10 +22,19 @@ | |||
| 21 | 22 | ||
| 22 | namespace Loader { | 23 | namespace Loader { |
| 23 | 24 | ||
| 24 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_) | 25 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, |
| 25 | : AppLoader(std::move(file_)) { | 26 | bool override_update) |
| 27 | : AppLoader(std::move(file_)), override_update(override_update) { | ||
| 26 | const auto dir = file->GetContainingDirectory(); | 28 | const auto dir = file->GetContainingDirectory(); |
| 27 | 29 | ||
| 30 | // Title ID | ||
| 31 | const auto npdm = dir->GetFile("main.npdm"); | ||
| 32 | if (npdm != nullptr) { | ||
| 33 | const auto res = metadata.Load(npdm); | ||
| 34 | if (res == ResultStatus::Success) | ||
| 35 | title_id = metadata.GetTitleID(); | ||
| 36 | } | ||
| 37 | |||
| 28 | // Icon | 38 | // Icon |
| 29 | FileSys::VirtualFile icon_file = nullptr; | 39 | FileSys::VirtualFile icon_file = nullptr; |
| 30 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | 40 | for (const auto& language : FileSys::LANGUAGE_NAMES) { |
| @@ -61,14 +71,14 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys | |||
| 61 | 71 | ||
| 62 | if (nacp_file != nullptr) { | 72 | if (nacp_file != nullptr) { |
| 63 | FileSys::NACP nacp(nacp_file); | 73 | FileSys::NACP nacp(nacp_file); |
| 64 | title_id = nacp.GetTitleId(); | ||
| 65 | name = nacp.GetApplicationName(); | 74 | name = nacp.GetApplicationName(); |
| 66 | } | 75 | } |
| 67 | } | 76 | } |
| 68 | 77 | ||
| 69 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( | 78 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( |
| 70 | FileSys::VirtualDir directory) | 79 | FileSys::VirtualDir directory, bool override_update) |
| 71 | : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {} | 80 | : AppLoader(directory->GetFile("main")), dir(std::move(directory)), |
| 81 | override_update(override_update) {} | ||
| 72 | 82 | ||
| 73 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { | 83 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { |
| 74 | if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { | 84 | if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { |
| @@ -90,7 +100,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 90 | dir = file->GetContainingDirectory(); | 100 | dir = file->GetContainingDirectory(); |
| 91 | } | 101 | } |
| 92 | 102 | ||
| 93 | const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); | 103 | // Read meta to determine title ID |
| 104 | FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); | ||
| 94 | if (npdm == nullptr) | 105 | if (npdm == nullptr) |
| 95 | return ResultStatus::ErrorMissingNPDM; | 106 | return ResultStatus::ErrorMissingNPDM; |
| 96 | 107 | ||
| @@ -98,6 +109,21 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 98 | if (result != ResultStatus::Success) { | 109 | if (result != ResultStatus::Success) { |
| 99 | return result; | 110 | return result; |
| 100 | } | 111 | } |
| 112 | |||
| 113 | if (override_update) { | ||
| 114 | const FileSys::PatchManager patch_manager(metadata.GetTitleID()); | ||
| 115 | dir = patch_manager.PatchExeFS(dir); | ||
| 116 | } | ||
| 117 | |||
| 118 | // Reread in case PatchExeFS affected the main.npdm | ||
| 119 | npdm = dir->GetFile("main.npdm"); | ||
| 120 | if (npdm == nullptr) | ||
| 121 | return ResultStatus::ErrorMissingNPDM; | ||
| 122 | |||
| 123 | ResultStatus result2 = metadata.Load(npdm); | ||
| 124 | if (result2 != ResultStatus::Success) { | ||
| 125 | return result2; | ||
| 126 | } | ||
| 101 | metadata.Print(); | 127 | metadata.Print(); |
| 102 | 128 | ||
| 103 | const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; | 129 | const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; |
| @@ -159,8 +185,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buff | |||
| 159 | } | 185 | } |
| 160 | 186 | ||
| 161 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { | 187 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { |
| 162 | if (name.empty()) | ||
| 163 | return ResultStatus::ErrorNoControl; | ||
| 164 | out_program_id = title_id; | 188 | out_program_id = title_id; |
| 165 | return ResultStatus::Success; | 189 | return ResultStatus::Success; |
| 166 | } | 190 | } |
| @@ -172,4 +196,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) | |||
| 172 | return ResultStatus::Success; | 196 | return ResultStatus::Success; |
| 173 | } | 197 | } |
| 174 | 198 | ||
| 199 | bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const { | ||
| 200 | return false; | ||
| 201 | } | ||
| 202 | |||
| 175 | } // namespace Loader | 203 | } // namespace Loader |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index b20804f75..8a0dc1b1e 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -20,10 +20,12 @@ namespace Loader { | |||
| 20 | */ | 20 | */ |
| 21 | class AppLoader_DeconstructedRomDirectory final : public AppLoader { | 21 | class AppLoader_DeconstructedRomDirectory final : public AppLoader { |
| 22 | public: | 22 | public: |
| 23 | explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); | 23 | explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file, |
| 24 | bool override_update = false); | ||
| 24 | 25 | ||
| 25 | // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' | 26 | // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' |
| 26 | explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory); | 27 | explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory, |
| 28 | bool override_update = false); | ||
| 27 | 29 | ||
| 28 | /** | 30 | /** |
| 29 | * Returns the type of the file | 31 | * Returns the type of the file |
| @@ -42,6 +44,7 @@ public: | |||
| 42 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 45 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 44 | ResultStatus ReadTitle(std::string& title) override; | 46 | ResultStatus ReadTitle(std::string& title) override; |
| 47 | bool IsRomFSUpdatable() const override; | ||
| 45 | 48 | ||
| 46 | private: | 49 | private: |
| 47 | FileSys::ProgramMetadata metadata; | 50 | FileSys::ProgramMetadata metadata; |
| @@ -51,6 +54,7 @@ private: | |||
| 51 | std::vector<u8> icon_data; | 54 | std::vector<u8> icon_data; |
| 52 | std::string name; | 55 | std::string name; |
| 53 | u64 title_id{}; | 56 | u64 title_id{}; |
| 57 | bool override_update; | ||
| 54 | }; | 58 | }; |
| 55 | 59 | ||
| 56 | } // namespace Loader | 60 | } // namespace Loader |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 120e1e133..0e2af20b4 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -300,7 +300,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { | |||
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | std::vector<u8> program_image(total_image_size); | 302 | std::vector<u8> program_image(total_image_size); |
| 303 | size_t current_image_position = 0; | 303 | std::size_t current_image_position = 0; |
| 304 | 304 | ||
| 305 | auto& kernel = Core::System::GetInstance().Kernel(); | 305 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 306 | SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); | 306 | SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index c13fb49b8..f2a183ba1 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -5,9 +5,9 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <ostream> | 6 | #include <ostream> |
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/file_util.h" | ||
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 9 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 10 | #include "core/file_sys/vfs_real.h" | ||
| 11 | #include "core/hle/kernel/process.h" | 11 | #include "core/hle/kernel/process.h" |
| 12 | #include "core/loader/deconstructed_rom_directory.h" | 12 | #include "core/loader/deconstructed_rom_directory.h" |
| 13 | #include "core/loader/elf.h" | 13 | #include "core/loader/elf.h" |
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/loader/nca.h" | 15 | #include "core/loader/nca.h" |
| 16 | #include "core/loader/nro.h" | 16 | #include "core/loader/nro.h" |
| 17 | #include "core/loader/nso.h" | 17 | #include "core/loader/nso.h" |
| 18 | #include "core/loader/nsp.h" | ||
| 18 | #include "core/loader/xci.h" | 19 | #include "core/loader/xci.h" |
| 19 | 20 | ||
| 20 | namespace Loader { | 21 | namespace Loader { |
| @@ -34,6 +35,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) { | |||
| 34 | CHECK_TYPE(NCA) | 35 | CHECK_TYPE(NCA) |
| 35 | CHECK_TYPE(XCI) | 36 | CHECK_TYPE(XCI) |
| 36 | CHECK_TYPE(NAX) | 37 | CHECK_TYPE(NAX) |
| 38 | CHECK_TYPE(NSP) | ||
| 37 | 39 | ||
| 38 | #undef CHECK_TYPE | 40 | #undef CHECK_TYPE |
| 39 | 41 | ||
| @@ -59,6 +61,8 @@ FileType GuessFromFilename(const std::string& name) { | |||
| 59 | return FileType::NCA; | 61 | return FileType::NCA; |
| 60 | if (extension == "xci") | 62 | if (extension == "xci") |
| 61 | return FileType::XCI; | 63 | return FileType::XCI; |
| 64 | if (extension == "nsp") | ||
| 65 | return FileType::NSP; | ||
| 62 | 66 | ||
| 63 | return FileType::Unknown; | 67 | return FileType::Unknown; |
| 64 | } | 68 | } |
| @@ -77,6 +81,8 @@ std::string GetFileTypeString(FileType type) { | |||
| 77 | return "XCI"; | 81 | return "XCI"; |
| 78 | case FileType::NAX: | 82 | case FileType::NAX: |
| 79 | return "NAX"; | 83 | return "NAX"; |
| 84 | case FileType::NSP: | ||
| 85 | return "NSP"; | ||
| 80 | case FileType::DeconstructedRomDirectory: | 86 | case FileType::DeconstructedRomDirectory: |
| 81 | return "Directory"; | 87 | return "Directory"; |
| 82 | case FileType::Error: | 88 | case FileType::Error: |
| @@ -87,7 +93,7 @@ std::string GetFileTypeString(FileType type) { | |||
| 87 | return "unknown"; | 93 | return "unknown"; |
| 88 | } | 94 | } |
| 89 | 95 | ||
| 90 | constexpr std::array<const char*, 49> RESULT_MESSAGES{ | 96 | constexpr std::array<const char*, 58> RESULT_MESSAGES{ |
| 91 | "The operation completed successfully.", | 97 | "The operation completed successfully.", |
| 92 | "The loader requested to load is already loaded.", | 98 | "The loader requested to load is already loaded.", |
| 93 | "The operation is not implemented.", | 99 | "The operation is not implemented.", |
| @@ -137,13 +143,25 @@ constexpr std::array<const char*, 49> RESULT_MESSAGES{ | |||
| 137 | "The AES Key Generation Source could not be found.", | 143 | "The AES Key Generation Source could not be found.", |
| 138 | "The SD Save Key Source could not be found.", | 144 | "The SD Save Key Source could not be found.", |
| 139 | "The SD NCA Key Source could not be found.", | 145 | "The SD NCA Key Source could not be found.", |
| 146 | "The NSP file is missing a Program-type NCA.", | ||
| 147 | "The BKTR-type NCA has a bad BKTR header.", | ||
| 148 | "The BKTR Subsection entry is not located immediately after the Relocation entry.", | ||
| 149 | "The BKTR Subsection entry is not at the end of the media block.", | ||
| 150 | "The BKTR-type NCA has a bad Relocation block.", | ||
| 151 | "The BKTR-type NCA has a bad Subsection block.", | ||
| 152 | "The BKTR-type NCA has a bad Relocation bucket.", | ||
| 153 | "The BKTR-type NCA has a bad Subsection bucket.", | ||
| 154 | "The BKTR-type NCA is missing the base RomFS.", | ||
| 140 | }; | 155 | }; |
| 141 | 156 | ||
| 142 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { | 157 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { |
| 143 | os << RESULT_MESSAGES.at(static_cast<size_t>(status)); | 158 | os << RESULT_MESSAGES.at(static_cast<std::size_t>(status)); |
| 144 | return os; | 159 | return os; |
| 145 | } | 160 | } |
| 146 | 161 | ||
| 162 | AppLoader::AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {} | ||
| 163 | AppLoader::~AppLoader() = default; | ||
| 164 | |||
| 147 | /** | 165 | /** |
| 148 | * Get a loader for a file with a specific type | 166 | * Get a loader for a file with a specific type |
| 149 | * @param file The file to load | 167 | * @param file The file to load |
| @@ -179,6 +197,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT | |||
| 179 | case FileType::NAX: | 197 | case FileType::NAX: |
| 180 | return std::make_unique<AppLoader_NAX>(std::move(file)); | 198 | return std::make_unique<AppLoader_NAX>(std::move(file)); |
| 181 | 199 | ||
| 200 | // NX NSP (Nintendo Submission Package) file format | ||
| 201 | case FileType::NSP: | ||
| 202 | return std::make_unique<AppLoader_NSP>(std::move(file)); | ||
| 203 | |||
| 182 | // NX deconstructed ROM directory. | 204 | // NX deconstructed ROM directory. |
| 183 | case FileType::DeconstructedRomDirectory: | 205 | case FileType::DeconstructedRomDirectory: |
| 184 | return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); | 206 | return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 885fee84c..843c4bb91 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 8 | #include <iosfwd> | 7 | #include <iosfwd> |
| 9 | #include <memory> | 8 | #include <memory> |
| 10 | #include <string> | 9 | #include <string> |
| @@ -12,7 +11,6 @@ | |||
| 12 | #include <vector> | 11 | #include <vector> |
| 13 | #include <boost/optional.hpp> | 12 | #include <boost/optional.hpp> |
| 14 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 15 | #include "common/file_util.h" | ||
| 16 | #include "core/file_sys/vfs.h" | 14 | #include "core/file_sys/vfs.h" |
| 17 | #include "core/hle/kernel/object.h" | 15 | #include "core/hle/kernel/object.h" |
| 18 | 16 | ||
| @@ -31,6 +29,7 @@ enum class FileType { | |||
| 31 | NSO, | 29 | NSO, |
| 32 | NRO, | 30 | NRO, |
| 33 | NCA, | 31 | NCA, |
| 32 | NSP, | ||
| 34 | XCI, | 33 | XCI, |
| 35 | NAX, | 34 | NAX, |
| 36 | DeconstructedRomDirectory, | 35 | DeconstructedRomDirectory, |
| @@ -107,6 +106,15 @@ enum class ResultStatus : u16 { | |||
| 107 | ErrorMissingAESKeyGenerationSource, | 106 | ErrorMissingAESKeyGenerationSource, |
| 108 | ErrorMissingSDSaveKeySource, | 107 | ErrorMissingSDSaveKeySource, |
| 109 | ErrorMissingSDNCAKeySource, | 108 | ErrorMissingSDNCAKeySource, |
| 109 | ErrorNSPMissingProgramNCA, | ||
| 110 | ErrorBadBKTRHeader, | ||
| 111 | ErrorBKTRSubsectionNotAfterRelocation, | ||
| 112 | ErrorBKTRSubsectionNotAtEnd, | ||
| 113 | ErrorBadRelocationBlock, | ||
| 114 | ErrorBadSubsectionBlock, | ||
| 115 | ErrorBadRelocationBuckets, | ||
| 116 | ErrorBadSubsectionBuckets, | ||
| 117 | ErrorMissingBKTRBaseRomFS, | ||
| 110 | }; | 118 | }; |
| 111 | 119 | ||
| 112 | std::ostream& operator<<(std::ostream& os, ResultStatus status); | 120 | std::ostream& operator<<(std::ostream& os, ResultStatus status); |
| @@ -114,8 +122,8 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status); | |||
| 114 | /// Interface for loading an application | 122 | /// Interface for loading an application |
| 115 | class AppLoader : NonCopyable { | 123 | class AppLoader : NonCopyable { |
| 116 | public: | 124 | public: |
| 117 | explicit AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {} | 125 | explicit AppLoader(FileSys::VirtualFile file); |
| 118 | virtual ~AppLoader() {} | 126 | virtual ~AppLoader(); |
| 119 | 127 | ||
| 120 | /** | 128 | /** |
| 121 | * Returns the type of this file | 129 | * Returns the type of this file |
| @@ -197,13 +205,22 @@ public: | |||
| 197 | } | 205 | } |
| 198 | 206 | ||
| 199 | /** | 207 | /** |
| 200 | * Get the update RomFS of the application | 208 | * Get whether or not updates can be applied to the RomFS. |
| 201 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer | 209 | * By default, this is true, however for formats where it cannot be guaranteed that the RomFS is |
| 202 | * @param file The file containing the RomFS | 210 | * the base game it should be set to false. |
| 203 | * @return ResultStatus result of function | 211 | * @return bool whether or not updatable. |
| 204 | */ | 212 | */ |
| 205 | virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) { | 213 | virtual bool IsRomFSUpdatable() const { |
| 206 | return ResultStatus::ErrorNotImplemented; | 214 | return true; |
| 215 | } | ||
| 216 | |||
| 217 | /** | ||
| 218 | * Gets the difference between the start of the IVFC header and the start of level 6 (RomFS) | ||
| 219 | * data. Needed for bktr patching. | ||
| 220 | * @return IVFC offset for romfs. | ||
| 221 | */ | ||
| 222 | virtual u64 ReadRomFSIVFCOffset() const { | ||
| 223 | return 0; | ||
| 207 | } | 224 | } |
| 208 | 225 | ||
| 209 | /** | 226 | /** |
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index b46d81c02..5d4380684 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp | |||
| @@ -11,6 +11,20 @@ | |||
| 11 | #include "core/loader/nca.h" | 11 | #include "core/loader/nca.h" |
| 12 | 12 | ||
| 13 | namespace Loader { | 13 | namespace Loader { |
| 14 | namespace { | ||
| 15 | FileType IdentifyTypeImpl(const FileSys::NAX& nax) { | ||
| 16 | if (nax.GetStatus() != ResultStatus::Success) { | ||
| 17 | return FileType::Error; | ||
| 18 | } | ||
| 19 | |||
| 20 | const auto nca = nax.AsNCA(); | ||
| 21 | if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) { | ||
| 22 | return FileType::Error; | ||
| 23 | } | ||
| 24 | |||
| 25 | return FileType::NAX; | ||
| 26 | } | ||
| 27 | } // Anonymous namespace | ||
| 14 | 28 | ||
| 15 | AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) | 29 | AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) |
| 16 | : AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)), | 30 | : AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)), |
| @@ -19,14 +33,12 @@ AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) | |||
| 19 | AppLoader_NAX::~AppLoader_NAX() = default; | 33 | AppLoader_NAX::~AppLoader_NAX() = default; |
| 20 | 34 | ||
| 21 | FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { | 35 | FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { |
| 22 | FileSys::NAX nax(file); | 36 | const FileSys::NAX nax(file); |
| 23 | 37 | return IdentifyTypeImpl(nax); | |
| 24 | if (nax.GetStatus() == ResultStatus::Success && nax.AsNCA() != nullptr && | 38 | } |
| 25 | nax.AsNCA()->GetStatus() == ResultStatus::Success) { | ||
| 26 | return FileType::NAX; | ||
| 27 | } | ||
| 28 | 39 | ||
| 29 | return FileType::Error; | 40 | FileType AppLoader_NAX::GetFileType() { |
| 41 | return IdentifyTypeImpl(*nax); | ||
| 30 | } | 42 | } |
| 31 | 43 | ||
| 32 | ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 44 | ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) { |
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index 4dbae2918..56605fe45 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h | |||
| @@ -31,9 +31,7 @@ public: | |||
| 31 | */ | 31 | */ |
| 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 33 | 33 | ||
| 34 | FileType GetFileType() override { | 34 | FileType GetFileType() override; |
| 35 | return IdentifyType(file); | ||
| 36 | } | ||
| 37 | 35 | ||
| 38 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 36 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 39 | 37 | ||
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index c036a8a1c..6aaffae59 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -48,7 +48,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 48 | if (exefs == nullptr) | 48 | if (exefs == nullptr) |
| 49 | return ResultStatus::ErrorNoExeFS; | 49 | return ResultStatus::ErrorNoExeFS; |
| 50 | 50 | ||
| 51 | directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs); | 51 | directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); |
| 52 | 52 | ||
| 53 | const auto load_result = directory_loader->Load(process); | 53 | const auto load_result = directory_loader->Load(process); |
| 54 | if (load_result != ResultStatus::Success) | 54 | if (load_result != ResultStatus::Success) |
| @@ -71,6 +71,12 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { | |||
| 71 | return ResultStatus::Success; | 71 | return ResultStatus::Success; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | u64 AppLoader_NCA::ReadRomFSIVFCOffset() const { | ||
| 75 | if (nca == nullptr) | ||
| 76 | return 0; | ||
| 77 | return nca->GetBaseIVFCOffset(); | ||
| 78 | } | ||
| 79 | |||
| 74 | ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | 80 | ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { |
| 75 | if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) | 81 | if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) |
| 76 | return ResultStatus::ErrorNotInitialized; | 82 | return ResultStatus::ErrorNotInitialized; |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 326f84857..10be197c4 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -37,6 +37,7 @@ public: | |||
| 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 38 | 38 | ||
| 39 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 39 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 40 | u64 ReadRomFSIVFCOffset() const override; | ||
| 40 | ResultStatus ReadProgramId(u64& out_program_id) override; | 41 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 41 | 42 | ||
| 42 | private: | 43 | private: |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 77026b850..c49ec34ab 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -191,7 +191,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 191 | process->svc_access_mask.set(); | 191 | process->svc_access_mask.set(); |
| 192 | process->resource_limit = | 192 | process->resource_limit = |
| 193 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 193 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 194 | process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | 194 | process->Run(base_addr, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); |
| 195 | 195 | ||
| 196 | is_loaded = true; | 196 | is_loaded = true; |
| 197 | return ResultStatus::Success; | 197 | return ResultStatus::Success; |
| @@ -232,4 +232,9 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { | |||
| 232 | title = nacp->GetApplicationName(); | 232 | title = nacp->GetApplicationName(); |
| 233 | return ResultStatus::Success; | 233 | return ResultStatus::Success; |
| 234 | } | 234 | } |
| 235 | |||
| 236 | bool AppLoader_NRO::IsRomFSUpdatable() const { | ||
| 237 | return false; | ||
| 238 | } | ||
| 239 | |||
| 235 | } // namespace Loader | 240 | } // namespace Loader |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index bb01c9e25..96d2de305 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -39,6 +39,7 @@ public: | |||
| 39 | ResultStatus ReadProgramId(u64& out_program_id) override; | 39 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 41 | ResultStatus ReadTitle(std::string& title) override; | 41 | ResultStatus ReadTitle(std::string& title) override; |
| 42 | bool IsRomFSUpdatable() const override; | ||
| 42 | 43 | ||
| 43 | private: | 44 | private: |
| 44 | bool LoadNro(FileSys::VirtualFile file, VAddr load_base); | 45 | bool LoadNro(FileSys::VirtualFile file, VAddr load_base); |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 082a95d40..3c6306818 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -157,7 +157,8 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 157 | process->svc_access_mask.set(); | 157 | process->svc_access_mask.set(); |
| 158 | process->resource_limit = | 158 | process->resource_limit = |
| 159 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 159 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 160 | process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | 160 | process->Run(Memory::PROCESS_IMAGE_VADDR, Kernel::THREADPRIO_DEFAULT, |
| 161 | Memory::DEFAULT_STACK_SIZE); | ||
| 161 | 162 | ||
| 162 | is_loaded = true; | 163 | is_loaded = true; |
| 163 | return ResultStatus::Success; | 164 | return ResultStatus::Success; |
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp new file mode 100644 index 000000000..291a9876d --- /dev/null +++ b/src/core/loader/nsp.cpp | |||
| @@ -0,0 +1,125 @@ | |||
| 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 <vector> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "core/file_sys/card_image.h" | ||
| 9 | #include "core/file_sys/content_archive.h" | ||
| 10 | #include "core/file_sys/control_metadata.h" | ||
| 11 | #include "core/file_sys/nca_metadata.h" | ||
| 12 | #include "core/file_sys/patch_manager.h" | ||
| 13 | #include "core/file_sys/registered_cache.h" | ||
| 14 | #include "core/file_sys/romfs.h" | ||
| 15 | #include "core/file_sys/submission_package.h" | ||
| 16 | #include "core/hle/kernel/process.h" | ||
| 17 | #include "core/loader/deconstructed_rom_directory.h" | ||
| 18 | #include "core/loader/nca.h" | ||
| 19 | #include "core/loader/nsp.h" | ||
| 20 | |||
| 21 | namespace Loader { | ||
| 22 | |||
| 23 | AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) | ||
| 24 | : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)), | ||
| 25 | title_id(nsp->GetProgramTitleID()) { | ||
| 26 | |||
| 27 | if (nsp->GetStatus() != ResultStatus::Success) | ||
| 28 | return; | ||
| 29 | if (nsp->IsExtractedType()) | ||
| 30 | return; | ||
| 31 | |||
| 32 | const auto control_nca = | ||
| 33 | nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); | ||
| 34 | if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) | ||
| 35 | return; | ||
| 36 | |||
| 37 | std::tie(nacp_file, icon_file) = | ||
| 38 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); | ||
| 39 | } | ||
| 40 | |||
| 41 | AppLoader_NSP::~AppLoader_NSP() = default; | ||
| 42 | |||
| 43 | FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { | ||
| 44 | FileSys::NSP nsp(file); | ||
| 45 | |||
| 46 | if (nsp.GetStatus() == ResultStatus::Success) { | ||
| 47 | // Extracted Type case | ||
| 48 | if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && | ||
| 49 | FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) { | ||
| 50 | return FileType::NSP; | ||
| 51 | } | ||
| 52 | |||
| 53 | // Non-Ectracted Type case | ||
| 54 | if (!nsp.IsExtractedType() && | ||
| 55 | nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && | ||
| 56 | AppLoader_NCA::IdentifyType(nsp.GetNCAFile( | ||
| 57 | nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) { | ||
| 58 | return FileType::NSP; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | return FileType::Error; | ||
| 63 | } | ||
| 64 | |||
| 65 | ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) { | ||
| 66 | if (is_loaded) { | ||
| 67 | return ResultStatus::ErrorAlreadyLoaded; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (nsp->IsExtractedType()) { | ||
| 71 | secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); | ||
| 72 | } else { | ||
| 73 | if (title_id == 0) | ||
| 74 | return ResultStatus::ErrorNSPMissingProgramNCA; | ||
| 75 | |||
| 76 | secondary_loader = std::make_unique<AppLoader_NCA>( | ||
| 77 | nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); | ||
| 78 | |||
| 79 | if (nsp->GetStatus() != ResultStatus::Success) | ||
| 80 | return nsp->GetStatus(); | ||
| 81 | |||
| 82 | if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) | ||
| 83 | return nsp->GetProgramStatus(title_id); | ||
| 84 | |||
| 85 | if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { | ||
| 86 | if (!Core::Crypto::KeyManager::KeyFileExists(false)) | ||
| 87 | return ResultStatus::ErrorMissingProductionKeyFile; | ||
| 88 | return ResultStatus::ErrorNSPMissingProgramNCA; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | const auto result = secondary_loader->Load(process); | ||
| 93 | if (result != ResultStatus::Success) | ||
| 94 | return result; | ||
| 95 | |||
| 96 | is_loaded = true; | ||
| 97 | |||
| 98 | return ResultStatus::Success; | ||
| 99 | } | ||
| 100 | |||
| 101 | ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) { | ||
| 102 | return secondary_loader->ReadRomFS(dir); | ||
| 103 | } | ||
| 104 | |||
| 105 | ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { | ||
| 106 | if (title_id == 0) | ||
| 107 | return ResultStatus::ErrorNotInitialized; | ||
| 108 | out_program_id = title_id; | ||
| 109 | return ResultStatus::Success; | ||
| 110 | } | ||
| 111 | |||
| 112 | ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) { | ||
| 113 | if (icon_file == nullptr) | ||
| 114 | return ResultStatus::ErrorNoControl; | ||
| 115 | buffer = icon_file->ReadAllBytes(); | ||
| 116 | return ResultStatus::Success; | ||
| 117 | } | ||
| 118 | |||
| 119 | ResultStatus AppLoader_NSP::ReadTitle(std::string& title) { | ||
| 120 | if (nacp_file == nullptr) | ||
| 121 | return ResultStatus::ErrorNoControl; | ||
| 122 | title = nacp_file->GetApplicationName(); | ||
| 123 | return ResultStatus::Success; | ||
| 124 | } | ||
| 125 | } // namespace Loader | ||
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h new file mode 100644 index 000000000..7ef810499 --- /dev/null +++ b/src/core/loader/nsp.h | |||
| @@ -0,0 +1,54 @@ | |||
| 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 <memory> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | class NACP; | ||
| 14 | class NSP; | ||
| 15 | } // namespace FileSys | ||
| 16 | |||
| 17 | namespace Loader { | ||
| 18 | |||
| 19 | class AppLoader_NCA; | ||
| 20 | |||
| 21 | /// Loads an XCI file | ||
| 22 | class AppLoader_NSP final : public AppLoader { | ||
| 23 | public: | ||
| 24 | explicit AppLoader_NSP(FileSys::VirtualFile file); | ||
| 25 | ~AppLoader_NSP() override; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Returns the type of the file | ||
| 29 | * @param file std::shared_ptr<VfsFile> open file | ||
| 30 | * @return FileType found, or FileType::Error if this loader doesn't know it | ||
| 31 | */ | ||
| 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); | ||
| 33 | |||
| 34 | FileType GetFileType() override { | ||
| 35 | return IdentifyType(file); | ||
| 36 | } | ||
| 37 | |||
| 38 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||
| 39 | |||
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||
| 41 | ResultStatus ReadProgramId(u64& out_program_id) override; | ||
| 42 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | ||
| 43 | ResultStatus ReadTitle(std::string& title) override; | ||
| 44 | |||
| 45 | private: | ||
| 46 | std::unique_ptr<FileSys::NSP> nsp; | ||
| 47 | std::unique_ptr<AppLoader> secondary_loader; | ||
| 48 | |||
| 49 | FileSys::VirtualFile icon_file; | ||
| 50 | std::shared_ptr<FileSys::NACP> nacp_file; | ||
| 51 | u64 title_id; | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // namespace Loader | ||
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 9dc4d1f35..16509229f 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -8,7 +8,9 @@ | |||
| 8 | #include "core/file_sys/card_image.h" | 8 | #include "core/file_sys/card_image.h" |
| 9 | #include "core/file_sys/content_archive.h" | 9 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/control_metadata.h" | 10 | #include "core/file_sys/control_metadata.h" |
| 11 | #include "core/file_sys/patch_manager.h" | ||
| 11 | #include "core/file_sys/romfs.h" | 12 | #include "core/file_sys/romfs.h" |
| 13 | #include "core/file_sys/submission_package.h" | ||
| 12 | #include "core/hle/kernel/process.h" | 14 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/loader/nca.h" | 15 | #include "core/loader/nca.h" |
| 14 | #include "core/loader/xci.h" | 16 | #include "core/loader/xci.h" |
| @@ -17,25 +19,16 @@ namespace Loader { | |||
| 17 | 19 | ||
| 18 | AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | 20 | AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) |
| 19 | : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), | 21 | : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), |
| 20 | nca_loader(std::make_unique<AppLoader_NCA>( | 22 | nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { |
| 21 | xci->GetNCAFileByType(FileSys::NCAContentType::Program))) { | ||
| 22 | if (xci->GetStatus() != ResultStatus::Success) | 23 | if (xci->GetStatus() != ResultStatus::Success) |
| 23 | return; | 24 | return; |
| 25 | |||
| 24 | const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); | 26 | const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); |
| 25 | if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) | 27 | if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) |
| 26 | return; | 28 | return; |
| 27 | const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); | 29 | |
| 28 | if (romfs == nullptr) | 30 | std::tie(nacp_file, icon_file) = |
| 29 | return; | 31 | FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); |
| 30 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||
| 31 | icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); | ||
| 32 | if (icon_file != nullptr) | ||
| 33 | break; | ||
| 34 | } | ||
| 35 | const auto nacp_raw = romfs->GetFile("control.nacp"); | ||
| 36 | if (nacp_raw == nullptr) | ||
| 37 | return; | ||
| 38 | nacp_file = std::make_shared<FileSys::NACP>(nacp_raw); | ||
| 39 | } | 32 | } |
| 40 | 33 | ||
| 41 | AppLoader_XCI::~AppLoader_XCI() = default; | 34 | AppLoader_XCI::~AppLoader_XCI() = default; |
| @@ -64,11 +57,11 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 64 | if (xci->GetProgramNCAStatus() != ResultStatus::Success) | 57 | if (xci->GetProgramNCAStatus() != ResultStatus::Success) |
| 65 | return xci->GetProgramNCAStatus(); | 58 | return xci->GetProgramNCAStatus(); |
| 66 | 59 | ||
| 67 | const auto nca = xci->GetNCAFileByType(FileSys::NCAContentType::Program); | 60 | const auto nca = xci->GetProgramNCA(); |
| 68 | if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false)) | 61 | if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false)) |
| 69 | return ResultStatus::ErrorMissingProductionKeyFile; | 62 | return ResultStatus::ErrorMissingProductionKeyFile; |
| 70 | 63 | ||
| 71 | auto result = nca_loader->Load(process); | 64 | const auto result = nca_loader->Load(process); |
| 72 | if (result != ResultStatus::Success) | 65 | if (result != ResultStatus::Success) |
| 73 | return result; | 66 | return result; |
| 74 | 67 | ||
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 0e4e0157c..316b46820 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -370,16 +370,16 @@ u64 Read64(const VAddr addr) { | |||
| 370 | } | 370 | } |
| 371 | 371 | ||
| 372 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | 372 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, |
| 373 | const size_t size) { | 373 | const std::size_t size) { |
| 374 | auto& page_table = process.vm_manager.page_table; | 374 | auto& page_table = process.vm_manager.page_table; |
| 375 | 375 | ||
| 376 | size_t remaining_size = size; | 376 | std::size_t remaining_size = size; |
| 377 | size_t page_index = src_addr >> PAGE_BITS; | 377 | std::size_t page_index = src_addr >> PAGE_BITS; |
| 378 | size_t page_offset = src_addr & PAGE_MASK; | 378 | std::size_t page_offset = src_addr & PAGE_MASK; |
| 379 | 379 | ||
| 380 | while (remaining_size > 0) { | 380 | while (remaining_size > 0) { |
| 381 | const size_t copy_amount = | 381 | const std::size_t copy_amount = |
| 382 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 382 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 383 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 383 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 384 | 384 | ||
| 385 | switch (page_table.attributes[page_index]) { | 385 | switch (page_table.attributes[page_index]) { |
| @@ -414,7 +414,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_ | |||
| 414 | } | 414 | } |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { | 417 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { |
| 418 | ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); | 418 | ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); |
| 419 | } | 419 | } |
| 420 | 420 | ||
| @@ -435,15 +435,15 @@ void Write64(const VAddr addr, const u64 data) { | |||
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | 437 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, |
| 438 | const size_t size) { | 438 | const std::size_t size) { |
| 439 | auto& page_table = process.vm_manager.page_table; | 439 | auto& page_table = process.vm_manager.page_table; |
| 440 | size_t remaining_size = size; | 440 | std::size_t remaining_size = size; |
| 441 | size_t page_index = dest_addr >> PAGE_BITS; | 441 | std::size_t page_index = dest_addr >> PAGE_BITS; |
| 442 | size_t page_offset = dest_addr & PAGE_MASK; | 442 | std::size_t page_offset = dest_addr & PAGE_MASK; |
| 443 | 443 | ||
| 444 | while (remaining_size > 0) { | 444 | while (remaining_size > 0) { |
| 445 | const size_t copy_amount = | 445 | const std::size_t copy_amount = |
| 446 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 446 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 447 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 447 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 448 | 448 | ||
| 449 | switch (page_table.attributes[page_index]) { | 449 | switch (page_table.attributes[page_index]) { |
| @@ -477,19 +477,19 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi | |||
| 477 | } | 477 | } |
| 478 | } | 478 | } |
| 479 | 479 | ||
| 480 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { | 480 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { |
| 481 | WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); | 481 | WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); |
| 482 | } | 482 | } |
| 483 | 483 | ||
| 484 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) { | 484 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { |
| 485 | auto& page_table = process.vm_manager.page_table; | 485 | auto& page_table = process.vm_manager.page_table; |
| 486 | size_t remaining_size = size; | 486 | std::size_t remaining_size = size; |
| 487 | size_t page_index = dest_addr >> PAGE_BITS; | 487 | std::size_t page_index = dest_addr >> PAGE_BITS; |
| 488 | size_t page_offset = dest_addr & PAGE_MASK; | 488 | std::size_t page_offset = dest_addr & PAGE_MASK; |
| 489 | 489 | ||
| 490 | while (remaining_size > 0) { | 490 | while (remaining_size > 0) { |
| 491 | const size_t copy_amount = | 491 | const std::size_t copy_amount = |
| 492 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 492 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 493 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 493 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 494 | 494 | ||
| 495 | switch (page_table.attributes[page_index]) { | 495 | switch (page_table.attributes[page_index]) { |
| @@ -522,15 +522,16 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size | |||
| 522 | } | 522 | } |
| 523 | } | 523 | } |
| 524 | 524 | ||
| 525 | void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) { | 525 | void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, |
| 526 | const std::size_t size) { | ||
| 526 | auto& page_table = process.vm_manager.page_table; | 527 | auto& page_table = process.vm_manager.page_table; |
| 527 | size_t remaining_size = size; | 528 | std::size_t remaining_size = size; |
| 528 | size_t page_index = src_addr >> PAGE_BITS; | 529 | std::size_t page_index = src_addr >> PAGE_BITS; |
| 529 | size_t page_offset = src_addr & PAGE_MASK; | 530 | std::size_t page_offset = src_addr & PAGE_MASK; |
| 530 | 531 | ||
| 531 | while (remaining_size > 0) { | 532 | while (remaining_size > 0) { |
| 532 | const size_t copy_amount = | 533 | const std::size_t copy_amount = |
| 533 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 534 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 534 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 535 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 535 | 536 | ||
| 536 | switch (page_table.attributes[page_index]) { | 537 | switch (page_table.attributes[page_index]) { |
| @@ -565,7 +566,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, | |||
| 565 | } | 566 | } |
| 566 | } | 567 | } |
| 567 | 568 | ||
| 568 | void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) { | 569 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { |
| 569 | CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); | 570 | CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); |
| 570 | } | 571 | } |
| 571 | 572 | ||
diff --git a/src/core/memory.h b/src/core/memory.h index f06e04a75..2a27c0251 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -22,11 +22,11 @@ namespace Memory { | |||
| 22 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can | 22 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can |
| 23 | * be mapped. | 23 | * be mapped. |
| 24 | */ | 24 | */ |
| 25 | constexpr size_t PAGE_BITS = 12; | 25 | constexpr std::size_t PAGE_BITS = 12; |
| 26 | constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; | 26 | constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; |
| 27 | constexpr u64 PAGE_MASK = PAGE_SIZE - 1; | 27 | constexpr u64 PAGE_MASK = PAGE_SIZE - 1; |
| 28 | constexpr size_t ADDRESS_SPACE_BITS = 36; | 28 | constexpr std::size_t ADDRESS_SPACE_BITS = 36; |
| 29 | constexpr size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS); | 29 | constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS); |
| 30 | 30 | ||
| 31 | enum class PageType : u8 { | 31 | enum class PageType : u8 { |
| 32 | /// Page is unmapped and should cause an access error. | 32 | /// Page is unmapped and should cause an access error. |
| @@ -154,13 +154,13 @@ void Write16(VAddr addr, u16 data); | |||
| 154 | void Write32(VAddr addr, u32 data); | 154 | void Write32(VAddr addr, u32 data); |
| 155 | void Write64(VAddr addr, u64 data); | 155 | void Write64(VAddr addr, u64 data); |
| 156 | 156 | ||
| 157 | void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size); | 157 | void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size); |
| 158 | void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size); | 158 | void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); |
| 159 | void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | 159 | void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, |
| 160 | size_t size); | 160 | std::size_t size); |
| 161 | void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size); | 161 | void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); |
| 162 | void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size); | 162 | void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); |
| 163 | void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); | 163 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); |
| 164 | 164 | ||
| 165 | u8* GetPointer(VAddr vaddr); | 165 | u8* GetPointer(VAddr vaddr); |
| 166 | 166 | ||
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h index e8ea19333..0269c7ff1 100644 --- a/src/core/memory_hook.h +++ b/src/core/memory_hook.h | |||
| @@ -32,14 +32,14 @@ public: | |||
| 32 | virtual boost::optional<u32> Read32(VAddr addr) = 0; | 32 | virtual boost::optional<u32> Read32(VAddr addr) = 0; |
| 33 | virtual boost::optional<u64> Read64(VAddr addr) = 0; | 33 | virtual boost::optional<u64> Read64(VAddr addr) = 0; |
| 34 | 34 | ||
| 35 | virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) = 0; | 35 | virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; |
| 36 | 36 | ||
| 37 | virtual bool Write8(VAddr addr, u8 data) = 0; | 37 | virtual bool Write8(VAddr addr, u8 data) = 0; |
| 38 | virtual bool Write16(VAddr addr, u16 data) = 0; | 38 | virtual bool Write16(VAddr addr, u16 data) = 0; |
| 39 | virtual bool Write32(VAddr addr, u32 data) = 0; | 39 | virtual bool Write32(VAddr addr, u32 data) = 0; |
| 40 | virtual bool Write64(VAddr addr, u64 data) = 0; | 40 | virtual bool Write64(VAddr addr, u64 data) = 0; |
| 41 | 41 | ||
| 42 | virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) = 0; | 42 | virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | using MemoryHookPointer = std::shared_ptr<MemoryHook>; | 45 | using MemoryHookPointer = std::shared_ptr<MemoryHook>; |
diff --git a/src/core/settings.h b/src/core/settings.h index ed6f42471..0318d019c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| @@ -120,6 +121,7 @@ struct Values { | |||
| 120 | std::array<std::string, NativeAnalog::NumAnalogs> analogs; | 121 | std::array<std::string, NativeAnalog::NumAnalogs> analogs; |
| 121 | std::string motion_device; | 122 | std::string motion_device; |
| 122 | std::string touch_device; | 123 | std::string touch_device; |
| 124 | std::atomic_bool is_device_reload_pending{true}; | ||
| 123 | 125 | ||
| 124 | // Core | 126 | // Core |
| 125 | bool use_cpu_jit; | 127 | bool use_cpu_jit; |
| @@ -127,6 +129,8 @@ struct Values { | |||
| 127 | 129 | ||
| 128 | // Data Storage | 130 | // Data Storage |
| 129 | bool use_virtual_sd; | 131 | bool use_virtual_sd; |
| 132 | std::string nand_dir; | ||
| 133 | std::string sdmc_dir; | ||
| 130 | 134 | ||
| 131 | // Renderer | 135 | // Renderer |
| 132 | float resolution_factor; | 136 | float resolution_factor; |
| @@ -144,6 +148,7 @@ struct Values { | |||
| 144 | 148 | ||
| 145 | // Audio | 149 | // Audio |
| 146 | std::string sink_id; | 150 | std::string sink_id; |
| 151 | bool enable_audio_stretching; | ||
| 147 | std::string audio_device_id; | 152 | std::string audio_device_id; |
| 148 | float volume; | 153 | float volume; |
| 149 | 154 | ||
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 65571b948..b0df154ca 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -7,6 +7,8 @@ | |||
| 7 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 8 | 8 | ||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/file_sys/control_metadata.h" | ||
| 11 | #include "core/file_sys/patch_manager.h" | ||
| 10 | #include "core/loader/loader.h" | 12 | #include "core/loader/loader.h" |
| 11 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 12 | #include "core/telemetry_session.h" | 14 | #include "core/telemetry_session.h" |
| @@ -88,12 +90,28 @@ TelemetrySession::TelemetrySession() { | |||
| 88 | std::chrono::system_clock::now().time_since_epoch()) | 90 | std::chrono::system_clock::now().time_since_epoch()) |
| 89 | .count()}; | 91 | .count()}; |
| 90 | AddField(Telemetry::FieldType::Session, "Init_Time", init_time); | 92 | AddField(Telemetry::FieldType::Session, "Init_Time", init_time); |
| 91 | std::string program_name; | 93 | |
| 92 | const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)}; | 94 | u64 program_id{}; |
| 95 | const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)}; | ||
| 93 | if (res == Loader::ResultStatus::Success) { | 96 | if (res == Loader::ResultStatus::Success) { |
| 94 | AddField(Telemetry::FieldType::Session, "ProgramName", program_name); | 97 | AddField(Telemetry::FieldType::Session, "ProgramId", program_id); |
| 98 | |||
| 99 | std::string name; | ||
| 100 | System::GetInstance().GetAppLoader().ReadTitle(name); | ||
| 101 | |||
| 102 | if (name.empty()) { | ||
| 103 | auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); | ||
| 104 | if (nacp != nullptr) | ||
| 105 | name = nacp->GetApplicationName(); | ||
| 106 | } | ||
| 107 | |||
| 108 | if (!name.empty()) | ||
| 109 | AddField(Telemetry::FieldType::Session, "ProgramName", name); | ||
| 95 | } | 110 | } |
| 96 | 111 | ||
| 112 | AddField(Telemetry::FieldType::Session, "ProgramFormat", | ||
| 113 | static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType())); | ||
| 114 | |||
| 97 | // Log application information | 115 | // Log application information |
| 98 | Telemetry::AppendBuildInfo(field_collection); | 116 | Telemetry::AppendBuildInfo(field_collection); |
| 99 | 117 | ||
| @@ -102,6 +120,9 @@ TelemetrySession::TelemetrySession() { | |||
| 102 | Telemetry::AppendOSInfo(field_collection); | 120 | Telemetry::AppendOSInfo(field_collection); |
| 103 | 121 | ||
| 104 | // Log user configuration information | 122 | // Log user configuration information |
| 123 | AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id); | ||
| 124 | AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", | ||
| 125 | Settings::values.enable_audio_stretching); | ||
| 105 | AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); | 126 | AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); |
| 106 | AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", | 127 | AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", |
| 107 | Settings::values.use_multi_core); | 128 | Settings::values.use_multi_core); |
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp index af032f0c9..73cacb47f 100644 --- a/src/core/tracer/recorder.cpp +++ b/src/core/tracer/recorder.cpp | |||
| @@ -76,7 +76,7 @@ void Recorder::Finish(const std::string& filename) { | |||
| 76 | try { | 76 | try { |
| 77 | // Open file and write header | 77 | // Open file and write header |
| 78 | FileUtil::IOFile file(filename, "wb"); | 78 | FileUtil::IOFile file(filename, "wb"); |
| 79 | size_t written = file.WriteObject(header); | 79 | std::size_t written = file.WriteObject(header); |
| 80 | if (written != 1 || file.Tell() != initial.gpu_registers) | 80 | if (written != 1 || file.Tell() != initial.gpu_registers) |
| 81 | throw "Failed to write header"; | 81 | throw "Failed to write header"; |
| 82 | 82 | ||
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b12623d55..37f572853 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 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 <thread> | ||
| 6 | #include "common/param_package.h" | 7 | #include "common/param_package.h" |
| 7 | #include "input_common/analog_from_button.h" | 8 | #include "input_common/analog_from_button.h" |
| 8 | #include "input_common/keyboard.h" | 9 | #include "input_common/keyboard.h" |
| @@ -17,6 +18,10 @@ namespace InputCommon { | |||
| 17 | static std::shared_ptr<Keyboard> keyboard; | 18 | static std::shared_ptr<Keyboard> keyboard; |
| 18 | static std::shared_ptr<MotionEmu> motion_emu; | 19 | static std::shared_ptr<MotionEmu> motion_emu; |
| 19 | 20 | ||
| 21 | #ifdef HAVE_SDL2 | ||
| 22 | static std::thread poll_thread; | ||
| 23 | #endif | ||
| 24 | |||
| 20 | void Init() { | 25 | void Init() { |
| 21 | keyboard = std::make_shared<Keyboard>(); | 26 | keyboard = std::make_shared<Keyboard>(); |
| 22 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); | 27 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); |
| @@ -30,6 +35,12 @@ void Init() { | |||
| 30 | #endif | 35 | #endif |
| 31 | } | 36 | } |
| 32 | 37 | ||
| 38 | void StartJoystickEventHandler() { | ||
| 39 | #ifdef HAVE_SDL2 | ||
| 40 | poll_thread = std::thread(SDL::PollLoop); | ||
| 41 | #endif | ||
| 42 | } | ||
| 43 | |||
| 33 | void Shutdown() { | 44 | void Shutdown() { |
| 34 | Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); | 45 | Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); |
| 35 | keyboard.reset(); | 46 | keyboard.reset(); |
| @@ -39,6 +50,7 @@ void Shutdown() { | |||
| 39 | 50 | ||
| 40 | #ifdef HAVE_SDL2 | 51 | #ifdef HAVE_SDL2 |
| 41 | SDL::Shutdown(); | 52 | SDL::Shutdown(); |
| 53 | poll_thread.join(); | ||
| 42 | #endif | 54 | #endif |
| 43 | } | 55 | } |
| 44 | 56 | ||
diff --git a/src/input_common/main.h b/src/input_common/main.h index 77a0ce90b..9eb13106e 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h | |||
| @@ -20,6 +20,8 @@ void Init(); | |||
| 20 | /// Deregisters all built-in input device factories and shuts them down. | 20 | /// Deregisters all built-in input device factories and shuts them down. |
| 21 | void Shutdown(); | 21 | void Shutdown(); |
| 22 | 22 | ||
| 23 | void StartJoystickEventHandler(); | ||
| 24 | |||
| 23 | class Keyboard; | 25 | class Keyboard; |
| 24 | 26 | ||
| 25 | /// Gets the keyboard button device factory. | 27 | /// Gets the keyboard button device factory. |
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp index d1b960fd7..faf3c1fa3 100644 --- a/src/input_common/sdl/sdl.cpp +++ b/src/input_common/sdl/sdl.cpp | |||
| @@ -2,15 +2,24 @@ | |||
| 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 <atomic> | ||
| 5 | #include <cmath> | 7 | #include <cmath> |
| 8 | #include <functional> | ||
| 9 | #include <iterator> | ||
| 10 | #include <mutex> | ||
| 6 | #include <string> | 11 | #include <string> |
| 12 | #include <thread> | ||
| 7 | #include <tuple> | 13 | #include <tuple> |
| 8 | #include <unordered_map> | 14 | #include <unordered_map> |
| 9 | #include <utility> | 15 | #include <utility> |
| 16 | #include <vector> | ||
| 10 | #include <SDL.h> | 17 | #include <SDL.h> |
| 18 | #include "common/assert.h" | ||
| 11 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 12 | #include "common/math_util.h" | 20 | #include "common/math_util.h" |
| 13 | #include "common/param_package.h" | 21 | #include "common/param_package.h" |
| 22 | #include "common/threadsafe_queue.h" | ||
| 14 | #include "input_common/main.h" | 23 | #include "input_common/main.h" |
| 15 | #include "input_common/sdl/sdl.h" | 24 | #include "input_common/sdl/sdl.h" |
| 16 | 25 | ||
| @@ -21,33 +30,51 @@ namespace SDL { | |||
| 21 | class SDLJoystick; | 30 | class SDLJoystick; |
| 22 | class SDLButtonFactory; | 31 | class SDLButtonFactory; |
| 23 | class SDLAnalogFactory; | 32 | class SDLAnalogFactory; |
| 24 | static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list; | 33 | |
| 34 | /// Map of GUID of a list of corresponding virtual Joysticks | ||
| 35 | static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; | ||
| 36 | static std::mutex joystick_map_mutex; | ||
| 37 | |||
| 25 | static std::shared_ptr<SDLButtonFactory> button_factory; | 38 | static std::shared_ptr<SDLButtonFactory> button_factory; |
| 26 | static std::shared_ptr<SDLAnalogFactory> analog_factory; | 39 | static std::shared_ptr<SDLAnalogFactory> analog_factory; |
| 27 | 40 | ||
| 28 | static bool initialized = false; | 41 | /// Used by the Pollers during config |
| 42 | static std::atomic<bool> polling; | ||
| 43 | static Common::SPSCQueue<SDL_Event> event_queue; | ||
| 44 | |||
| 45 | static std::atomic<bool> initialized = false; | ||
| 46 | |||
| 47 | static std::string GetGUID(SDL_Joystick* joystick) { | ||
| 48 | SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); | ||
| 49 | char guid_str[33]; | ||
| 50 | SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); | ||
| 51 | return guid_str; | ||
| 52 | } | ||
| 29 | 53 | ||
| 30 | class SDLJoystick { | 54 | class SDLJoystick { |
| 31 | public: | 55 | public: |
| 32 | explicit SDLJoystick(int joystick_index) | 56 | SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, |
| 33 | : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { | 57 | decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) |
| 34 | if (!joystick) { | 58 | : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {} |
| 35 | LOG_ERROR(Input, "failed to open joystick {}", joystick_index); | 59 | |
| 36 | } | 60 | void SetButton(int button, bool value) { |
| 61 | std::lock_guard<std::mutex> lock(mutex); | ||
| 62 | state.buttons[button] = value; | ||
| 37 | } | 63 | } |
| 38 | 64 | ||
| 39 | bool GetButton(int button) const { | 65 | bool GetButton(int button) const { |
| 40 | if (!joystick) | 66 | std::lock_guard<std::mutex> lock(mutex); |
| 41 | return {}; | 67 | return state.buttons.at(button); |
| 42 | SDL_JoystickUpdate(); | 68 | } |
| 43 | return SDL_JoystickGetButton(joystick.get(), button) == 1; | 69 | |
| 70 | void SetAxis(int axis, Sint16 value) { | ||
| 71 | std::lock_guard<std::mutex> lock(mutex); | ||
| 72 | state.axes[axis] = value; | ||
| 44 | } | 73 | } |
| 45 | 74 | ||
| 46 | float GetAxis(int axis) const { | 75 | float GetAxis(int axis) const { |
| 47 | if (!joystick) | 76 | std::lock_guard<std::mutex> lock(mutex); |
| 48 | return {}; | 77 | return state.axes.at(axis) / 32767.0f; |
| 49 | SDL_JoystickUpdate(); | ||
| 50 | return SDL_JoystickGetAxis(joystick.get(), axis) / 32767.0f; | ||
| 51 | } | 78 | } |
| 52 | 79 | ||
| 53 | std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { | 80 | std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { |
| @@ -67,18 +94,213 @@ public: | |||
| 67 | return std::make_tuple(x, y); | 94 | return std::make_tuple(x, y); |
| 68 | } | 95 | } |
| 69 | 96 | ||
| 97 | void SetHat(int hat, Uint8 direction) { | ||
| 98 | std::lock_guard<std::mutex> lock(mutex); | ||
| 99 | state.hats[hat] = direction; | ||
| 100 | } | ||
| 101 | |||
| 70 | bool GetHatDirection(int hat, Uint8 direction) const { | 102 | bool GetHatDirection(int hat, Uint8 direction) const { |
| 71 | return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; | 103 | std::lock_guard<std::mutex> lock(mutex); |
| 104 | return (state.hats.at(hat) & direction) != 0; | ||
| 105 | } | ||
| 106 | /** | ||
| 107 | * The guid of the joystick | ||
| 108 | */ | ||
| 109 | const std::string& GetGUID() const { | ||
| 110 | return guid; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * The number of joystick from the same type that were connected before this joystick | ||
| 115 | */ | ||
| 116 | int GetPort() const { | ||
| 117 | return port; | ||
| 118 | } | ||
| 119 | |||
| 120 | SDL_Joystick* GetSDLJoystick() const { | ||
| 121 | return sdl_joystick.get(); | ||
| 72 | } | 122 | } |
| 73 | 123 | ||
| 74 | SDL_JoystickID GetJoystickID() const { | 124 | void SetSDLJoystick(SDL_Joystick* joystick, |
| 75 | return SDL_JoystickInstanceID(joystick.get()); | 125 | decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) { |
| 126 | sdl_joystick = | ||
| 127 | std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter); | ||
| 76 | } | 128 | } |
| 77 | 129 | ||
| 78 | private: | 130 | private: |
| 79 | std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; | 131 | struct State { |
| 132 | std::unordered_map<int, bool> buttons; | ||
| 133 | std::unordered_map<int, Sint16> axes; | ||
| 134 | std::unordered_map<int, Uint8> hats; | ||
| 135 | } state; | ||
| 136 | std::string guid; | ||
| 137 | int port; | ||
| 138 | std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; | ||
| 139 | mutable std::mutex mutex; | ||
| 80 | }; | 140 | }; |
| 81 | 141 | ||
| 142 | /** | ||
| 143 | * Get the nth joystick with the corresponding GUID | ||
| 144 | */ | ||
| 145 | static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) { | ||
| 146 | std::lock_guard<std::mutex> lock(joystick_map_mutex); | ||
| 147 | const auto it = joystick_map.find(guid); | ||
| 148 | if (it != joystick_map.end()) { | ||
| 149 | while (it->second.size() <= port) { | ||
| 150 | auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr, | ||
| 151 | [](SDL_Joystick*) {}); | ||
| 152 | it->second.emplace_back(std::move(joystick)); | ||
| 153 | } | ||
| 154 | return it->second[port]; | ||
| 155 | } | ||
| 156 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {}); | ||
| 157 | return joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie | ||
| 162 | * it to a SDLJoystick with the same guid and that port | ||
| 163 | */ | ||
| 164 | static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { | ||
| 165 | std::lock_guard<std::mutex> lock(joystick_map_mutex); | ||
| 166 | auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); | ||
| 167 | const std::string guid = GetGUID(sdl_joystick); | ||
| 168 | auto map_it = joystick_map.find(guid); | ||
| 169 | if (map_it != joystick_map.end()) { | ||
| 170 | auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), | ||
| 171 | [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { | ||
| 172 | return sdl_joystick == joystick->GetSDLJoystick(); | ||
| 173 | }); | ||
| 174 | if (vec_it != map_it->second.end()) { | ||
| 175 | // This is the common case: There is already an existing SDL_Joystick maped to a | ||
| 176 | // SDLJoystick. return the SDLJoystick | ||
| 177 | return *vec_it; | ||
| 178 | } | ||
| 179 | // Search for a SDLJoystick without a mapped SDL_Joystick... | ||
| 180 | auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), | ||
| 181 | [](const std::shared_ptr<SDLJoystick>& joystick) { | ||
| 182 | return !joystick->GetSDLJoystick(); | ||
| 183 | }); | ||
| 184 | if (nullptr_it != map_it->second.end()) { | ||
| 185 | // ... and map it | ||
| 186 | (*nullptr_it)->SetSDLJoystick(sdl_joystick); | ||
| 187 | return *nullptr_it; | ||
| 188 | } | ||
| 189 | // There is no SDLJoystick without a mapped SDL_Joystick | ||
| 190 | // Create a new SDLJoystick | ||
| 191 | auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick); | ||
| 192 | return map_it->second.emplace_back(std::move(joystick)); | ||
| 193 | } | ||
| 194 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); | ||
| 195 | return joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 196 | } | ||
| 197 | |||
| 198 | void InitJoystick(int joystick_index) { | ||
| 199 | std::lock_guard<std::mutex> lock(joystick_map_mutex); | ||
| 200 | SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); | ||
| 201 | if (!sdl_joystick) { | ||
| 202 | LOG_ERROR(Input, "failed to open joystick {}", joystick_index); | ||
| 203 | return; | ||
| 204 | } | ||
| 205 | std::string guid = GetGUID(sdl_joystick); | ||
| 206 | if (joystick_map.find(guid) == joystick_map.end()) { | ||
| 207 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); | ||
| 208 | joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | auto& joystick_guid_list = joystick_map[guid]; | ||
| 212 | const auto it = std::find_if( | ||
| 213 | joystick_guid_list.begin(), joystick_guid_list.end(), | ||
| 214 | [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); | ||
| 215 | if (it != joystick_guid_list.end()) { | ||
| 216 | (*it)->SetSDLJoystick(sdl_joystick); | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick); | ||
| 220 | joystick_guid_list.emplace_back(std::move(joystick)); | ||
| 221 | } | ||
| 222 | |||
| 223 | void CloseJoystick(SDL_Joystick* sdl_joystick) { | ||
| 224 | std::lock_guard<std::mutex> lock(joystick_map_mutex); | ||
| 225 | std::string guid = GetGUID(sdl_joystick); | ||
| 226 | // This call to guid is save since the joystick is guranteed to be in that map | ||
| 227 | auto& joystick_guid_list = joystick_map[guid]; | ||
| 228 | const auto joystick_it = | ||
| 229 | std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), | ||
| 230 | [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { | ||
| 231 | return joystick->GetSDLJoystick() == sdl_joystick; | ||
| 232 | }); | ||
| 233 | (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {}); | ||
| 234 | } | ||
| 235 | |||
| 236 | void HandleGameControllerEvent(const SDL_Event& event) { | ||
| 237 | switch (event.type) { | ||
| 238 | case SDL_JOYBUTTONUP: { | ||
| 239 | auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); | ||
| 240 | if (joystick) { | ||
| 241 | joystick->SetButton(event.jbutton.button, false); | ||
| 242 | } | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | case SDL_JOYBUTTONDOWN: { | ||
| 246 | auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); | ||
| 247 | if (joystick) { | ||
| 248 | joystick->SetButton(event.jbutton.button, true); | ||
| 249 | } | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | case SDL_JOYHATMOTION: { | ||
| 253 | auto joystick = GetSDLJoystickBySDLID(event.jhat.which); | ||
| 254 | if (joystick) { | ||
| 255 | joystick->SetHat(event.jhat.hat, event.jhat.value); | ||
| 256 | } | ||
| 257 | break; | ||
| 258 | } | ||
| 259 | case SDL_JOYAXISMOTION: { | ||
| 260 | auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); | ||
| 261 | if (joystick) { | ||
| 262 | joystick->SetAxis(event.jaxis.axis, event.jaxis.value); | ||
| 263 | } | ||
| 264 | break; | ||
| 265 | } | ||
| 266 | case SDL_JOYDEVICEREMOVED: | ||
| 267 | LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); | ||
| 268 | CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); | ||
| 269 | break; | ||
| 270 | case SDL_JOYDEVICEADDED: | ||
| 271 | LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which); | ||
| 272 | InitJoystick(event.jdevice.which); | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | void CloseSDLJoysticks() { | ||
| 278 | std::lock_guard<std::mutex> lock(joystick_map_mutex); | ||
| 279 | joystick_map.clear(); | ||
| 280 | } | ||
| 281 | |||
| 282 | void PollLoop() { | ||
| 283 | if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { | ||
| 284 | LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); | ||
| 285 | return; | ||
| 286 | } | ||
| 287 | |||
| 288 | SDL_Event event; | ||
| 289 | while (initialized) { | ||
| 290 | // Wait for 10 ms or until an event happens | ||
| 291 | if (SDL_WaitEventTimeout(&event, 10)) { | ||
| 292 | // Don't handle the event if we are configuring | ||
| 293 | if (polling) { | ||
| 294 | event_queue.Push(event); | ||
| 295 | } else { | ||
| 296 | HandleGameControllerEvent(event); | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | CloseSDLJoysticks(); | ||
| 301 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | ||
| 302 | } | ||
| 303 | |||
| 82 | class SDLButton final : public Input::ButtonDevice { | 304 | class SDLButton final : public Input::ButtonDevice { |
| 83 | public: | 305 | public: |
| 84 | explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) | 306 | explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) |
| @@ -144,22 +366,14 @@ private: | |||
| 144 | int axis_y; | 366 | int axis_y; |
| 145 | }; | 367 | }; |
| 146 | 368 | ||
| 147 | static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) { | ||
| 148 | std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock(); | ||
| 149 | if (!joystick) { | ||
| 150 | joystick = std::make_shared<SDLJoystick>(joystick_index); | ||
| 151 | joystick_list[joystick_index] = joystick; | ||
| 152 | } | ||
| 153 | return joystick; | ||
| 154 | } | ||
| 155 | |||
| 156 | /// A button device factory that creates button devices from SDL joystick | 369 | /// A button device factory that creates button devices from SDL joystick |
| 157 | class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { | 370 | class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { |
| 158 | public: | 371 | public: |
| 159 | /** | 372 | /** |
| 160 | * Creates a button device from a joystick button | 373 | * Creates a button device from a joystick button |
| 161 | * @param params contains parameters for creating the device: | 374 | * @param params contains parameters for creating the device: |
| 162 | * - "joystick": the index of the joystick to bind | 375 | * - "guid": the guid of the joystick to bind |
| 376 | * - "port": the nth joystick of the same type to bind | ||
| 163 | * - "button"(optional): the index of the button to bind | 377 | * - "button"(optional): the index of the button to bind |
| 164 | * - "hat"(optional): the index of the hat to bind as direction buttons | 378 | * - "hat"(optional): the index of the hat to bind as direction buttons |
| 165 | * - "axis"(optional): the index of the axis to bind | 379 | * - "axis"(optional): the index of the axis to bind |
| @@ -167,12 +381,15 @@ public: | |||
| 167 | * "down", "left" or "right" | 381 | * "down", "left" or "right" |
| 168 | * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is | 382 | * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is |
| 169 | * triggered if the axis value crosses | 383 | * triggered if the axis value crosses |
| 170 | * - "direction"(only used for axis): "+" means the button is triggered when the axis value | 384 | * - "direction"(only used for axis): "+" means the button is triggered when the axis |
| 171 | * is greater than the threshold; "-" means the button is triggered when the axis value | 385 | * value is greater than the threshold; "-" means the button is triggered when the axis |
| 172 | * is smaller than the threshold | 386 | * value is smaller than the threshold |
| 173 | */ | 387 | */ |
| 174 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { | 388 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { |
| 175 | const int joystick_index = params.Get("joystick", 0); | 389 | const std::string guid = params.Get("guid", "0"); |
| 390 | const int port = params.Get("port", 0); | ||
| 391 | |||
| 392 | auto joystick = GetSDLJoystickByGUID(guid, port); | ||
| 176 | 393 | ||
| 177 | if (params.Has("hat")) { | 394 | if (params.Has("hat")) { |
| 178 | const int hat = params.Get("hat", 0); | 395 | const int hat = params.Get("hat", 0); |
| @@ -189,8 +406,9 @@ public: | |||
| 189 | } else { | 406 | } else { |
| 190 | direction = 0; | 407 | direction = 0; |
| 191 | } | 408 | } |
| 192 | return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat, | 409 | // This is necessary so accessing GetHat with hat won't crash |
| 193 | direction); | 410 | joystick->SetHat(hat, SDL_HAT_CENTERED); |
| 411 | return std::make_unique<SDLDirectionButton>(joystick, hat, direction); | ||
| 194 | } | 412 | } |
| 195 | 413 | ||
| 196 | if (params.Has("axis")) { | 414 | if (params.Has("axis")) { |
| @@ -206,12 +424,15 @@ public: | |||
| 206 | trigger_if_greater = true; | 424 | trigger_if_greater = true; |
| 207 | LOG_ERROR(Input, "Unknown direction '{}'", direction_name); | 425 | LOG_ERROR(Input, "Unknown direction '{}'", direction_name); |
| 208 | } | 426 | } |
| 209 | return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold, | 427 | // This is necessary so accessing GetAxis with axis won't crash |
| 210 | trigger_if_greater); | 428 | joystick->SetAxis(axis, 0); |
| 429 | return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater); | ||
| 211 | } | 430 | } |
| 212 | 431 | ||
| 213 | const int button = params.Get("button", 0); | 432 | const int button = params.Get("button", 0); |
| 214 | return std::make_unique<SDLButton>(GetJoystick(joystick_index), button); | 433 | // This is necessary so accessing GetButton with button won't crash |
| 434 | joystick->SetButton(button, false); | ||
| 435 | return std::make_unique<SDLButton>(joystick, button); | ||
| 215 | } | 436 | } |
| 216 | }; | 437 | }; |
| 217 | 438 | ||
| @@ -221,27 +442,32 @@ public: | |||
| 221 | /** | 442 | /** |
| 222 | * Creates analog device from joystick axes | 443 | * Creates analog device from joystick axes |
| 223 | * @param params contains parameters for creating the device: | 444 | * @param params contains parameters for creating the device: |
| 224 | * - "joystick": the index of the joystick to bind | 445 | * - "guid": the guid of the joystick to bind |
| 446 | * - "port": the nth joystick of the same type | ||
| 225 | * - "axis_x": the index of the axis to be bind as x-axis | 447 | * - "axis_x": the index of the axis to be bind as x-axis |
| 226 | * - "axis_y": the index of the axis to be bind as y-axis | 448 | * - "axis_y": the index of the axis to be bind as y-axis |
| 227 | */ | 449 | */ |
| 228 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { | 450 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { |
| 229 | const int joystick_index = params.Get("joystick", 0); | 451 | const std::string guid = params.Get("guid", "0"); |
| 452 | const int port = params.Get("port", 0); | ||
| 230 | const int axis_x = params.Get("axis_x", 0); | 453 | const int axis_x = params.Get("axis_x", 0); |
| 231 | const int axis_y = params.Get("axis_y", 1); | 454 | const int axis_y = params.Get("axis_y", 1); |
| 232 | return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y); | 455 | |
| 456 | auto joystick = GetSDLJoystickByGUID(guid, port); | ||
| 457 | |||
| 458 | // This is necessary so accessing GetAxis with axis_x and axis_y won't crash | ||
| 459 | joystick->SetAxis(axis_x, 0); | ||
| 460 | joystick->SetAxis(axis_y, 0); | ||
| 461 | return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y); | ||
| 233 | } | 462 | } |
| 234 | }; | 463 | }; |
| 235 | 464 | ||
| 236 | void Init() { | 465 | void Init() { |
| 237 | if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { | 466 | using namespace Input; |
| 238 | LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); | 467 | RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); |
| 239 | } else { | 468 | RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); |
| 240 | using namespace Input; | 469 | polling = false; |
| 241 | RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); | 470 | initialized = true; |
| 242 | RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); | ||
| 243 | initialized = true; | ||
| 244 | } | ||
| 245 | } | 471 | } |
| 246 | 472 | ||
| 247 | void Shutdown() { | 473 | void Shutdown() { |
| @@ -249,30 +475,17 @@ void Shutdown() { | |||
| 249 | using namespace Input; | 475 | using namespace Input; |
| 250 | UnregisterFactory<ButtonDevice>("sdl"); | 476 | UnregisterFactory<ButtonDevice>("sdl"); |
| 251 | UnregisterFactory<AnalogDevice>("sdl"); | 477 | UnregisterFactory<AnalogDevice>("sdl"); |
| 252 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | 478 | initialized = false; |
| 253 | } | 479 | } |
| 254 | } | 480 | } |
| 255 | 481 | ||
| 256 | /** | ||
| 257 | * This function converts a joystick ID used in SDL events to the device index. This is necessary | ||
| 258 | * because Citra opens joysticks using their indices, not their IDs. | ||
| 259 | */ | ||
| 260 | static int JoystickIDToDeviceIndex(SDL_JoystickID id) { | ||
| 261 | int num_joysticks = SDL_NumJoysticks(); | ||
| 262 | for (int i = 0; i < num_joysticks; i++) { | ||
| 263 | auto joystick = GetJoystick(i); | ||
| 264 | if (joystick->GetJoystickID() == id) { | ||
| 265 | return i; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | return -1; | ||
| 269 | } | ||
| 270 | |||
| 271 | Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { | 482 | Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { |
| 272 | Common::ParamPackage params({{"engine", "sdl"}}); | 483 | Common::ParamPackage params({{"engine", "sdl"}}); |
| 273 | switch (event.type) { | 484 | switch (event.type) { |
| 274 | case SDL_JOYAXISMOTION: | 485 | case SDL_JOYAXISMOTION: { |
| 275 | params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which)); | 486 | auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); |
| 487 | params.Set("port", joystick->GetPort()); | ||
| 488 | params.Set("guid", joystick->GetGUID()); | ||
| 276 | params.Set("axis", event.jaxis.axis); | 489 | params.Set("axis", event.jaxis.axis); |
| 277 | if (event.jaxis.value > 0) { | 490 | if (event.jaxis.value > 0) { |
| 278 | params.Set("direction", "+"); | 491 | params.Set("direction", "+"); |
| @@ -282,12 +495,18 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { | |||
| 282 | params.Set("threshold", "-0.5"); | 495 | params.Set("threshold", "-0.5"); |
| 283 | } | 496 | } |
| 284 | break; | 497 | break; |
| 285 | case SDL_JOYBUTTONUP: | 498 | } |
| 286 | params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which)); | 499 | case SDL_JOYBUTTONUP: { |
| 500 | auto joystick = GetSDLJoystickBySDLID(event.jbutton.which); | ||
| 501 | params.Set("port", joystick->GetPort()); | ||
| 502 | params.Set("guid", joystick->GetGUID()); | ||
| 287 | params.Set("button", event.jbutton.button); | 503 | params.Set("button", event.jbutton.button); |
| 288 | break; | 504 | break; |
| 289 | case SDL_JOYHATMOTION: | 505 | } |
| 290 | params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which)); | 506 | case SDL_JOYHATMOTION: { |
| 507 | auto joystick = GetSDLJoystickBySDLID(event.jhat.which); | ||
| 508 | params.Set("port", joystick->GetPort()); | ||
| 509 | params.Set("guid", joystick->GetGUID()); | ||
| 291 | params.Set("hat", event.jhat.hat); | 510 | params.Set("hat", event.jhat.hat); |
| 292 | switch (event.jhat.value) { | 511 | switch (event.jhat.value) { |
| 293 | case SDL_HAT_UP: | 512 | case SDL_HAT_UP: |
| @@ -307,6 +526,7 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { | |||
| 307 | } | 526 | } |
| 308 | break; | 527 | break; |
| 309 | } | 528 | } |
| 529 | } | ||
| 310 | return params; | 530 | return params; |
| 311 | } | 531 | } |
| 312 | 532 | ||
| @@ -315,31 +535,20 @@ namespace Polling { | |||
| 315 | class SDLPoller : public InputCommon::Polling::DevicePoller { | 535 | class SDLPoller : public InputCommon::Polling::DevicePoller { |
| 316 | public: | 536 | public: |
| 317 | void Start() override { | 537 | void Start() override { |
| 318 | // SDL joysticks must be opened, otherwise they don't generate events | 538 | event_queue.Clear(); |
| 319 | SDL_JoystickUpdate(); | 539 | polling = true; |
| 320 | int num_joysticks = SDL_NumJoysticks(); | ||
| 321 | for (int i = 0; i < num_joysticks; i++) { | ||
| 322 | joysticks_opened.emplace_back(GetJoystick(i)); | ||
| 323 | } | ||
| 324 | // Empty event queue to get rid of old events. citra-qt doesn't use the queue | ||
| 325 | SDL_Event dummy; | ||
| 326 | while (SDL_PollEvent(&dummy)) { | ||
| 327 | } | ||
| 328 | } | 540 | } |
| 329 | 541 | ||
| 330 | void Stop() override { | 542 | void Stop() override { |
| 331 | joysticks_opened.clear(); | 543 | polling = false; |
| 332 | } | 544 | } |
| 333 | |||
| 334 | private: | ||
| 335 | std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened; | ||
| 336 | }; | 545 | }; |
| 337 | 546 | ||
| 338 | class SDLButtonPoller final : public SDLPoller { | 547 | class SDLButtonPoller final : public SDLPoller { |
| 339 | public: | 548 | public: |
| 340 | Common::ParamPackage GetNextInput() override { | 549 | Common::ParamPackage GetNextInput() override { |
| 341 | SDL_Event event; | 550 | SDL_Event event; |
| 342 | while (SDL_PollEvent(&event)) { | 551 | while (event_queue.Pop(event)) { |
| 343 | switch (event.type) { | 552 | switch (event.type) { |
| 344 | case SDL_JOYAXISMOTION: | 553 | case SDL_JOYAXISMOTION: |
| 345 | if (std::abs(event.jaxis.value / 32767.0) < 0.5) { | 554 | if (std::abs(event.jaxis.value / 32767.0) < 0.5) { |
| @@ -367,7 +576,7 @@ public: | |||
| 367 | 576 | ||
| 368 | Common::ParamPackage GetNextInput() override { | 577 | Common::ParamPackage GetNextInput() override { |
| 369 | SDL_Event event; | 578 | SDL_Event event; |
| 370 | while (SDL_PollEvent(&event)) { | 579 | while (event_queue.Pop(event)) { |
| 371 | if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { | 580 | if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { |
| 372 | continue; | 581 | continue; |
| 373 | } | 582 | } |
| @@ -384,8 +593,10 @@ public: | |||
| 384 | } | 593 | } |
| 385 | Common::ParamPackage params; | 594 | Common::ParamPackage params; |
| 386 | if (analog_xaxis != -1 && analog_yaxis != -1) { | 595 | if (analog_xaxis != -1 && analog_yaxis != -1) { |
| 596 | auto joystick = GetSDLJoystickBySDLID(event.jaxis.which); | ||
| 387 | params.Set("engine", "sdl"); | 597 | params.Set("engine", "sdl"); |
| 388 | params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick)); | 598 | params.Set("port", joystick->GetPort()); |
| 599 | params.Set("guid", joystick->GetGUID()); | ||
| 389 | params.Set("axis_x", analog_xaxis); | 600 | params.Set("axis_x", analog_xaxis); |
| 390 | params.Set("axis_y", analog_yaxis); | 601 | params.Set("axis_y", analog_yaxis); |
| 391 | analog_xaxis = -1; | 602 | analog_xaxis = -1; |
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h index 7934099d4..0206860d3 100644 --- a/src/input_common/sdl/sdl.h +++ b/src/input_common/sdl/sdl.h | |||
| @@ -28,6 +28,15 @@ void Init(); | |||
| 28 | /// Unresisters SDL device factories and shut them down. | 28 | /// Unresisters SDL device factories and shut them down. |
| 29 | void Shutdown(); | 29 | void Shutdown(); |
| 30 | 30 | ||
| 31 | /// Needs to be called before SDL_QuitSubSystem. | ||
| 32 | void CloseSDLJoysticks(); | ||
| 33 | |||
| 34 | /// Handle SDL_Events for joysticks from SDL_PollEvent | ||
| 35 | void HandleGameControllerEvent(const SDL_Event& event); | ||
| 36 | |||
| 37 | /// A Loop that calls HandleGameControllerEvent until Shutdown is called | ||
| 38 | void PollLoop(); | ||
| 39 | |||
| 31 | /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice | 40 | /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice |
| 32 | Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); | 41 | Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); |
| 33 | 42 | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4d74bb395..37f09ce5f 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,16 +1,15 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/param_package.cpp | 2 | common/param_package.cpp |
| 3 | common/ring_buffer.cpp | ||
| 3 | core/arm/arm_test_common.cpp | 4 | core/arm/arm_test_common.cpp |
| 4 | core/arm/arm_test_common.h | 5 | core/arm/arm_test_common.h |
| 5 | core/core_timing.cpp | 6 | core/core_timing.cpp |
| 6 | glad.cpp | ||
| 7 | tests.cpp | 7 | tests.cpp |
| 8 | ) | 8 | ) |
| 9 | 9 | ||
| 10 | create_target_directory_groups(tests) | 10 | create_target_directory_groups(tests) |
| 11 | 11 | ||
| 12 | target_link_libraries(tests PRIVATE common core) | 12 | target_link_libraries(tests PRIVATE common core) |
| 13 | target_link_libraries(tests PRIVATE glad) # To support linker work-around | ||
| 14 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) | 13 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) |
| 15 | 14 | ||
| 16 | add_test(NAME tests COMMAND tests) | 15 | add_test(NAME tests COMMAND tests) |
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp new file mode 100644 index 000000000..c883c4d56 --- /dev/null +++ b/src/tests/common/ring_buffer.cpp | |||
| @@ -0,0 +1,130 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <cstddef> | ||
| 8 | #include <numeric> | ||
| 9 | #include <thread> | ||
| 10 | #include <vector> | ||
| 11 | #include <catch2/catch.hpp> | ||
| 12 | #include "common/ring_buffer.h" | ||
| 13 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | TEST_CASE("RingBuffer: Basic Tests", "[common]") { | ||
| 17 | RingBuffer<char, 4, 1> buf; | ||
| 18 | |||
| 19 | // Pushing values into a ring buffer with space should succeed. | ||
| 20 | for (std::size_t i = 0; i < 4; i++) { | ||
| 21 | const char elem = static_cast<char>(i); | ||
| 22 | const std::size_t count = buf.Push(&elem, 1); | ||
| 23 | REQUIRE(count == 1); | ||
| 24 | } | ||
| 25 | |||
| 26 | REQUIRE(buf.Size() == 4); | ||
| 27 | |||
| 28 | // Pushing values into a full ring buffer should fail. | ||
| 29 | { | ||
| 30 | const char elem = static_cast<char>(42); | ||
| 31 | const std::size_t count = buf.Push(&elem, 1); | ||
| 32 | REQUIRE(count == 0); | ||
| 33 | } | ||
| 34 | |||
| 35 | REQUIRE(buf.Size() == 4); | ||
| 36 | |||
| 37 | // Popping multiple values from a ring buffer with values should succeed. | ||
| 38 | { | ||
| 39 | const std::vector<char> popped = buf.Pop(2); | ||
| 40 | REQUIRE(popped.size() == 2); | ||
| 41 | REQUIRE(popped[0] == 0); | ||
| 42 | REQUIRE(popped[1] == 1); | ||
| 43 | } | ||
| 44 | |||
| 45 | REQUIRE(buf.Size() == 2); | ||
| 46 | |||
| 47 | // Popping a single value from a ring buffer with values should succeed. | ||
| 48 | { | ||
| 49 | const std::vector<char> popped = buf.Pop(1); | ||
| 50 | REQUIRE(popped.size() == 1); | ||
| 51 | REQUIRE(popped[0] == 2); | ||
| 52 | } | ||
| 53 | |||
| 54 | REQUIRE(buf.Size() == 1); | ||
| 55 | |||
| 56 | // Pushing more values than space available should partially suceed. | ||
| 57 | { | ||
| 58 | std::vector<char> to_push(6); | ||
| 59 | std::iota(to_push.begin(), to_push.end(), 88); | ||
| 60 | const std::size_t count = buf.Push(to_push); | ||
| 61 | REQUIRE(count == 3); | ||
| 62 | } | ||
| 63 | |||
| 64 | REQUIRE(buf.Size() == 4); | ||
| 65 | |||
| 66 | // Doing an unlimited pop should pop all values. | ||
| 67 | { | ||
| 68 | const std::vector<char> popped = buf.Pop(); | ||
| 69 | REQUIRE(popped.size() == 4); | ||
| 70 | REQUIRE(popped[0] == 3); | ||
| 71 | REQUIRE(popped[1] == 88); | ||
| 72 | REQUIRE(popped[2] == 89); | ||
| 73 | REQUIRE(popped[3] == 90); | ||
| 74 | } | ||
| 75 | |||
| 76 | REQUIRE(buf.Size() == 0); | ||
| 77 | } | ||
| 78 | |||
| 79 | TEST_CASE("RingBuffer: Threaded Test", "[common]") { | ||
| 80 | RingBuffer<char, 4, 2> buf; | ||
| 81 | const char seed = 42; | ||
| 82 | const std::size_t count = 1000000; | ||
| 83 | std::size_t full = 0; | ||
| 84 | std::size_t empty = 0; | ||
| 85 | |||
| 86 | const auto next_value = [](std::array<char, 2>& value) { | ||
| 87 | value[0] += 1; | ||
| 88 | value[1] += 2; | ||
| 89 | }; | ||
| 90 | |||
| 91 | std::thread producer{[&] { | ||
| 92 | std::array<char, 2> value = {seed, seed}; | ||
| 93 | std::size_t i = 0; | ||
| 94 | while (i < count) { | ||
| 95 | if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { | ||
| 96 | REQUIRE(c == 1); | ||
| 97 | i++; | ||
| 98 | next_value(value); | ||
| 99 | } else { | ||
| 100 | full++; | ||
| 101 | std::this_thread::yield(); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | }}; | ||
| 105 | |||
| 106 | std::thread consumer{[&] { | ||
| 107 | std::array<char, 2> value = {seed, seed}; | ||
| 108 | std::size_t i = 0; | ||
| 109 | while (i < count) { | ||
| 110 | if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { | ||
| 111 | REQUIRE(v.size() == 2); | ||
| 112 | REQUIRE(v[0] == value[0]); | ||
| 113 | REQUIRE(v[1] == value[1]); | ||
| 114 | i++; | ||
| 115 | next_value(value); | ||
| 116 | } else { | ||
| 117 | empty++; | ||
| 118 | std::this_thread::yield(); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | }}; | ||
| 122 | |||
| 123 | producer.join(); | ||
| 124 | consumer.join(); | ||
| 125 | |||
| 126 | REQUIRE(buf.Size() == 0); | ||
| 127 | printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty); | ||
| 128 | } | ||
| 129 | |||
| 130 | } // namespace Common | ||
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 038d57b3a..7c69fc26e 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp | |||
| @@ -87,11 +87,11 @@ boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { | |||
| 87 | return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; | 87 | return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) { | 90 | bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) { |
| 91 | VAddr addr = src_addr; | 91 | VAddr addr = src_addr; |
| 92 | u8* data = static_cast<u8*>(dest_buffer); | 92 | u8* data = static_cast<u8*>(dest_buffer); |
| 93 | 93 | ||
| 94 | for (size_t i = 0; i < size; i++, addr++, data++) { | 94 | for (std::size_t i = 0; i < size; i++, addr++, data++) { |
| 95 | *data = *Read8(addr); | 95 | *data = *Read8(addr); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| @@ -126,11 +126,12 @@ bool TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) { | |||
| 126 | return true; | 126 | return true; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) { | 129 | bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, |
| 130 | std::size_t size) { | ||
| 130 | VAddr addr = dest_addr; | 131 | VAddr addr = dest_addr; |
| 131 | const u8* data = static_cast<const u8*>(src_buffer); | 132 | const u8* data = static_cast<const u8*>(src_buffer); |
| 132 | 133 | ||
| 133 | for (size_t i = 0; i < size; i++, addr++, data++) { | 134 | for (std::size_t i = 0; i < size; i++, addr++, data++) { |
| 134 | env->write_records.emplace_back(8, addr, *data); | 135 | env->write_records.emplace_back(8, addr, *data); |
| 135 | if (env->mutable_memory) | 136 | if (env->mutable_memory) |
| 136 | env->SetMemory8(addr, *data); | 137 | env->SetMemory8(addr, *data); |
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h index e4b6df194..5de8dab4e 100644 --- a/src/tests/core/arm/arm_test_common.h +++ b/src/tests/core/arm/arm_test_common.h | |||
| @@ -19,8 +19,8 @@ struct PageTable; | |||
| 19 | namespace ArmTests { | 19 | namespace ArmTests { |
| 20 | 20 | ||
| 21 | struct WriteRecord { | 21 | struct WriteRecord { |
| 22 | WriteRecord(size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} | 22 | WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} |
| 23 | size_t size; | 23 | std::size_t size; |
| 24 | VAddr addr; | 24 | VAddr addr; |
| 25 | u64 data; | 25 | u64 data; |
| 26 | bool operator==(const WriteRecord& o) const { | 26 | bool operator==(const WriteRecord& o) const { |
| @@ -71,14 +71,14 @@ private: | |||
| 71 | boost::optional<u32> Read32(VAddr addr) override; | 71 | boost::optional<u32> Read32(VAddr addr) override; |
| 72 | boost::optional<u64> Read64(VAddr addr) override; | 72 | boost::optional<u64> Read64(VAddr addr) override; |
| 73 | 73 | ||
| 74 | bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) override; | 74 | bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; |
| 75 | 75 | ||
| 76 | bool Write8(VAddr addr, u8 data) override; | 76 | bool Write8(VAddr addr, u8 data) override; |
| 77 | bool Write16(VAddr addr, u16 data) override; | 77 | bool Write16(VAddr addr, u16 data) override; |
| 78 | bool Write32(VAddr addr, u32 data) override; | 78 | bool Write32(VAddr addr, u32 data) override; |
| 79 | bool Write64(VAddr addr, u64 data) override; | 79 | bool Write64(VAddr addr, u64 data) override; |
| 80 | 80 | ||
| 81 | bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) override; | 81 | bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override; |
| 82 | 82 | ||
| 83 | std::unordered_map<VAddr, u8> data; | 83 | std::unordered_map<VAddr, u8> data; |
| 84 | }; | 84 | }; |
diff --git a/src/tests/glad.cpp b/src/tests/glad.cpp deleted file mode 100644 index 1797c0e3d..000000000 --- a/src/tests/glad.cpp +++ /dev/null | |||
| @@ -1,14 +0,0 @@ | |||
| 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 <catch2/catch.hpp> | ||
| 6 | #include <glad/glad.h> | ||
| 7 | |||
| 8 | // This is not an actual test, but a work-around for issue #2183. | ||
| 9 | // If tests uses functions in core but doesn't explicitly use functions in glad, the linker of macOS | ||
| 10 | // will error about undefined references from video_core to glad. So we explicitly use a glad | ||
| 11 | // function here to shut up the linker. | ||
| 12 | TEST_CASE("glad fake test", "[dummy]") { | ||
| 13 | REQUIRE(&gladLoadGL != nullptr); | ||
| 14 | } | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index aa5bc3bbe..f5ae57039 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -5,6 +5,8 @@ add_library(video_core STATIC | |||
| 5 | debug_utils/debug_utils.h | 5 | debug_utils/debug_utils.h |
| 6 | engines/fermi_2d.cpp | 6 | engines/fermi_2d.cpp |
| 7 | engines/fermi_2d.h | 7 | engines/fermi_2d.h |
| 8 | engines/kepler_memory.cpp | ||
| 9 | engines/kepler_memory.h | ||
| 8 | engines/maxwell_3d.cpp | 10 | engines/maxwell_3d.cpp |
| 9 | engines/maxwell_3d.h | 11 | engines/maxwell_3d.h |
| 10 | engines/maxwell_compute.cpp | 12 | engines/maxwell_compute.cpp |
| @@ -12,6 +14,7 @@ add_library(video_core STATIC | |||
| 12 | engines/maxwell_dma.cpp | 14 | engines/maxwell_dma.cpp |
| 13 | engines/maxwell_dma.h | 15 | engines/maxwell_dma.h |
| 14 | engines/shader_bytecode.h | 16 | engines/shader_bytecode.h |
| 17 | engines/shader_header.h | ||
| 15 | gpu.cpp | 18 | gpu.cpp |
| 16 | gpu.h | 19 | gpu.h |
| 17 | macro_interpreter.cpp | 20 | macro_interpreter.cpp |
| @@ -22,6 +25,8 @@ add_library(video_core STATIC | |||
| 22 | rasterizer_interface.h | 25 | rasterizer_interface.h |
| 23 | renderer_base.cpp | 26 | renderer_base.cpp |
| 24 | renderer_base.h | 27 | renderer_base.h |
| 28 | renderer_opengl/gl_buffer_cache.cpp | ||
| 29 | renderer_opengl/gl_buffer_cache.h | ||
| 25 | renderer_opengl/gl_rasterizer.cpp | 30 | renderer_opengl/gl_rasterizer.cpp |
| 26 | renderer_opengl/gl_rasterizer.h | 31 | renderer_opengl/gl_rasterizer.h |
| 27 | renderer_opengl/gl_rasterizer_cache.cpp | 32 | renderer_opengl/gl_rasterizer_cache.cpp |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index dc485e811..f1aa6091b 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/tracer/recorder.h" | 14 | #include "core/tracer/recorder.h" |
| 15 | #include "video_core/command_processor.h" | 15 | #include "video_core/command_processor.h" |
| 16 | #include "video_core/engines/fermi_2d.h" | 16 | #include "video_core/engines/fermi_2d.h" |
| 17 | #include "video_core/engines/kepler_memory.h" | ||
| 17 | #include "video_core/engines/maxwell_3d.h" | 18 | #include "video_core/engines/maxwell_3d.h" |
| 18 | #include "video_core/engines/maxwell_compute.h" | 19 | #include "video_core/engines/maxwell_compute.h" |
| 19 | #include "video_core/engines/maxwell_dma.h" | 20 | #include "video_core/engines/maxwell_dma.h" |
| @@ -28,98 +29,109 @@ enum class BufferMethods { | |||
| 28 | CountBufferMethods = 0x40, | 29 | CountBufferMethods = 0x40, |
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) { | 32 | MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB(128, 128, 192)); |
| 32 | LOG_TRACE(HW_GPU, | ||
| 33 | "Processing method {:08X} on subchannel {} value " | ||
| 34 | "{:08X} remaining params {}", | ||
| 35 | method, subchannel, value, remaining_params); | ||
| 36 | |||
| 37 | if (method == static_cast<u32>(BufferMethods::BindObject)) { | ||
| 38 | // Bind the current subchannel to the desired engine id. | ||
| 39 | LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); | ||
| 40 | bound_engines[subchannel] = static_cast<EngineID>(value); | ||
| 41 | return; | ||
| 42 | } | ||
| 43 | 33 | ||
| 44 | if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { | 34 | void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) { |
| 45 | // TODO(Subv): Research and implement these methods. | 35 | MICROPROFILE_SCOPE(ProcessCommandLists); |
| 46 | LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | 36 | ||
| 50 | ASSERT(bound_engines.find(subchannel) != bound_engines.end()); | 37 | auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) { |
| 51 | 38 | LOG_TRACE(HW_GPU, | |
| 52 | const EngineID engine = bound_engines[subchannel]; | 39 | "Processing method {:08X} on subchannel {} value " |
| 53 | 40 | "{:08X} remaining params {}", | |
| 54 | switch (engine) { | 41 | method, subchannel, value, remaining_params); |
| 55 | case EngineID::FERMI_TWOD_A: | ||
| 56 | fermi_2d->WriteReg(method, value); | ||
| 57 | break; | ||
| 58 | case EngineID::MAXWELL_B: | ||
| 59 | maxwell_3d->WriteReg(method, value, remaining_params); | ||
| 60 | break; | ||
| 61 | case EngineID::MAXWELL_COMPUTE_B: | ||
| 62 | maxwell_compute->WriteReg(method, value); | ||
| 63 | break; | ||
| 64 | case EngineID::MAXWELL_DMA_COPY_A: | ||
| 65 | maxwell_dma->WriteReg(method, value); | ||
| 66 | break; | ||
| 67 | default: | ||
| 68 | UNIMPLEMENTED_MSG("Unimplemented engine"); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | 42 | ||
| 72 | void GPU::ProcessCommandList(GPUVAddr address, u32 size) { | 43 | ASSERT(subchannel < bound_engines.size()); |
| 73 | const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); | 44 | |
| 74 | VAddr current_addr = *head_address; | 45 | if (method == static_cast<u32>(BufferMethods::BindObject)) { |
| 75 | while (current_addr < *head_address + size * sizeof(CommandHeader)) { | 46 | // Bind the current subchannel to the desired engine id. |
| 76 | const CommandHeader header = {Memory::Read32(current_addr)}; | 47 | LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); |
| 77 | current_addr += sizeof(u32); | 48 | bound_engines[subchannel] = static_cast<EngineID>(value); |
| 78 | 49 | return; | |
| 79 | switch (header.mode.Value()) { | ||
| 80 | case SubmissionMode::IncreasingOld: | ||
| 81 | case SubmissionMode::Increasing: { | ||
| 82 | // Increase the method value with each argument. | ||
| 83 | for (unsigned i = 0; i < header.arg_count; ++i) { | ||
| 84 | WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr), | ||
| 85 | header.arg_count - i - 1); | ||
| 86 | current_addr += sizeof(u32); | ||
| 87 | } | ||
| 88 | break; | ||
| 89 | } | 50 | } |
| 90 | case SubmissionMode::NonIncreasingOld: | 51 | |
| 91 | case SubmissionMode::NonIncreasing: { | 52 | if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { |
| 92 | // Use the same method value for all arguments. | 53 | // TODO(Subv): Research and implement these methods. |
| 93 | for (unsigned i = 0; i < header.arg_count; ++i) { | 54 | LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); |
| 94 | WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), | 55 | return; |
| 95 | header.arg_count - i - 1); | 56 | } |
| 96 | current_addr += sizeof(u32); | 57 | |
| 97 | } | 58 | const EngineID engine = bound_engines[subchannel]; |
| 59 | |||
| 60 | switch (engine) { | ||
| 61 | case EngineID::FERMI_TWOD_A: | ||
| 62 | fermi_2d->WriteReg(method, value); | ||
| 63 | break; | ||
| 64 | case EngineID::MAXWELL_B: | ||
| 65 | maxwell_3d->WriteReg(method, value, remaining_params); | ||
| 98 | break; | 66 | break; |
| 67 | case EngineID::MAXWELL_COMPUTE_B: | ||
| 68 | maxwell_compute->WriteReg(method, value); | ||
| 69 | break; | ||
| 70 | case EngineID::MAXWELL_DMA_COPY_A: | ||
| 71 | maxwell_dma->WriteReg(method, value); | ||
| 72 | break; | ||
| 73 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: | ||
| 74 | kepler_memory->WriteReg(method, value); | ||
| 75 | break; | ||
| 76 | default: | ||
| 77 | UNIMPLEMENTED_MSG("Unimplemented engine"); | ||
| 99 | } | 78 | } |
| 100 | case SubmissionMode::IncreaseOnce: { | 79 | }; |
| 101 | ASSERT(header.arg_count.Value() >= 1); | ||
| 102 | 80 | ||
| 103 | // Use the original method for the first argument and then the next method for all other | 81 | for (auto entry : commands) { |
| 104 | // arguments. | 82 | Tegra::GPUVAddr address = entry.Address(); |
| 105 | WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), | 83 | u32 size = entry.sz; |
| 106 | header.arg_count - 1); | 84 | const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); |
| 85 | VAddr current_addr = *head_address; | ||
| 86 | while (current_addr < *head_address + size * sizeof(CommandHeader)) { | ||
| 87 | const CommandHeader header = {Memory::Read32(current_addr)}; | ||
| 107 | current_addr += sizeof(u32); | 88 | current_addr += sizeof(u32); |
| 108 | 89 | ||
| 109 | for (unsigned i = 1; i < header.arg_count; ++i) { | 90 | switch (header.mode.Value()) { |
| 110 | WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr), | 91 | case SubmissionMode::IncreasingOld: |
| 111 | header.arg_count - i - 1); | 92 | case SubmissionMode::Increasing: { |
| 93 | // Increase the method value with each argument. | ||
| 94 | for (unsigned i = 0; i < header.arg_count; ++i) { | ||
| 95 | WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr), | ||
| 96 | header.arg_count - i - 1); | ||
| 97 | current_addr += sizeof(u32); | ||
| 98 | } | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | case SubmissionMode::NonIncreasingOld: | ||
| 102 | case SubmissionMode::NonIncreasing: { | ||
| 103 | // Use the same method value for all arguments. | ||
| 104 | for (unsigned i = 0; i < header.arg_count; ++i) { | ||
| 105 | WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), | ||
| 106 | header.arg_count - i - 1); | ||
| 107 | current_addr += sizeof(u32); | ||
| 108 | } | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | case SubmissionMode::IncreaseOnce: { | ||
| 112 | ASSERT(header.arg_count.Value() >= 1); | ||
| 113 | |||
| 114 | // Use the original method for the first argument and then the next method for all | ||
| 115 | // other arguments. | ||
| 116 | WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), | ||
| 117 | header.arg_count - 1); | ||
| 112 | current_addr += sizeof(u32); | 118 | current_addr += sizeof(u32); |
| 119 | |||
| 120 | for (unsigned i = 1; i < header.arg_count; ++i) { | ||
| 121 | WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr), | ||
| 122 | header.arg_count - i - 1); | ||
| 123 | current_addr += sizeof(u32); | ||
| 124 | } | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | case SubmissionMode::Inline: { | ||
| 128 | // The register value is stored in the bits 16-28 as an immediate | ||
| 129 | WriteReg(header.method, header.subchannel, header.inline_data, 0); | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | default: | ||
| 133 | UNIMPLEMENTED(); | ||
| 113 | } | 134 | } |
| 114 | break; | ||
| 115 | } | ||
| 116 | case SubmissionMode::Inline: { | ||
| 117 | // The register value is stored in the bits 16-28 as an immediate | ||
| 118 | WriteReg(header.method, header.subchannel, header.inline_data, 0); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | default: | ||
| 122 | UNIMPLEMENTED(); | ||
| 123 | } | 135 | } |
| 124 | } | 136 | } |
| 125 | } | 137 | } |
diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h index a01153e0b..bd766e77a 100644 --- a/src/video_core/command_processor.h +++ b/src/video_core/command_processor.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <type_traits> | 7 | #include <type_traits> |
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/memory_manager.h" | ||
| 10 | 11 | ||
| 11 | namespace Tegra { | 12 | namespace Tegra { |
| 12 | 13 | ||
| @@ -19,6 +20,22 @@ enum class SubmissionMode : u32 { | |||
| 19 | IncreaseOnce = 5 | 20 | IncreaseOnce = 5 |
| 20 | }; | 21 | }; |
| 21 | 22 | ||
| 23 | struct CommandListHeader { | ||
| 24 | u32 entry0; // gpu_va_lo | ||
| 25 | union { | ||
| 26 | u32 entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F) | ||
| 27 | BitField<0, 8, u32> gpu_va_hi; | ||
| 28 | BitField<8, 2, u32> unk1; | ||
| 29 | BitField<10, 21, u32> sz; | ||
| 30 | BitField<31, 1, u32> unk2; | ||
| 31 | }; | ||
| 32 | |||
| 33 | GPUVAddr Address() const { | ||
| 34 | return (static_cast<GPUVAddr>(gpu_va_hi) << 32) | entry0; | ||
| 35 | } | ||
| 36 | }; | ||
| 37 | static_assert(sizeof(CommandListHeader) == 8, "CommandListHeader is incorrect size"); | ||
| 38 | |||
| 22 | union CommandHeader { | 39 | union CommandHeader { |
| 23 | u32 hex; | 40 | u32 hex; |
| 24 | 41 | ||
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index dcf9ef8b9..021b83eaa 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -26,7 +26,7 @@ public: | |||
| 26 | void WriteReg(u32 method, u32 value); | 26 | void WriteReg(u32 method, u32 value); |
| 27 | 27 | ||
| 28 | struct Regs { | 28 | struct Regs { |
| 29 | static constexpr size_t NUM_REGS = 0x258; | 29 | static constexpr std::size_t NUM_REGS = 0x258; |
| 30 | 30 | ||
| 31 | struct Surface { | 31 | struct Surface { |
| 32 | RenderTargetFormat format; | 32 | RenderTargetFormat format; |
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp new file mode 100644 index 000000000..66ae6332d --- /dev/null +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 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 "common/logging/log.h" | ||
| 6 | #include "core/memory.h" | ||
| 7 | #include "video_core/engines/kepler_memory.h" | ||
| 8 | |||
| 9 | namespace Tegra::Engines { | ||
| 10 | |||
| 11 | KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} | ||
| 12 | KeplerMemory::~KeplerMemory() = default; | ||
| 13 | |||
| 14 | void KeplerMemory::WriteReg(u32 method, u32 value) { | ||
| 15 | ASSERT_MSG(method < Regs::NUM_REGS, | ||
| 16 | "Invalid KeplerMemory register, increase the size of the Regs structure"); | ||
| 17 | |||
| 18 | regs.reg_array[method] = value; | ||
| 19 | |||
| 20 | switch (method) { | ||
| 21 | case KEPLERMEMORY_REG_INDEX(exec): { | ||
| 22 | state.write_offset = 0; | ||
| 23 | break; | ||
| 24 | } | ||
| 25 | case KEPLERMEMORY_REG_INDEX(data): { | ||
| 26 | ProcessData(value); | ||
| 27 | break; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | void KeplerMemory::ProcessData(u32 data) { | ||
| 33 | ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); | ||
| 34 | ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); | ||
| 35 | |||
| 36 | GPUVAddr address = regs.dest.Address(); | ||
| 37 | VAddr dest_address = | ||
| 38 | *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); | ||
| 39 | |||
| 40 | Memory::Write32(dest_address, data); | ||
| 41 | |||
| 42 | state.write_offset++; | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace Tegra::Engines | ||
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h new file mode 100644 index 000000000..b0d0078cf --- /dev/null +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -0,0 +1,90 @@ | |||
| 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 <array> | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | |||
| 14 | namespace Tegra::Engines { | ||
| 15 | |||
| 16 | #define KEPLERMEMORY_REG_INDEX(field_name) \ | ||
| 17 | (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32)) | ||
| 18 | |||
| 19 | class KeplerMemory final { | ||
| 20 | public: | ||
| 21 | KeplerMemory(MemoryManager& memory_manager); | ||
| 22 | ~KeplerMemory(); | ||
| 23 | |||
| 24 | /// Write the value to the register identified by method. | ||
| 25 | void WriteReg(u32 method, u32 value); | ||
| 26 | |||
| 27 | struct Regs { | ||
| 28 | static constexpr size_t NUM_REGS = 0x7F; | ||
| 29 | |||
| 30 | union { | ||
| 31 | struct { | ||
| 32 | INSERT_PADDING_WORDS(0x60); | ||
| 33 | |||
| 34 | u32 line_length_in; | ||
| 35 | u32 line_count; | ||
| 36 | |||
| 37 | struct { | ||
| 38 | u32 address_high; | ||
| 39 | u32 address_low; | ||
| 40 | u32 pitch; | ||
| 41 | u32 block_dimensions; | ||
| 42 | u32 width; | ||
| 43 | u32 height; | ||
| 44 | u32 depth; | ||
| 45 | u32 z; | ||
| 46 | u32 x; | ||
| 47 | u32 y; | ||
| 48 | |||
| 49 | GPUVAddr Address() const { | ||
| 50 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 51 | address_low); | ||
| 52 | } | ||
| 53 | } dest; | ||
| 54 | |||
| 55 | struct { | ||
| 56 | union { | ||
| 57 | BitField<0, 1, u32> linear; | ||
| 58 | }; | ||
| 59 | } exec; | ||
| 60 | |||
| 61 | u32 data; | ||
| 62 | |||
| 63 | INSERT_PADDING_WORDS(0x11); | ||
| 64 | }; | ||
| 65 | std::array<u32, NUM_REGS> reg_array; | ||
| 66 | }; | ||
| 67 | } regs{}; | ||
| 68 | |||
| 69 | struct { | ||
| 70 | u32 write_offset = 0; | ||
| 71 | } state{}; | ||
| 72 | |||
| 73 | private: | ||
| 74 | MemoryManager& memory_manager; | ||
| 75 | |||
| 76 | void ProcessData(u32 data); | ||
| 77 | }; | ||
| 78 | |||
| 79 | #define ASSERT_REG_POSITION(field_name, position) \ | ||
| 80 | static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \ | ||
| 81 | "Field " #field_name " has invalid position") | ||
| 82 | |||
| 83 | ASSERT_REG_POSITION(line_length_in, 0x60); | ||
| 84 | ASSERT_REG_POSITION(line_count, 0x61); | ||
| 85 | ASSERT_REG_POSITION(dest, 0x62); | ||
| 86 | ASSERT_REG_POSITION(exec, 0x6C); | ||
| 87 | ASSERT_REG_POSITION(data, 0x6D); | ||
| 88 | #undef ASSERT_REG_POSITION | ||
| 89 | |||
| 90 | } // namespace Tegra::Engines | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 68ff1e86b..8afd26fe9 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | ||
| 8 | #include "core/memory.h" | 9 | #include "core/memory.h" |
| 9 | #include "video_core/debug_utils/debug_utils.h" | 10 | #include "video_core/debug_utils/debug_utils.h" |
| 10 | #include "video_core/engines/maxwell_3d.h" | 11 | #include "video_core/engines/maxwell_3d.h" |
| @@ -134,8 +135,6 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { | |||
| 134 | break; | 135 | break; |
| 135 | } | 136 | } |
| 136 | 137 | ||
| 137 | rasterizer.NotifyMaxwellRegisterChanged(method); | ||
| 138 | |||
| 139 | if (debug_context) { | 138 | if (debug_context) { |
| 140 | debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); | 139 | debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); |
| 141 | } | 140 | } |
| @@ -194,8 +193,8 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 194 | // wait queues. | 193 | // wait queues. |
| 195 | LongQueryResult query_result{}; | 194 | LongQueryResult query_result{}; |
| 196 | query_result.value = result; | 195 | query_result.value = result; |
| 197 | // TODO(Subv): Generate a real GPU timestamp and write it here instead of 0 | 196 | // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming |
| 198 | query_result.timestamp = 0; | 197 | query_result.timestamp = CoreTiming::GetTicks(); |
| 199 | Memory::WriteBlock(*address, &query_result, sizeof(query_result)); | 198 | Memory::WriteBlock(*address, &query_result, sizeof(query_result)); |
| 200 | } | 199 | } |
| 201 | break; | 200 | break; |
| @@ -249,8 +248,8 @@ void Maxwell3D::DrawArrays() { | |||
| 249 | 248 | ||
| 250 | void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { | 249 | void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { |
| 251 | // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. | 250 | // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. |
| 252 | auto& shader = state.shader_stages[static_cast<size_t>(stage)]; | 251 | auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; |
| 253 | auto& bind_data = regs.cb_bind[static_cast<size_t>(stage)]; | 252 | auto& bind_data = regs.cb_bind[static_cast<std::size_t>(stage)]; |
| 254 | 253 | ||
| 255 | auto& buffer = shader.const_buffers[bind_data.index]; | 254 | auto& buffer = shader.const_buffers[bind_data.index]; |
| 256 | 255 | ||
| @@ -292,10 +291,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { | |||
| 292 | tic_entry.header_version == Texture::TICHeaderVersion::Pitch, | 291 | tic_entry.header_version == Texture::TICHeaderVersion::Pitch, |
| 293 | "TIC versions other than BlockLinear or Pitch are unimplemented"); | 292 | "TIC versions other than BlockLinear or Pitch are unimplemented"); |
| 294 | 293 | ||
| 295 | ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) || | ||
| 296 | (tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap), | ||
| 297 | "Texture types other than Texture2D are unimplemented"); | ||
| 298 | |||
| 299 | auto r_type = tic_entry.r_type.Value(); | 294 | auto r_type = tic_entry.r_type.Value(); |
| 300 | auto g_type = tic_entry.g_type.Value(); | 295 | auto g_type = tic_entry.g_type.Value(); |
| 301 | auto b_type = tic_entry.b_type.Value(); | 296 | auto b_type = tic_entry.b_type.Value(); |
| @@ -321,14 +316,14 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { | |||
| 321 | std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { | 316 | std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { |
| 322 | std::vector<Texture::FullTextureInfo> textures; | 317 | std::vector<Texture::FullTextureInfo> textures; |
| 323 | 318 | ||
| 324 | auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)]; | 319 | auto& fragment_shader = state.shader_stages[static_cast<std::size_t>(stage)]; |
| 325 | auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index]; | 320 | auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index]; |
| 326 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); | 321 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); |
| 327 | 322 | ||
| 328 | GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size; | 323 | GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size; |
| 329 | 324 | ||
| 330 | // Offset into the texture constbuffer where the texture info begins. | 325 | // Offset into the texture constbuffer where the texture info begins. |
| 331 | static constexpr size_t TextureInfoOffset = 0x20; | 326 | static constexpr std::size_t TextureInfoOffset = 0x20; |
| 332 | 327 | ||
| 333 | for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; | 328 | for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; |
| 334 | current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { | 329 | current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { |
| @@ -365,8 +360,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt | |||
| 365 | return textures; | 360 | return textures; |
| 366 | } | 361 | } |
| 367 | 362 | ||
| 368 | Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const { | 363 | Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, |
| 369 | auto& shader = state.shader_stages[static_cast<size_t>(stage)]; | 364 | std::size_t offset) const { |
| 365 | auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; | ||
| 370 | auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; | 366 | auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; |
| 371 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); | 367 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); |
| 372 | 368 | ||
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 92bfda053..b81b0723d 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -34,17 +34,17 @@ public: | |||
| 34 | /// Register structure of the Maxwell3D engine. | 34 | /// Register structure of the Maxwell3D engine. |
| 35 | /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. | 35 | /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. |
| 36 | struct Regs { | 36 | struct Regs { |
| 37 | static constexpr size_t NUM_REGS = 0xE00; | 37 | static constexpr std::size_t NUM_REGS = 0xE00; |
| 38 | 38 | ||
| 39 | static constexpr size_t NumRenderTargets = 8; | 39 | static constexpr std::size_t NumRenderTargets = 8; |
| 40 | static constexpr size_t NumViewports = 16; | 40 | static constexpr std::size_t NumViewports = 16; |
| 41 | static constexpr size_t NumCBData = 16; | 41 | static constexpr std::size_t NumCBData = 16; |
| 42 | static constexpr size_t NumVertexArrays = 32; | 42 | static constexpr std::size_t NumVertexArrays = 32; |
| 43 | static constexpr size_t NumVertexAttributes = 32; | 43 | static constexpr std::size_t NumVertexAttributes = 32; |
| 44 | static constexpr size_t MaxShaderProgram = 6; | 44 | static constexpr std::size_t MaxShaderProgram = 6; |
| 45 | static constexpr size_t MaxShaderStage = 5; | 45 | static constexpr std::size_t MaxShaderStage = 5; |
| 46 | // Maximum number of const buffers per shader stage. | 46 | // Maximum number of const buffers per shader stage. |
| 47 | static constexpr size_t MaxConstBuffers = 18; | 47 | static constexpr std::size_t MaxConstBuffers = 18; |
| 48 | 48 | ||
| 49 | enum class QueryMode : u32 { | 49 | enum class QueryMode : u32 { |
| 50 | Write = 0, | 50 | Write = 0, |
| @@ -127,6 +127,7 @@ public: | |||
| 127 | BitField<21, 6, Size> size; | 127 | BitField<21, 6, Size> size; |
| 128 | BitField<27, 3, Type> type; | 128 | BitField<27, 3, Type> type; |
| 129 | BitField<31, 1, u32> bgra; | 129 | BitField<31, 1, u32> bgra; |
| 130 | u32 hex; | ||
| 130 | }; | 131 | }; |
| 131 | 132 | ||
| 132 | u32 ComponentCount() const { | 133 | u32 ComponentCount() const { |
| @@ -262,6 +263,10 @@ public: | |||
| 262 | bool IsValid() const { | 263 | bool IsValid() const { |
| 263 | return size != Size::Invalid; | 264 | return size != Size::Invalid; |
| 264 | } | 265 | } |
| 266 | |||
| 267 | bool operator<(const VertexAttribute& other) const { | ||
| 268 | return hex < other.hex; | ||
| 269 | } | ||
| 265 | }; | 270 | }; |
| 266 | 271 | ||
| 267 | enum class PrimitiveTopology : u32 { | 272 | enum class PrimitiveTopology : u32 { |
| @@ -438,9 +443,9 @@ public: | |||
| 438 | } | 443 | } |
| 439 | }; | 444 | }; |
| 440 | 445 | ||
| 441 | bool IsShaderConfigEnabled(size_t index) const { | 446 | bool IsShaderConfigEnabled(std::size_t index) const { |
| 442 | // The VertexB is always enabled. | 447 | // The VertexB is always enabled. |
| 443 | if (index == static_cast<size_t>(Regs::ShaderProgram::VertexB)) { | 448 | if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { |
| 444 | return true; | 449 | return true; |
| 445 | } | 450 | } |
| 446 | return shader_config[index].enable != 0; | 451 | return shader_config[index].enable != 0; |
| @@ -528,7 +533,11 @@ public: | |||
| 528 | u32 stencil_back_mask; | 533 | u32 stencil_back_mask; |
| 529 | u32 stencil_back_func_mask; | 534 | u32 stencil_back_func_mask; |
| 530 | 535 | ||
| 531 | INSERT_PADDING_WORDS(0x20); | 536 | INSERT_PADDING_WORDS(0x13); |
| 537 | |||
| 538 | u32 rt_separate_frag_data; | ||
| 539 | |||
| 540 | INSERT_PADDING_WORDS(0xC); | ||
| 532 | 541 | ||
| 533 | struct { | 542 | struct { |
| 534 | u32 address_high; | 543 | u32 address_high; |
| @@ -545,14 +554,29 @@ public: | |||
| 545 | 554 | ||
| 546 | INSERT_PADDING_WORDS(0x5B); | 555 | INSERT_PADDING_WORDS(0x5B); |
| 547 | 556 | ||
| 548 | VertexAttribute vertex_attrib_format[NumVertexAttributes]; | 557 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; |
| 549 | 558 | ||
| 550 | INSERT_PADDING_WORDS(0xF); | 559 | INSERT_PADDING_WORDS(0xF); |
| 551 | 560 | ||
| 552 | struct { | 561 | struct { |
| 553 | union { | 562 | union { |
| 554 | BitField<0, 4, u32> count; | 563 | BitField<0, 4, u32> count; |
| 564 | BitField<4, 3, u32> map_0; | ||
| 565 | BitField<7, 3, u32> map_1; | ||
| 566 | BitField<10, 3, u32> map_2; | ||
| 567 | BitField<13, 3, u32> map_3; | ||
| 568 | BitField<16, 3, u32> map_4; | ||
| 569 | BitField<19, 3, u32> map_5; | ||
| 570 | BitField<22, 3, u32> map_6; | ||
| 571 | BitField<25, 3, u32> map_7; | ||
| 555 | }; | 572 | }; |
| 573 | |||
| 574 | u32 GetMap(std::size_t index) const { | ||
| 575 | const std::array<u32, NumRenderTargets> maps{map_0, map_1, map_2, map_3, | ||
| 576 | map_4, map_5, map_6, map_7}; | ||
| 577 | ASSERT(index < maps.size()); | ||
| 578 | return maps[index]; | ||
| 579 | } | ||
| 556 | } rt_control; | 580 | } rt_control; |
| 557 | 581 | ||
| 558 | INSERT_PADDING_WORDS(0x2); | 582 | INSERT_PADDING_WORDS(0x2); |
| @@ -901,7 +925,7 @@ public: | |||
| 901 | std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; | 925 | std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; |
| 902 | 926 | ||
| 903 | /// Returns the texture information for a specific texture in a specific shader stage. | 927 | /// Returns the texture information for a specific texture in a specific shader stage. |
| 904 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; | 928 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; |
| 905 | 929 | ||
| 906 | private: | 930 | private: |
| 907 | VideoCore::RasterizerInterface& rasterizer; | 931 | VideoCore::RasterizerInterface& rasterizer; |
| @@ -963,8 +987,9 @@ ASSERT_REG_POSITION(clear_stencil, 0x368); | |||
| 963 | ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); | 987 | ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); |
| 964 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); | 988 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); |
| 965 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); | 989 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); |
| 990 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); | ||
| 966 | ASSERT_REG_POSITION(zeta, 0x3F8); | 991 | ASSERT_REG_POSITION(zeta, 0x3F8); |
| 967 | ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458); | 992 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); |
| 968 | ASSERT_REG_POSITION(rt_control, 0x487); | 993 | ASSERT_REG_POSITION(rt_control, 0x487); |
| 969 | ASSERT_REG_POSITION(zeta_width, 0x48a); | 994 | ASSERT_REG_POSITION(zeta_width, 0x48a); |
| 970 | ASSERT_REG_POSITION(zeta_height, 0x48b); | 995 | ASSERT_REG_POSITION(zeta_height, 0x48b); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 6e740713f..aa7481b8c 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -41,7 +41,6 @@ void MaxwellDMA::HandleCopy() { | |||
| 41 | 41 | ||
| 42 | // TODO(Subv): Perform more research and implement all features of this engine. | 42 | // TODO(Subv): Perform more research and implement all features of this engine. |
| 43 | ASSERT(regs.exec.enable_swizzle == 0); | 43 | ASSERT(regs.exec.enable_swizzle == 0); |
| 44 | ASSERT(regs.exec.enable_2d == 1); | ||
| 45 | ASSERT(regs.exec.query_mode == Regs::QueryMode::None); | 44 | ASSERT(regs.exec.query_mode == Regs::QueryMode::None); |
| 46 | ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); | 45 | ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); |
| 47 | ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); | 46 | ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); |
| @@ -51,10 +50,19 @@ void MaxwellDMA::HandleCopy() { | |||
| 51 | ASSERT(regs.dst_params.pos_y == 0); | 50 | ASSERT(regs.dst_params.pos_y == 0); |
| 52 | 51 | ||
| 53 | if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { | 52 | if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { |
| 54 | Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count * regs.y_count); | 53 | std::size_t copy_size = regs.x_count; |
| 54 | |||
| 55 | // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D | ||
| 56 | // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). | ||
| 57 | if (regs.exec.enable_2d) { | ||
| 58 | copy_size = copy_size * regs.y_count; | ||
| 59 | } | ||
| 60 | |||
| 61 | Memory::CopyBlock(dest_cpu, source_cpu, copy_size); | ||
| 55 | return; | 62 | return; |
| 56 | } | 63 | } |
| 57 | 64 | ||
| 65 | ASSERT(regs.exec.enable_2d == 1); | ||
| 58 | u8* src_buffer = Memory::GetPointer(source_cpu); | 66 | u8* src_buffer = Memory::GetPointer(source_cpu); |
| 59 | u8* dst_buffer = Memory::GetPointer(dest_cpu); | 67 | u8* dst_buffer = Memory::GetPointer(dest_cpu); |
| 60 | 68 | ||
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 7882f16e0..311ccb616 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -23,7 +23,7 @@ public: | |||
| 23 | void WriteReg(u32 method, u32 value); | 23 | void WriteReg(u32 method, u32 value); |
| 24 | 24 | ||
| 25 | struct Regs { | 25 | struct Regs { |
| 26 | static constexpr size_t NUM_REGS = 0x1D6; | 26 | static constexpr std::size_t NUM_REGS = 0x1D6; |
| 27 | 27 | ||
| 28 | struct Parameters { | 28 | struct Parameters { |
| 29 | union { | 29 | union { |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 3e4efbe0c..7e1de0fa1 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -20,10 +20,10 @@ namespace Tegra::Shader { | |||
| 20 | 20 | ||
| 21 | struct Register { | 21 | struct Register { |
| 22 | /// Number of registers | 22 | /// Number of registers |
| 23 | static constexpr size_t NumRegisters = 256; | 23 | static constexpr std::size_t NumRegisters = 256; |
| 24 | 24 | ||
| 25 | /// Register 255 is special cased to always be 0 | 25 | /// Register 255 is special cased to always be 0 |
| 26 | static constexpr size_t ZeroIndex = 255; | 26 | static constexpr std::size_t ZeroIndex = 255; |
| 27 | 27 | ||
| 28 | enum class Size : u64 { | 28 | enum class Size : u64 { |
| 29 | Byte = 0, | 29 | Byte = 0, |
| @@ -67,6 +67,13 @@ private: | |||
| 67 | u64 value{}; | 67 | u64 value{}; |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | enum class AttributeSize : u64 { | ||
| 71 | Word = 0, | ||
| 72 | DoubleWord = 1, | ||
| 73 | TripleWord = 2, | ||
| 74 | QuadWord = 3, | ||
| 75 | }; | ||
| 76 | |||
| 70 | union Attribute { | 77 | union Attribute { |
| 71 | Attribute() = default; | 78 | Attribute() = default; |
| 72 | 79 | ||
| @@ -76,6 +83,7 @@ union Attribute { | |||
| 76 | Position = 7, | 83 | Position = 7, |
| 77 | Attribute_0 = 8, | 84 | Attribute_0 = 8, |
| 78 | Attribute_31 = 39, | 85 | Attribute_31 = 39, |
| 86 | PointCoord = 46, | ||
| 79 | // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex | 87 | // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex |
| 80 | // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval | 88 | // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval |
| 81 | // shader. | 89 | // shader. |
| @@ -86,9 +94,10 @@ union Attribute { | |||
| 86 | }; | 94 | }; |
| 87 | 95 | ||
| 88 | union { | 96 | union { |
| 97 | BitField<20, 10, u64> immediate; | ||
| 89 | BitField<22, 2, u64> element; | 98 | BitField<22, 2, u64> element; |
| 90 | BitField<24, 6, Index> index; | 99 | BitField<24, 6, Index> index; |
| 91 | BitField<47, 3, u64> size; | 100 | BitField<47, 3, AttributeSize> size; |
| 92 | } fmt20; | 101 | } fmt20; |
| 93 | 102 | ||
| 94 | union { | 103 | union { |
| @@ -231,6 +240,41 @@ enum class FlowCondition : u64 { | |||
| 231 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? | 240 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? |
| 232 | }; | 241 | }; |
| 233 | 242 | ||
| 243 | enum class ControlCode : u64 { | ||
| 244 | F = 0, | ||
| 245 | LT = 1, | ||
| 246 | EQ = 2, | ||
| 247 | LE = 3, | ||
| 248 | GT = 4, | ||
| 249 | NE = 5, | ||
| 250 | GE = 6, | ||
| 251 | Num = 7, | ||
| 252 | Nan = 8, | ||
| 253 | LTU = 9, | ||
| 254 | EQU = 10, | ||
| 255 | LEU = 11, | ||
| 256 | GTU = 12, | ||
| 257 | NEU = 13, | ||
| 258 | GEU = 14, | ||
| 259 | // | ||
| 260 | OFF = 16, | ||
| 261 | LO = 17, | ||
| 262 | SFF = 18, | ||
| 263 | LS = 19, | ||
| 264 | HI = 20, | ||
| 265 | SFT = 21, | ||
| 266 | HS = 22, | ||
| 267 | OFT = 23, | ||
| 268 | CSM_TA = 24, | ||
| 269 | CSM_TR = 25, | ||
| 270 | CSM_MX = 26, | ||
| 271 | FCSM_TA = 27, | ||
| 272 | FCSM_TR = 28, | ||
| 273 | FCSM_MX = 29, | ||
| 274 | RLE = 30, | ||
| 275 | RGT = 31, | ||
| 276 | }; | ||
| 277 | |||
| 234 | enum class PredicateResultMode : u64 { | 278 | enum class PredicateResultMode : u64 { |
| 235 | None = 0x0, | 279 | None = 0x0, |
| 236 | NotZero = 0x3, | 280 | NotZero = 0x3, |
| @@ -243,7 +287,47 @@ enum class TextureType : u64 { | |||
| 243 | TextureCube = 3, | 287 | TextureCube = 3, |
| 244 | }; | 288 | }; |
| 245 | 289 | ||
| 246 | enum class IpaMode : u64 { Pass = 0, None = 1, Constant = 2, Sc = 3 }; | 290 | enum class TextureQueryType : u64 { |
| 291 | Dimension = 1, | ||
| 292 | TextureType = 2, | ||
| 293 | SamplePosition = 5, | ||
| 294 | Filter = 16, | ||
| 295 | LevelOfDetail = 18, | ||
| 296 | Wrap = 20, | ||
| 297 | BorderColor = 22, | ||
| 298 | }; | ||
| 299 | |||
| 300 | enum class TextureProcessMode : u64 { | ||
| 301 | None = 0, | ||
| 302 | LZ = 1, // Unknown, appears to be the same as none. | ||
| 303 | LB = 2, // Load Bias. | ||
| 304 | LL = 3, // Load LOD (LevelOfDetail) | ||
| 305 | LBA = 6, // Load Bias. The A is unknown, does not appear to differ with LB | ||
| 306 | LLA = 7 // Load LOD. The A is unknown, does not appear to differ with LL | ||
| 307 | }; | ||
| 308 | |||
| 309 | enum class TextureMiscMode : u64 { | ||
| 310 | DC, | ||
| 311 | AOFFI, // Uses Offset | ||
| 312 | NDV, | ||
| 313 | NODEP, | ||
| 314 | MZ, | ||
| 315 | PTP, | ||
| 316 | }; | ||
| 317 | |||
| 318 | enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 }; | ||
| 319 | enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 }; | ||
| 320 | |||
| 321 | struct IpaMode { | ||
| 322 | IpaInterpMode interpolation_mode; | ||
| 323 | IpaSampleMode sampling_mode; | ||
| 324 | inline bool operator==(const IpaMode& a) { | ||
| 325 | return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode); | ||
| 326 | } | ||
| 327 | inline bool operator!=(const IpaMode& a) { | ||
| 328 | return !((*this) == a); | ||
| 329 | } | ||
| 330 | }; | ||
| 247 | 331 | ||
| 248 | union Instruction { | 332 | union Instruction { |
| 249 | Instruction& operator=(const Instruction& instr) { | 333 | Instruction& operator=(const Instruction& instr) { |
| @@ -328,10 +412,16 @@ union Instruction { | |||
| 328 | } alu; | 412 | } alu; |
| 329 | 413 | ||
| 330 | union { | 414 | union { |
| 331 | BitField<54, 3, IpaMode> mode; | 415 | BitField<51, 1, u64> saturate; |
| 416 | BitField<52, 2, IpaSampleMode> sample_mode; | ||
| 417 | BitField<54, 2, IpaInterpMode> interp_mode; | ||
| 332 | } ipa; | 418 | } ipa; |
| 333 | 419 | ||
| 334 | union { | 420 | union { |
| 421 | BitField<39, 2, u64> tab5cb8_2; | ||
| 422 | BitField<41, 3, u64> tab5c68_1; | ||
| 423 | BitField<44, 2, u64> tab5c68_0; | ||
| 424 | BitField<47, 1, u64> cc; | ||
| 335 | BitField<48, 1, u64> negate_b; | 425 | BitField<48, 1, u64> negate_b; |
| 336 | } fmul; | 426 | } fmul; |
| 337 | 427 | ||
| @@ -395,12 +485,54 @@ union Instruction { | |||
| 395 | } bfe; | 485 | } bfe; |
| 396 | 486 | ||
| 397 | union { | 487 | union { |
| 488 | BitField<48, 3, u64> pred48; | ||
| 489 | |||
| 490 | union { | ||
| 491 | BitField<20, 20, u64> entry_a; | ||
| 492 | BitField<39, 5, u64> entry_b; | ||
| 493 | BitField<45, 1, u64> neg; | ||
| 494 | BitField<46, 1, u64> uses_cc; | ||
| 495 | } imm; | ||
| 496 | |||
| 497 | union { | ||
| 498 | BitField<20, 14, u64> cb_index; | ||
| 499 | BitField<34, 5, u64> cb_offset; | ||
| 500 | BitField<56, 1, u64> neg; | ||
| 501 | BitField<57, 1, u64> uses_cc; | ||
| 502 | } hi; | ||
| 503 | |||
| 504 | union { | ||
| 505 | BitField<20, 14, u64> cb_index; | ||
| 506 | BitField<34, 5, u64> cb_offset; | ||
| 507 | BitField<39, 5, u64> entry_a; | ||
| 508 | BitField<45, 1, u64> neg; | ||
| 509 | BitField<46, 1, u64> uses_cc; | ||
| 510 | } rz; | ||
| 511 | |||
| 512 | union { | ||
| 513 | BitField<39, 5, u64> entry_a; | ||
| 514 | BitField<45, 1, u64> neg; | ||
| 515 | BitField<46, 1, u64> uses_cc; | ||
| 516 | } r1; | ||
| 517 | |||
| 518 | union { | ||
| 519 | BitField<28, 8, u64> entry_a; | ||
| 520 | BitField<37, 1, u64> neg; | ||
| 521 | BitField<38, 1, u64> uses_cc; | ||
| 522 | } r2; | ||
| 523 | |||
| 524 | } lea; | ||
| 525 | |||
| 526 | union { | ||
| 398 | BitField<0, 5, FlowCondition> cond; | 527 | BitField<0, 5, FlowCondition> cond; |
| 399 | } flow; | 528 | } flow; |
| 400 | 529 | ||
| 401 | union { | 530 | union { |
| 531 | BitField<47, 1, u64> cc; | ||
| 402 | BitField<48, 1, u64> negate_b; | 532 | BitField<48, 1, u64> negate_b; |
| 403 | BitField<49, 1, u64> negate_c; | 533 | BitField<49, 1, u64> negate_c; |
| 534 | BitField<51, 2, u64> tab5980_1; | ||
| 535 | BitField<53, 2, u64> tab5980_0; | ||
| 404 | } ffma; | 536 | } ffma; |
| 405 | 537 | ||
| 406 | union { | 538 | union { |
| @@ -446,6 +578,27 @@ union Instruction { | |||
| 446 | } psetp; | 578 | } psetp; |
| 447 | 579 | ||
| 448 | union { | 580 | union { |
| 581 | BitField<12, 3, u64> pred12; | ||
| 582 | BitField<15, 1, u64> neg_pred12; | ||
| 583 | BitField<24, 2, PredOperation> cond; | ||
| 584 | BitField<29, 3, u64> pred29; | ||
| 585 | BitField<32, 1, u64> neg_pred29; | ||
| 586 | BitField<39, 3, u64> pred39; | ||
| 587 | BitField<42, 1, u64> neg_pred39; | ||
| 588 | BitField<44, 1, u64> bf; | ||
| 589 | BitField<45, 2, PredOperation> op; | ||
| 590 | } pset; | ||
| 591 | |||
| 592 | union { | ||
| 593 | BitField<0, 3, u64> pred0; | ||
| 594 | BitField<3, 3, u64> pred3; | ||
| 595 | BitField<8, 5, ControlCode> cc; // flag in cc | ||
| 596 | BitField<39, 3, u64> pred39; | ||
| 597 | BitField<42, 1, u64> neg_pred39; | ||
| 598 | BitField<45, 4, PredOperation> op; // op with pred39 | ||
| 599 | } csetp; | ||
| 600 | |||
| 601 | union { | ||
| 449 | BitField<39, 3, u64> pred39; | 602 | BitField<39, 3, u64> pred39; |
| 450 | BitField<42, 1, u64> neg_pred; | 603 | BitField<42, 1, u64> neg_pred; |
| 451 | BitField<43, 1, u64> neg_a; | 604 | BitField<43, 1, u64> neg_a; |
| @@ -490,25 +643,127 @@ union Instruction { | |||
| 490 | BitField<28, 1, u64> array; | 643 | BitField<28, 1, u64> array; |
| 491 | BitField<29, 2, TextureType> texture_type; | 644 | BitField<29, 2, TextureType> texture_type; |
| 492 | BitField<31, 4, u64> component_mask; | 645 | BitField<31, 4, u64> component_mask; |
| 646 | BitField<49, 1, u64> nodep_flag; | ||
| 647 | BitField<50, 1, u64> dc_flag; | ||
| 648 | BitField<54, 1, u64> aoffi_flag; | ||
| 649 | BitField<55, 3, TextureProcessMode> process_mode; | ||
| 493 | 650 | ||
| 494 | bool IsComponentEnabled(size_t component) const { | 651 | bool IsComponentEnabled(std::size_t component) const { |
| 495 | return ((1ull << component) & component_mask) != 0; | 652 | return ((1ull << component) & component_mask) != 0; |
| 496 | } | 653 | } |
| 654 | |||
| 655 | TextureProcessMode GetTextureProcessMode() const { | ||
| 656 | return process_mode; | ||
| 657 | } | ||
| 658 | |||
| 659 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 660 | switch (mode) { | ||
| 661 | case TextureMiscMode::DC: | ||
| 662 | return dc_flag != 0; | ||
| 663 | case TextureMiscMode::NODEP: | ||
| 664 | return nodep_flag != 0; | ||
| 665 | case TextureMiscMode::AOFFI: | ||
| 666 | return aoffi_flag != 0; | ||
| 667 | default: | ||
| 668 | break; | ||
| 669 | } | ||
| 670 | return false; | ||
| 671 | } | ||
| 497 | } tex; | 672 | } tex; |
| 498 | 673 | ||
| 499 | union { | 674 | union { |
| 675 | BitField<22, 6, TextureQueryType> query_type; | ||
| 676 | BitField<31, 4, u64> component_mask; | ||
| 677 | BitField<49, 1, u64> nodep_flag; | ||
| 678 | |||
| 679 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 680 | switch (mode) { | ||
| 681 | case TextureMiscMode::NODEP: | ||
| 682 | return nodep_flag != 0; | ||
| 683 | default: | ||
| 684 | break; | ||
| 685 | } | ||
| 686 | return false; | ||
| 687 | } | ||
| 688 | } txq; | ||
| 689 | |||
| 690 | union { | ||
| 500 | BitField<28, 1, u64> array; | 691 | BitField<28, 1, u64> array; |
| 501 | BitField<29, 2, TextureType> texture_type; | 692 | BitField<29, 2, TextureType> texture_type; |
| 693 | BitField<31, 4, u64> component_mask; | ||
| 694 | BitField<35, 1, u64> ndv_flag; | ||
| 695 | BitField<49, 1, u64> nodep_flag; | ||
| 696 | |||
| 697 | bool IsComponentEnabled(std::size_t component) const { | ||
| 698 | return ((1ull << component) & component_mask) != 0; | ||
| 699 | } | ||
| 700 | |||
| 701 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 702 | switch (mode) { | ||
| 703 | case TextureMiscMode::NDV: | ||
| 704 | return (ndv_flag != 0); | ||
| 705 | case TextureMiscMode::NODEP: | ||
| 706 | return (nodep_flag != 0); | ||
| 707 | default: | ||
| 708 | break; | ||
| 709 | } | ||
| 710 | return false; | ||
| 711 | } | ||
| 712 | } tmml; | ||
| 713 | |||
| 714 | union { | ||
| 715 | BitField<28, 1, u64> array; | ||
| 716 | BitField<29, 2, TextureType> texture_type; | ||
| 717 | BitField<35, 1, u64> ndv_flag; | ||
| 718 | BitField<49, 1, u64> nodep_flag; | ||
| 719 | BitField<50, 1, u64> dc_flag; | ||
| 720 | BitField<54, 2, u64> info; | ||
| 502 | BitField<56, 2, u64> component; | 721 | BitField<56, 2, u64> component; |
| 722 | |||
| 723 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 724 | switch (mode) { | ||
| 725 | case TextureMiscMode::NDV: | ||
| 726 | return ndv_flag != 0; | ||
| 727 | case TextureMiscMode::NODEP: | ||
| 728 | return nodep_flag != 0; | ||
| 729 | case TextureMiscMode::DC: | ||
| 730 | return dc_flag != 0; | ||
| 731 | case TextureMiscMode::AOFFI: | ||
| 732 | return info == 1; | ||
| 733 | case TextureMiscMode::PTP: | ||
| 734 | return info == 2; | ||
| 735 | default: | ||
| 736 | break; | ||
| 737 | } | ||
| 738 | return false; | ||
| 739 | } | ||
| 503 | } tld4; | 740 | } tld4; |
| 504 | 741 | ||
| 505 | union { | 742 | union { |
| 743 | BitField<49, 1, u64> nodep_flag; | ||
| 744 | BitField<50, 1, u64> dc_flag; | ||
| 745 | BitField<51, 1, u64> aoffi_flag; | ||
| 506 | BitField<52, 2, u64> component; | 746 | BitField<52, 2, u64> component; |
| 747 | |||
| 748 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 749 | switch (mode) { | ||
| 750 | case TextureMiscMode::DC: | ||
| 751 | return dc_flag != 0; | ||
| 752 | case TextureMiscMode::NODEP: | ||
| 753 | return nodep_flag != 0; | ||
| 754 | case TextureMiscMode::AOFFI: | ||
| 755 | return aoffi_flag != 0; | ||
| 756 | default: | ||
| 757 | break; | ||
| 758 | } | ||
| 759 | return false; | ||
| 760 | } | ||
| 507 | } tld4s; | 761 | } tld4s; |
| 508 | 762 | ||
| 509 | union { | 763 | union { |
| 510 | BitField<0, 8, Register> gpr0; | 764 | BitField<0, 8, Register> gpr0; |
| 511 | BitField<28, 8, Register> gpr28; | 765 | BitField<28, 8, Register> gpr28; |
| 766 | BitField<49, 1, u64> nodep_flag; | ||
| 512 | BitField<50, 3, u64> component_mask_selector; | 767 | BitField<50, 3, u64> component_mask_selector; |
| 513 | BitField<53, 4, u64> texture_info; | 768 | BitField<53, 4, u64> texture_info; |
| 514 | 769 | ||
| @@ -528,6 +783,37 @@ union Instruction { | |||
| 528 | UNREACHABLE(); | 783 | UNREACHABLE(); |
| 529 | } | 784 | } |
| 530 | 785 | ||
| 786 | TextureProcessMode GetTextureProcessMode() const { | ||
| 787 | switch (texture_info) { | ||
| 788 | case 0: | ||
| 789 | case 2: | ||
| 790 | case 6: | ||
| 791 | case 8: | ||
| 792 | case 9: | ||
| 793 | case 11: | ||
| 794 | return TextureProcessMode::LZ; | ||
| 795 | case 3: | ||
| 796 | case 5: | ||
| 797 | case 13: | ||
| 798 | return TextureProcessMode::LL; | ||
| 799 | default: | ||
| 800 | break; | ||
| 801 | } | ||
| 802 | return TextureProcessMode::None; | ||
| 803 | } | ||
| 804 | |||
| 805 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 806 | switch (mode) { | ||
| 807 | case TextureMiscMode::DC: | ||
| 808 | return (texture_info >= 4 && texture_info <= 6) || texture_info == 9; | ||
| 809 | case TextureMiscMode::NODEP: | ||
| 810 | return nodep_flag != 0; | ||
| 811 | default: | ||
| 812 | break; | ||
| 813 | } | ||
| 814 | return false; | ||
| 815 | } | ||
| 816 | |||
| 531 | bool IsArrayTexture() const { | 817 | bool IsArrayTexture() const { |
| 532 | // TEXS only supports Texture2D arrays. | 818 | // TEXS only supports Texture2D arrays. |
| 533 | return texture_info >= 7 && texture_info <= 9; | 819 | return texture_info >= 7 && texture_info <= 9; |
| @@ -537,7 +823,7 @@ union Instruction { | |||
| 537 | return gpr28.Value() != Register::ZeroIndex; | 823 | return gpr28.Value() != Register::ZeroIndex; |
| 538 | } | 824 | } |
| 539 | 825 | ||
| 540 | bool IsComponentEnabled(size_t component) const { | 826 | bool IsComponentEnabled(std::size_t component) const { |
| 541 | static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ | 827 | static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ |
| 542 | {}, | 828 | {}, |
| 543 | {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, | 829 | {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, |
| @@ -545,7 +831,7 @@ union Instruction { | |||
| 545 | {0x7, 0xb, 0xd, 0xe, 0xf}, | 831 | {0x7, 0xb, 0xd, 0xe, 0xf}, |
| 546 | }}; | 832 | }}; |
| 547 | 833 | ||
| 548 | size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U}; | 834 | std::size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U}; |
| 549 | index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0; | 835 | index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0; |
| 550 | 836 | ||
| 551 | u32 mask = mask_lut[index][component_mask_selector]; | 837 | u32 mask = mask_lut[index][component_mask_selector]; |
| @@ -556,6 +842,7 @@ union Instruction { | |||
| 556 | } texs; | 842 | } texs; |
| 557 | 843 | ||
| 558 | union { | 844 | union { |
| 845 | BitField<49, 1, u64> nodep_flag; | ||
| 559 | BitField<53, 4, u64> texture_info; | 846 | BitField<53, 4, u64> texture_info; |
| 560 | 847 | ||
| 561 | TextureType GetTextureType() const { | 848 | TextureType GetTextureType() const { |
| @@ -576,6 +863,26 @@ union Instruction { | |||
| 576 | UNREACHABLE(); | 863 | UNREACHABLE(); |
| 577 | } | 864 | } |
| 578 | 865 | ||
| 866 | TextureProcessMode GetTextureProcessMode() const { | ||
| 867 | if (texture_info == 1 || texture_info == 5 || texture_info == 12) | ||
| 868 | return TextureProcessMode::LL; | ||
| 869 | return TextureProcessMode::LZ; | ||
| 870 | } | ||
| 871 | |||
| 872 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 873 | switch (mode) { | ||
| 874 | case TextureMiscMode::AOFFI: | ||
| 875 | return texture_info == 12 || texture_info == 4; | ||
| 876 | case TextureMiscMode::MZ: | ||
| 877 | return texture_info == 5; | ||
| 878 | case TextureMiscMode::NODEP: | ||
| 879 | return nodep_flag != 0; | ||
| 880 | default: | ||
| 881 | break; | ||
| 882 | } | ||
| 883 | return false; | ||
| 884 | } | ||
| 885 | |||
| 579 | bool IsArrayTexture() const { | 886 | bool IsArrayTexture() const { |
| 580 | // TEXS only supports Texture2D arrays. | 887 | // TEXS only supports Texture2D arrays. |
| 581 | return texture_info == 8; | 888 | return texture_info == 8; |
| @@ -618,6 +925,7 @@ union Instruction { | |||
| 618 | BitField<36, 5, u64> index; | 925 | BitField<36, 5, u64> index; |
| 619 | } cbuf36; | 926 | } cbuf36; |
| 620 | 927 | ||
| 928 | BitField<47, 1, u64> generates_cc; | ||
| 621 | BitField<61, 1, u64> is_b_imm; | 929 | BitField<61, 1, u64> is_b_imm; |
| 622 | BitField<60, 1, u64> is_b_gpr; | 930 | BitField<60, 1, u64> is_b_gpr; |
| 623 | BitField<59, 1, u64> is_c_gpr; | 931 | BitField<59, 1, u64> is_c_gpr; |
| @@ -647,11 +955,13 @@ public: | |||
| 647 | LDG, // Load from global memory | 955 | LDG, // Load from global memory |
| 648 | STG, // Store in global memory | 956 | STG, // Store in global memory |
| 649 | TEX, | 957 | TEX, |
| 650 | TEXQ, // Texture Query | 958 | TXQ, // Texture Query |
| 651 | TEXS, // Texture Fetch with scalar/non-vec4 source/destinations | 959 | TEXS, // Texture Fetch with scalar/non-vec4 source/destinations |
| 652 | TLDS, // Texture Load with scalar/non-vec4 source/destinations | 960 | TLDS, // Texture Load with scalar/non-vec4 source/destinations |
| 653 | TLD4, // Texture Load 4 | 961 | TLD4, // Texture Load 4 |
| 654 | TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations | 962 | TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations |
| 963 | TMML_B, // Texture Mip Map Level | ||
| 964 | TMML, // Texture Mip Map Level | ||
| 655 | EXIT, | 965 | EXIT, |
| 656 | IPA, | 966 | IPA, |
| 657 | FFMA_IMM, // Fused Multiply and Add | 967 | FFMA_IMM, // Fused Multiply and Add |
| @@ -676,6 +986,11 @@ public: | |||
| 676 | ISCADD_C, // Scale and Add | 986 | ISCADD_C, // Scale and Add |
| 677 | ISCADD_R, | 987 | ISCADD_R, |
| 678 | ISCADD_IMM, | 988 | ISCADD_IMM, |
| 989 | LEA_R1, | ||
| 990 | LEA_R2, | ||
| 991 | LEA_RZ, | ||
| 992 | LEA_IMM, | ||
| 993 | LEA_HI, | ||
| 679 | POPC_C, | 994 | POPC_C, |
| 680 | POPC_R, | 995 | POPC_R, |
| 681 | POPC_IMM, | 996 | POPC_IMM, |
| @@ -734,6 +1049,8 @@ public: | |||
| 734 | ISET_C, | 1049 | ISET_C, |
| 735 | ISET_IMM, | 1050 | ISET_IMM, |
| 736 | PSETP, | 1051 | PSETP, |
| 1052 | PSET, | ||
| 1053 | CSETP, | ||
| 737 | XMAD_IMM, | 1054 | XMAD_IMM, |
| 738 | XMAD_CR, | 1055 | XMAD_CR, |
| 739 | XMAD_RC, | 1056 | XMAD_RC, |
| @@ -757,6 +1074,7 @@ public: | |||
| 757 | IntegerSet, | 1074 | IntegerSet, |
| 758 | IntegerSetPredicate, | 1075 | IntegerSetPredicate, |
| 759 | PredicateSetPredicate, | 1076 | PredicateSetPredicate, |
| 1077 | PredicateSetRegister, | ||
| 760 | Conversion, | 1078 | Conversion, |
| 761 | Xmad, | 1079 | Xmad, |
| 762 | Unknown, | 1080 | Unknown, |
| @@ -821,7 +1139,7 @@ public: | |||
| 821 | private: | 1139 | private: |
| 822 | struct Detail { | 1140 | struct Detail { |
| 823 | private: | 1141 | private: |
| 824 | static constexpr size_t opcode_bitsize = 16; | 1142 | static constexpr std::size_t opcode_bitsize = 16; |
| 825 | 1143 | ||
| 826 | /** | 1144 | /** |
| 827 | * Generates the mask and the expected value after masking from a given bitstring. | 1145 | * Generates the mask and the expected value after masking from a given bitstring. |
| @@ -830,8 +1148,8 @@ private: | |||
| 830 | */ | 1148 | */ |
| 831 | static auto GetMaskAndExpect(const char* const bitstring) { | 1149 | static auto GetMaskAndExpect(const char* const bitstring) { |
| 832 | u16 mask = 0, expect = 0; | 1150 | u16 mask = 0, expect = 0; |
| 833 | for (size_t i = 0; i < opcode_bitsize; i++) { | 1151 | for (std::size_t i = 0; i < opcode_bitsize; i++) { |
| 834 | const size_t bit_position = opcode_bitsize - i - 1; | 1152 | const std::size_t bit_position = opcode_bitsize - i - 1; |
| 835 | switch (bitstring[i]) { | 1153 | switch (bitstring[i]) { |
| 836 | case '0': | 1154 | case '0': |
| 837 | mask |= 1 << bit_position; | 1155 | mask |= 1 << bit_position; |
| @@ -871,11 +1189,13 @@ private: | |||
| 871 | INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), | 1189 | INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), |
| 872 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), | 1190 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), |
| 873 | INST("110000----111---", Id::TEX, Type::Memory, "TEX"), | 1191 | INST("110000----111---", Id::TEX, Type::Memory, "TEX"), |
| 874 | INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), | 1192 | INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"), |
| 875 | INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), | 1193 | INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), |
| 876 | INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), | 1194 | INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), |
| 877 | INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"), | 1195 | INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"), |
| 878 | INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"), | 1196 | INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"), |
| 1197 | INST("110111110110----", Id::TMML_B, Type::Memory, "TMML_B"), | ||
| 1198 | INST("1101111101011---", Id::TMML, Type::Memory, "TMML"), | ||
| 879 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), | 1199 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), |
| 880 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | 1200 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), |
| 881 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), | 1201 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), |
| @@ -906,6 +1226,11 @@ private: | |||
| 906 | INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), | 1226 | INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), |
| 907 | INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), | 1227 | INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), |
| 908 | INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), | 1228 | INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), |
| 1229 | INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"), | ||
| 1230 | INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"), | ||
| 1231 | INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), | ||
| 1232 | INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"), | ||
| 1233 | INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"), | ||
| 909 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 1234 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
| 910 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), | 1235 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), |
| 911 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), | 1236 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), |
| @@ -960,7 +1285,9 @@ private: | |||
| 960 | INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"), | 1285 | INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"), |
| 961 | INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"), | 1286 | INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"), |
| 962 | INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), | 1287 | INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), |
| 1288 | INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"), | ||
| 963 | INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), | 1289 | INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), |
| 1290 | INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"), | ||
| 964 | INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), | 1291 | INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), |
| 965 | INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), | 1292 | INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), |
| 966 | INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), | 1293 | INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), |
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h new file mode 100644 index 000000000..a885ee3cf --- /dev/null +++ b/src/video_core/engines/shader_header.h | |||
| @@ -0,0 +1,103 @@ | |||
| 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 "common/bit_field.h" | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Tegra::Shader { | ||
| 12 | |||
| 13 | enum class OutputTopology : u32 { | ||
| 14 | PointList = 1, | ||
| 15 | LineStrip = 6, | ||
| 16 | TriangleStrip = 7, | ||
| 17 | }; | ||
| 18 | |||
| 19 | // Documentation in: | ||
| 20 | // http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture | ||
| 21 | struct Header { | ||
| 22 | union { | ||
| 23 | BitField<0, 5, u32> sph_type; | ||
| 24 | BitField<5, 5, u32> version; | ||
| 25 | BitField<10, 4, u32> shader_type; | ||
| 26 | BitField<14, 1, u32> mrt_enable; | ||
| 27 | BitField<15, 1, u32> kills_pixels; | ||
| 28 | BitField<16, 1, u32> does_global_store; | ||
| 29 | BitField<17, 4, u32> sass_version; | ||
| 30 | BitField<21, 5, u32> reserved; | ||
| 31 | BitField<26, 1, u32> does_load_or_store; | ||
| 32 | BitField<27, 1, u32> does_fp64; | ||
| 33 | BitField<28, 4, u32> stream_out_mask; | ||
| 34 | } common0; | ||
| 35 | |||
| 36 | union { | ||
| 37 | BitField<0, 24, u32> shader_local_memory_low_size; | ||
| 38 | BitField<24, 8, u32> per_patch_attribute_count; | ||
| 39 | } common1; | ||
| 40 | |||
| 41 | union { | ||
| 42 | BitField<0, 24, u32> shader_local_memory_high_size; | ||
| 43 | BitField<24, 8, u32> threads_per_input_primitive; | ||
| 44 | } common2; | ||
| 45 | |||
| 46 | union { | ||
| 47 | BitField<0, 24, u32> shader_local_memory_crs_size; | ||
| 48 | BitField<24, 4, OutputTopology> output_topology; | ||
| 49 | BitField<28, 4, u32> reserved; | ||
| 50 | } common3; | ||
| 51 | |||
| 52 | union { | ||
| 53 | BitField<0, 12, u32> max_output_vertices; | ||
| 54 | BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. | ||
| 55 | BitField<24, 4, u32> reserved; | ||
| 56 | BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders. | ||
| 57 | } common4; | ||
| 58 | |||
| 59 | union { | ||
| 60 | struct { | ||
| 61 | INSERT_PADDING_BYTES(3); // ImapSystemValuesA | ||
| 62 | INSERT_PADDING_BYTES(1); // ImapSystemValuesB | ||
| 63 | INSERT_PADDING_BYTES(16); // ImapGenericVector[32] | ||
| 64 | INSERT_PADDING_BYTES(2); // ImapColor | ||
| 65 | INSERT_PADDING_BYTES(2); // ImapSystemValuesC | ||
| 66 | INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10] | ||
| 67 | INSERT_PADDING_BYTES(1); // ImapReserved | ||
| 68 | INSERT_PADDING_BYTES(3); // OmapSystemValuesA | ||
| 69 | INSERT_PADDING_BYTES(1); // OmapSystemValuesB | ||
| 70 | INSERT_PADDING_BYTES(16); // OmapGenericVector[32] | ||
| 71 | INSERT_PADDING_BYTES(2); // OmapColor | ||
| 72 | INSERT_PADDING_BYTES(2); // OmapSystemValuesC | ||
| 73 | INSERT_PADDING_BYTES(5); // OmapFixedFncTexture[10] | ||
| 74 | INSERT_PADDING_BYTES(1); // OmapReserved | ||
| 75 | } vtg; | ||
| 76 | |||
| 77 | struct { | ||
| 78 | INSERT_PADDING_BYTES(3); // ImapSystemValuesA | ||
| 79 | INSERT_PADDING_BYTES(1); // ImapSystemValuesB | ||
| 80 | INSERT_PADDING_BYTES(32); // ImapGenericVector[32] | ||
| 81 | INSERT_PADDING_BYTES(2); // ImapColor | ||
| 82 | INSERT_PADDING_BYTES(2); // ImapSystemValuesC | ||
| 83 | INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10] | ||
| 84 | INSERT_PADDING_BYTES(2); // ImapReserved | ||
| 85 | struct { | ||
| 86 | u32 target; | ||
| 87 | union { | ||
| 88 | BitField<0, 1, u32> sample_mask; | ||
| 89 | BitField<1, 1, u32> depth; | ||
| 90 | BitField<2, 30, u32> reserved; | ||
| 91 | }; | ||
| 92 | } omap; | ||
| 93 | bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { | ||
| 94 | const u32 bit = render_target * 4 + component; | ||
| 95 | return omap.target & (1 << bit); | ||
| 96 | } | ||
| 97 | } ps; | ||
| 98 | }; | ||
| 99 | }; | ||
| 100 | |||
| 101 | static_assert(sizeof(Header) == 0x50, "Incorrect structure size"); | ||
| 102 | |||
| 103 | } // namespace Tegra::Shader | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e6d8e65c6..baa8b63b7 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "video_core/engines/fermi_2d.h" | 6 | #include "video_core/engines/fermi_2d.h" |
| 7 | #include "video_core/engines/kepler_memory.h" | ||
| 7 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 8 | #include "video_core/engines/maxwell_compute.h" | 9 | #include "video_core/engines/maxwell_compute.h" |
| 9 | #include "video_core/engines/maxwell_dma.h" | 10 | #include "video_core/engines/maxwell_dma.h" |
| @@ -27,6 +28,7 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { | |||
| 27 | fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); | 28 | fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); |
| 28 | maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); | 29 | maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); |
| 29 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); | 30 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); |
| 31 | kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); | ||
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | GPU::~GPU() = default; | 34 | GPU::~GPU() = default; |
| @@ -66,6 +68,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { | |||
| 66 | case RenderTargetFormat::RGBA8_UINT: | 68 | case RenderTargetFormat::RGBA8_UINT: |
| 67 | case RenderTargetFormat::RGB10_A2_UNORM: | 69 | case RenderTargetFormat::RGB10_A2_UNORM: |
| 68 | case RenderTargetFormat::BGRA8_UNORM: | 70 | case RenderTargetFormat::BGRA8_UNORM: |
| 71 | case RenderTargetFormat::BGRA8_SRGB: | ||
| 69 | case RenderTargetFormat::RG16_UNORM: | 72 | case RenderTargetFormat::RG16_UNORM: |
| 70 | case RenderTargetFormat::RG16_SNORM: | 73 | case RenderTargetFormat::RG16_SNORM: |
| 71 | case RenderTargetFormat::RG16_UINT: | 74 | case RenderTargetFormat::RG16_UINT: |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 2c3dbd97b..5cc1e19ca 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -4,8 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <unordered_map> | 9 | #include <vector> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "core/hle/service/nvflinger/buffer_queue.h" | 11 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 11 | #include "video_core/memory_manager.h" | 12 | #include "video_core/memory_manager.h" |
| @@ -26,6 +27,7 @@ enum class RenderTargetFormat : u32 { | |||
| 26 | RG32_FLOAT = 0xCB, | 27 | RG32_FLOAT = 0xCB, |
| 27 | RG32_UINT = 0xCD, | 28 | RG32_UINT = 0xCD, |
| 28 | BGRA8_UNORM = 0xCF, | 29 | BGRA8_UNORM = 0xCF, |
| 30 | BGRA8_SRGB = 0xD0, | ||
| 29 | RGB10_A2_UNORM = 0xD1, | 31 | RGB10_A2_UNORM = 0xD1, |
| 30 | RGBA8_UNORM = 0xD5, | 32 | RGBA8_UNORM = 0xD5, |
| 31 | RGBA8_SRGB = 0xD6, | 33 | RGBA8_SRGB = 0xD6, |
| @@ -40,6 +42,7 @@ enum class RenderTargetFormat : u32 { | |||
| 40 | R32_UINT = 0xE4, | 42 | R32_UINT = 0xE4, |
| 41 | R32_FLOAT = 0xE5, | 43 | R32_FLOAT = 0xE5, |
| 42 | B5G6R5_UNORM = 0xE8, | 44 | B5G6R5_UNORM = 0xE8, |
| 45 | BGR5A1_UNORM = 0xE9, | ||
| 43 | RG8_UNORM = 0xEA, | 46 | RG8_UNORM = 0xEA, |
| 44 | RG8_SNORM = 0xEB, | 47 | RG8_SNORM = 0xEB, |
| 45 | R16_UNORM = 0xEE, | 48 | R16_UNORM = 0xEE, |
| @@ -67,6 +70,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format); | |||
| 67 | /// Returns the number of bytes per pixel of each depth format. | 70 | /// Returns the number of bytes per pixel of each depth format. |
| 68 | u32 DepthFormatBytesPerPixel(DepthFormat format); | 71 | u32 DepthFormatBytesPerPixel(DepthFormat format); |
| 69 | 72 | ||
| 73 | struct CommandListHeader; | ||
| 70 | class DebugContext; | 74 | class DebugContext; |
| 71 | 75 | ||
| 72 | /** | 76 | /** |
| @@ -99,6 +103,7 @@ class Fermi2D; | |||
| 99 | class Maxwell3D; | 103 | class Maxwell3D; |
| 100 | class MaxwellCompute; | 104 | class MaxwellCompute; |
| 101 | class MaxwellDMA; | 105 | class MaxwellDMA; |
| 106 | class KeplerMemory; | ||
| 102 | } // namespace Engines | 107 | } // namespace Engines |
| 103 | 108 | ||
| 104 | enum class EngineID { | 109 | enum class EngineID { |
| @@ -115,7 +120,7 @@ public: | |||
| 115 | ~GPU(); | 120 | ~GPU(); |
| 116 | 121 | ||
| 117 | /// Processes a command list stored at the specified address in GPU memory. | 122 | /// Processes a command list stored at the specified address in GPU memory. |
| 118 | void ProcessCommandList(GPUVAddr address, u32 size); | 123 | void ProcessCommandLists(const std::vector<CommandListHeader>& commands); |
| 119 | 124 | ||
| 120 | /// Returns a reference to the Maxwell3D GPU engine. | 125 | /// Returns a reference to the Maxwell3D GPU engine. |
| 121 | Engines::Maxwell3D& Maxwell3D(); | 126 | Engines::Maxwell3D& Maxwell3D(); |
| @@ -130,13 +135,10 @@ public: | |||
| 130 | const Tegra::MemoryManager& MemoryManager() const; | 135 | const Tegra::MemoryManager& MemoryManager() const; |
| 131 | 136 | ||
| 132 | private: | 137 | private: |
| 133 | /// Writes a single register in the engine bound to the specified subchannel | ||
| 134 | void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params); | ||
| 135 | |||
| 136 | std::unique_ptr<Tegra::MemoryManager> memory_manager; | 138 | std::unique_ptr<Tegra::MemoryManager> memory_manager; |
| 137 | 139 | ||
| 138 | /// Mapping of command subchannels to their bound engine ids. | 140 | /// Mapping of command subchannels to their bound engine ids. |
| 139 | std::unordered_map<u32, EngineID> bound_engines; | 141 | std::array<EngineID, 8> bound_engines = {}; |
| 140 | 142 | ||
| 141 | /// 3D engine | 143 | /// 3D engine |
| 142 | std::unique_ptr<Engines::Maxwell3D> maxwell_3d; | 144 | std::unique_ptr<Engines::Maxwell3D> maxwell_3d; |
| @@ -146,6 +148,8 @@ private: | |||
| 146 | std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; | 148 | std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; |
| 147 | /// DMA engine | 149 | /// DMA engine |
| 148 | std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; | 150 | std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; |
| 151 | /// Inline memory engine | ||
| 152 | std::unique_ptr<Engines::KeplerMemory> kepler_memory; | ||
| 149 | }; | 153 | }; |
| 150 | 154 | ||
| 151 | } // namespace Tegra | 155 | } // namespace Tegra |
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index 7d836b816..cee0baaf3 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h | |||
| @@ -152,7 +152,7 @@ private: | |||
| 152 | boost::optional<u32> | 152 | boost::optional<u32> |
| 153 | delayed_pc; ///< Program counter to execute at after the delay slot is executed. | 153 | delayed_pc; ///< Program counter to execute at after the delay slot is executed. |
| 154 | 154 | ||
| 155 | static constexpr size_t NumMacroRegisters = 8; | 155 | static constexpr std::size_t NumMacroRegisters = 8; |
| 156 | 156 | ||
| 157 | /// General purpose macro registers. | 157 | /// General purpose macro registers. |
| 158 | std::array<u32, NumMacroRegisters> registers = {}; | 158 | std::array<u32, NumMacroRegisters> registers = {}; |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 9d78e8b6b..cd819d69f 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -20,9 +20,6 @@ public: | |||
| 20 | /// Clear the current framebuffer | 20 | /// Clear the current framebuffer |
| 21 | virtual void Clear() = 0; | 21 | virtual void Clear() = 0; |
| 22 | 22 | ||
| 23 | /// Notify rasterizer that the specified Maxwell register has been changed | ||
| 24 | virtual void NotifyMaxwellRegisterChanged(u32 method) = 0; | ||
| 25 | |||
| 26 | /// Notify rasterizer that all caches should be flushed to Switch memory | 23 | /// Notify rasterizer that all caches should be flushed to Switch memory |
| 27 | virtual void FlushAll() = 0; | 24 | virtual void FlushAll() = 0; |
| 28 | 25 | ||
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index be17a2b9c..0df3725c2 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp | |||
| @@ -19,6 +19,7 @@ void RendererBase::RefreshBaseSettings() { | |||
| 19 | UpdateCurrentFramebufferLayout(); | 19 | UpdateCurrentFramebufferLayout(); |
| 20 | 20 | ||
| 21 | renderer_settings.use_framelimiter = Settings::values.use_frame_limit; | 21 | renderer_settings.use_framelimiter = Settings::values.use_frame_limit; |
| 22 | renderer_settings.set_background_color = true; | ||
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | void RendererBase::UpdateCurrentFramebufferLayout() { | 25 | void RendererBase::UpdateCurrentFramebufferLayout() { |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 2a357f9d0..2cd0738ff 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -19,6 +19,7 @@ namespace VideoCore { | |||
| 19 | 19 | ||
| 20 | struct RendererSettings { | 20 | struct RendererSettings { |
| 21 | std::atomic_bool use_framelimiter{false}; | 21 | std::atomic_bool use_framelimiter{false}; |
| 22 | std::atomic_bool set_background_color{false}; | ||
| 22 | }; | 23 | }; |
| 23 | 24 | ||
| 24 | class RendererBase : NonCopyable { | 25 | class RendererBase : NonCopyable { |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp new file mode 100644 index 000000000..578aca789 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -0,0 +1,93 @@ | |||
| 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 <cstring> | ||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "common/alignment.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/memory.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | ||
| 12 | |||
| 13 | namespace OpenGL { | ||
| 14 | |||
| 15 | OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {} | ||
| 16 | |||
| 17 | GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, | ||
| 18 | std::size_t alignment, bool cache) { | ||
| 19 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); | ||
| 20 | const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | ||
| 21 | |||
| 22 | // Cache management is a big overhead, so only cache entries with a given size. | ||
| 23 | // TODO: Figure out which size is the best for given games. | ||
| 24 | cache &= size >= 2048; | ||
| 25 | |||
| 26 | if (cache) { | ||
| 27 | auto entry = TryGet(*cpu_addr); | ||
| 28 | if (entry) { | ||
| 29 | if (entry->size >= size && entry->alignment == alignment) { | ||
| 30 | return entry->offset; | ||
| 31 | } | ||
| 32 | Unregister(entry); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | AlignBuffer(alignment); | ||
| 37 | GLintptr uploaded_offset = buffer_offset; | ||
| 38 | |||
| 39 | Memory::ReadBlock(*cpu_addr, buffer_ptr, size); | ||
| 40 | |||
| 41 | buffer_ptr += size; | ||
| 42 | buffer_offset += size; | ||
| 43 | |||
| 44 | if (cache) { | ||
| 45 | auto entry = std::make_shared<CachedBufferEntry>(); | ||
| 46 | entry->offset = uploaded_offset; | ||
| 47 | entry->size = size; | ||
| 48 | entry->alignment = alignment; | ||
| 49 | entry->addr = *cpu_addr; | ||
| 50 | Register(entry); | ||
| 51 | } | ||
| 52 | |||
| 53 | return uploaded_offset; | ||
| 54 | } | ||
| 55 | |||
| 56 | GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size, | ||
| 57 | std::size_t alignment) { | ||
| 58 | AlignBuffer(alignment); | ||
| 59 | std::memcpy(buffer_ptr, raw_pointer, size); | ||
| 60 | GLintptr uploaded_offset = buffer_offset; | ||
| 61 | |||
| 62 | buffer_ptr += size; | ||
| 63 | buffer_offset += size; | ||
| 64 | return uploaded_offset; | ||
| 65 | } | ||
| 66 | |||
| 67 | void OGLBufferCache::Map(std::size_t max_size) { | ||
| 68 | bool invalidate; | ||
| 69 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = | ||
| 70 | stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4); | ||
| 71 | buffer_offset = buffer_offset_base; | ||
| 72 | |||
| 73 | if (invalidate) { | ||
| 74 | InvalidateAll(); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | void OGLBufferCache::Unmap() { | ||
| 78 | stream_buffer.Unmap(buffer_offset - buffer_offset_base); | ||
| 79 | } | ||
| 80 | |||
| 81 | GLuint OGLBufferCache::GetHandle() const { | ||
| 82 | return stream_buffer.GetHandle(); | ||
| 83 | } | ||
| 84 | |||
| 85 | void OGLBufferCache::AlignBuffer(std::size_t alignment) { | ||
| 86 | // Align the offset, not the mapped pointer | ||
| 87 | GLintptr offset_aligned = | ||
| 88 | static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); | ||
| 89 | buffer_ptr += offset_aligned - buffer_offset; | ||
| 90 | buffer_offset = offset_aligned; | ||
| 91 | } | ||
| 92 | |||
| 93 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h new file mode 100644 index 000000000..6c18461f4 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -0,0 +1,57 @@ | |||
| 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 <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/rasterizer_cache.h" | ||
| 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 13 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | ||
| 14 | |||
| 15 | namespace OpenGL { | ||
| 16 | |||
| 17 | struct CachedBufferEntry final { | ||
| 18 | VAddr GetAddr() const { | ||
| 19 | return addr; | ||
| 20 | } | ||
| 21 | |||
| 22 | std::size_t GetSizeInBytes() const { | ||
| 23 | return size; | ||
| 24 | } | ||
| 25 | |||
| 26 | VAddr addr; | ||
| 27 | std::size_t size; | ||
| 28 | GLintptr offset; | ||
| 29 | std::size_t alignment; | ||
| 30 | }; | ||
| 31 | |||
| 32 | class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { | ||
| 33 | public: | ||
| 34 | explicit OGLBufferCache(std::size_t size); | ||
| 35 | |||
| 36 | GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, | ||
| 37 | bool cache = true); | ||
| 38 | |||
| 39 | GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4); | ||
| 40 | |||
| 41 | void Map(std::size_t max_size); | ||
| 42 | void Unmap(); | ||
| 43 | |||
| 44 | GLuint GetHandle() const; | ||
| 45 | |||
| 46 | protected: | ||
| 47 | void AlignBuffer(std::size_t alignment); | ||
| 48 | |||
| 49 | private: | ||
| 50 | OGLStreamBuffer stream_buffer; | ||
| 51 | |||
| 52 | u8* buffer_ptr = nullptr; | ||
| 53 | GLintptr buffer_offset = 0; | ||
| 54 | GLintptr buffer_offset_base = 0; | ||
| 55 | }; | ||
| 56 | |||
| 57 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7ce969f73..70fb54507 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <string> | 8 | #include <string> |
| 8 | #include <string_view> | 9 | #include <string_view> |
| @@ -33,16 +34,19 @@ using PixelFormat = SurfaceParams::PixelFormat; | |||
| 33 | using SurfaceType = SurfaceParams::SurfaceType; | 34 | using SurfaceType = SurfaceParams::SurfaceType; |
| 34 | 35 | ||
| 35 | MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); | 36 | MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); |
| 36 | MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192)); | 37 | MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192)); |
| 37 | MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192)); | 38 | MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192)); |
| 39 | MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192)); | ||
| 40 | MICROPROFILE_DEFINE(OpenGL_Texture, "OpenGL", "Texture Setup", MP_RGB(128, 128, 192)); | ||
| 41 | MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(128, 128, 192)); | ||
| 38 | MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); | 42 | MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); |
| 39 | MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); | 43 | MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192)); |
| 40 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); | 44 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); |
| 41 | 45 | ||
| 42 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) | 46 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) |
| 43 | : emu_window{window}, screen_info{info}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) { | 47 | : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { |
| 44 | // Create sampler objects | 48 | // Create sampler objects |
| 45 | for (size_t i = 0; i < texture_samplers.size(); ++i) { | 49 | for (std::size_t i = 0; i < texture_samplers.size(); ++i) { |
| 46 | texture_samplers[i].Create(); | 50 | texture_samplers[i].Create(); |
| 47 | state.texture_units[i].sampler = texture_samplers[i].sampler.handle; | 51 | state.texture_units[i].sampler = texture_samplers[i].sampler.handle; |
| 48 | } | 52 | } |
| @@ -55,6 +59,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo | |||
| 55 | 59 | ||
| 56 | if (extension == "GL_ARB_direct_state_access") { | 60 | if (extension == "GL_ARB_direct_state_access") { |
| 57 | has_ARB_direct_state_access = true; | 61 | has_ARB_direct_state_access = true; |
| 62 | } else if (extension == "GL_ARB_multi_bind") { | ||
| 63 | has_ARB_multi_bind = true; | ||
| 58 | } else if (extension == "GL_ARB_separate_shader_objects") { | 64 | } else if (extension == "GL_ARB_separate_shader_objects") { |
| 59 | has_ARB_separate_shader_objects = true; | 65 | has_ARB_separate_shader_objects = true; |
| 60 | } else if (extension == "GL_ARB_vertex_attrib_binding") { | 66 | } else if (extension == "GL_ARB_vertex_attrib_binding") { |
| @@ -67,28 +73,13 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo | |||
| 67 | // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 | 73 | // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 |
| 68 | state.clip_distance[0] = true; | 74 | state.clip_distance[0] = true; |
| 69 | 75 | ||
| 70 | // Generate VAO and UBO | ||
| 71 | sw_vao.Create(); | ||
| 72 | uniform_buffer.Create(); | ||
| 73 | |||
| 74 | state.draw.vertex_array = sw_vao.handle; | ||
| 75 | state.draw.uniform_buffer = uniform_buffer.handle; | ||
| 76 | state.Apply(); | ||
| 77 | |||
| 78 | // Create render framebuffer | 76 | // Create render framebuffer |
| 79 | framebuffer.Create(); | 77 | framebuffer.Create(); |
| 80 | 78 | ||
| 81 | hw_vao.Create(); | ||
| 82 | |||
| 83 | state.draw.vertex_buffer = stream_buffer.GetHandle(); | ||
| 84 | |||
| 85 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); | 79 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); |
| 86 | state.draw.shader_program = 0; | 80 | state.draw.shader_program = 0; |
| 87 | state.draw.vertex_array = hw_vao.handle; | ||
| 88 | state.Apply(); | 81 | state.Apply(); |
| 89 | 82 | ||
| 90 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle()); | ||
| 91 | |||
| 92 | glEnable(GL_BLEND); | 83 | glEnable(GL_BLEND); |
| 93 | 84 | ||
| 94 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); | 85 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); |
| @@ -98,14 +89,60 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo | |||
| 98 | 89 | ||
| 99 | RasterizerOpenGL::~RasterizerOpenGL() {} | 90 | RasterizerOpenGL::~RasterizerOpenGL() {} |
| 100 | 91 | ||
| 101 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | 92 | void RasterizerOpenGL::SetupVertexArrays() { |
| 102 | GLintptr buffer_offset) { | ||
| 103 | MICROPROFILE_SCOPE(OpenGL_VAO); | 93 | MICROPROFILE_SCOPE(OpenGL_VAO); |
| 104 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 94 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 105 | const auto& regs = gpu.regs; | 95 | const auto& regs = gpu.regs; |
| 106 | 96 | ||
| 107 | state.draw.vertex_array = hw_vao.handle; | 97 | auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format); |
| 108 | state.draw.vertex_buffer = stream_buffer.GetHandle(); | 98 | auto& VAO = iter->second; |
| 99 | |||
| 100 | if (is_cache_miss) { | ||
| 101 | VAO.Create(); | ||
| 102 | state.draw.vertex_array = VAO.handle; | ||
| 103 | state.Apply(); | ||
| 104 | |||
| 105 | // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work | ||
| 106 | // around. | ||
| 107 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle()); | ||
| 108 | |||
| 109 | // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. | ||
| 110 | // Enables the first 16 vertex attributes always, as we don't know which ones are actually | ||
| 111 | // used until shader time. Note, Tegra technically supports 32, but we're capping this to 16 | ||
| 112 | // for now to avoid OpenGL errors. | ||
| 113 | // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't | ||
| 114 | // assume every shader uses them all. | ||
| 115 | for (unsigned index = 0; index < 16; ++index) { | ||
| 116 | const auto& attrib = regs.vertex_attrib_format[index]; | ||
| 117 | |||
| 118 | // Ignore invalid attributes. | ||
| 119 | if (!attrib.IsValid()) | ||
| 120 | continue; | ||
| 121 | |||
| 122 | const auto& buffer = regs.vertex_array[attrib.buffer]; | ||
| 123 | LOG_TRACE(HW_GPU, | ||
| 124 | "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", | ||
| 125 | index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), | ||
| 126 | attrib.offset.Value(), attrib.IsNormalized()); | ||
| 127 | |||
| 128 | ASSERT(buffer.IsEnabled()); | ||
| 129 | |||
| 130 | glEnableVertexAttribArray(index); | ||
| 131 | if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt || | ||
| 132 | attrib.type == | ||
| 133 | Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) { | ||
| 134 | glVertexAttribIFormat(index, attrib.ComponentCount(), | ||
| 135 | MaxwellToGL::VertexType(attrib), attrib.offset); | ||
| 136 | } else { | ||
| 137 | glVertexAttribFormat(index, attrib.ComponentCount(), | ||
| 138 | MaxwellToGL::VertexType(attrib), | ||
| 139 | attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); | ||
| 140 | } | ||
| 141 | glVertexAttribBinding(index, attrib.buffer); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | state.draw.vertex_array = VAO.handle; | ||
| 145 | state.draw.vertex_buffer = buffer_cache.GetHandle(); | ||
| 109 | state.Apply(); | 146 | state.Apply(); |
| 110 | 147 | ||
| 111 | // Upload all guest vertex arrays sequentially to our buffer | 148 | // Upload all guest vertex arrays sequentially to our buffer |
| @@ -117,77 +154,35 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | |||
| 117 | Tegra::GPUVAddr start = vertex_array.StartAddress(); | 154 | Tegra::GPUVAddr start = vertex_array.StartAddress(); |
| 118 | const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 155 | const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 119 | 156 | ||
| 120 | if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { | ||
| 121 | start += vertex_array.stride * (gpu.state.current_instance / vertex_array.divisor); | ||
| 122 | } | ||
| 123 | |||
| 124 | ASSERT(end > start); | 157 | ASSERT(end > start); |
| 125 | u64 size = end - start + 1; | 158 | const u64 size = end - start + 1; |
| 126 | 159 | const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size); | |
| 127 | GLintptr vertex_buffer_offset; | ||
| 128 | std::tie(array_ptr, buffer_offset, vertex_buffer_offset) = | ||
| 129 | UploadMemory(array_ptr, buffer_offset, start, size); | ||
| 130 | 160 | ||
| 131 | // Bind the vertex array to the buffer at the current offset. | 161 | // Bind the vertex array to the buffer at the current offset. |
| 132 | glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset, | 162 | glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset, |
| 133 | vertex_array.stride); | 163 | vertex_array.stride); |
| 134 | 164 | ||
| 135 | if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { | 165 | if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { |
| 136 | // Tell OpenGL that this is an instanced vertex buffer to prevent accessing different | 166 | // Enable vertex buffer instancing with the specified divisor. |
| 137 | // indexes on each vertex. We do the instance indexing manually by incrementing the | 167 | glVertexBindingDivisor(index, vertex_array.divisor); |
| 138 | // start address of the vertex buffer. | ||
| 139 | glVertexBindingDivisor(index, 1); | ||
| 140 | } else { | 168 | } else { |
| 141 | // Disable the vertex buffer instancing. | 169 | // Disable the vertex buffer instancing. |
| 142 | glVertexBindingDivisor(index, 0); | 170 | glVertexBindingDivisor(index, 0); |
| 143 | } | 171 | } |
| 144 | } | 172 | } |
| 145 | |||
| 146 | // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. | ||
| 147 | // Enables the first 16 vertex attributes always, as we don't know which ones are actually used | ||
| 148 | // until shader time. Note, Tegra technically supports 32, but we're capping this to 16 for now | ||
| 149 | // to avoid OpenGL errors. | ||
| 150 | // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't | ||
| 151 | // assume every shader uses them all. | ||
| 152 | for (unsigned index = 0; index < 16; ++index) { | ||
| 153 | auto& attrib = regs.vertex_attrib_format[index]; | ||
| 154 | |||
| 155 | // Ignore invalid attributes. | ||
| 156 | if (!attrib.IsValid()) | ||
| 157 | continue; | ||
| 158 | |||
| 159 | auto& buffer = regs.vertex_array[attrib.buffer]; | ||
| 160 | LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", | ||
| 161 | index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), | ||
| 162 | attrib.offset.Value(), attrib.IsNormalized()); | ||
| 163 | |||
| 164 | ASSERT(buffer.IsEnabled()); | ||
| 165 | |||
| 166 | glEnableVertexAttribArray(index); | ||
| 167 | if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt || | ||
| 168 | attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) { | ||
| 169 | glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), | ||
| 170 | attrib.offset); | ||
| 171 | } else { | ||
| 172 | glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), | ||
| 173 | attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); | ||
| 174 | } | ||
| 175 | glVertexAttribBinding(index, attrib.buffer); | ||
| 176 | } | ||
| 177 | |||
| 178 | return {array_ptr, buffer_offset}; | ||
| 179 | } | 173 | } |
| 180 | 174 | ||
| 181 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | 175 | void RasterizerOpenGL::SetupShaders() { |
| 182 | auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 176 | MICROPROFILE_SCOPE(OpenGL_Shader); |
| 177 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | ||
| 183 | 178 | ||
| 184 | // Next available bindpoints to use when uploading the const buffers and textures to the GLSL | 179 | // Next available bindpoints to use when uploading the const buffers and textures to the GLSL |
| 185 | // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. | 180 | // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. |
| 186 | u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; | 181 | u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; |
| 187 | u32 current_texture_bindpoint = 0; | 182 | u32 current_texture_bindpoint = 0; |
| 188 | 183 | ||
| 189 | for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 184 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| 190 | auto& shader_config = gpu.regs.shader_config[index]; | 185 | const auto& shader_config = gpu.regs.shader_config[index]; |
| 191 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; | 186 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; |
| 192 | 187 | ||
| 193 | // Skip stages that are not enabled | 188 | // Skip stages that are not enabled |
| @@ -195,21 +190,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr | |||
| 195 | continue; | 190 | continue; |
| 196 | } | 191 | } |
| 197 | 192 | ||
| 198 | std::tie(buffer_ptr, buffer_offset) = | 193 | const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 |
| 199 | AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment)); | ||
| 200 | |||
| 201 | const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 | ||
| 202 | 194 | ||
| 203 | GLShader::MaxwellUniformData ubo{}; | 195 | GLShader::MaxwellUniformData ubo{}; |
| 204 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); | 196 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); |
| 205 | std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); | 197 | const GLintptr offset = buffer_cache.UploadHostMemory( |
| 198 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); | ||
| 206 | 199 | ||
| 207 | // Bind the buffer | 200 | // Bind the buffer |
| 208 | glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset, | 201 | glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); |
| 209 | sizeof(ubo)); | ||
| 210 | |||
| 211 | buffer_ptr += sizeof(ubo); | ||
| 212 | buffer_offset += sizeof(ubo); | ||
| 213 | 202 | ||
| 214 | Shader shader{shader_cache.GetStageProgram(program)}; | 203 | Shader shader{shader_cache.GetStageProgram(program)}; |
| 215 | 204 | ||
| @@ -230,9 +219,8 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr | |||
| 230 | } | 219 | } |
| 231 | 220 | ||
| 232 | // Configure the const buffers for this shader stage. | 221 | // Configure the const buffers for this shader stage. |
| 233 | std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) = | 222 | current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), |
| 234 | SetupConstBuffers(buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage), | 223 | shader, current_constbuffer_bindpoint); |
| 235 | shader, current_constbuffer_bindpoint); | ||
| 236 | 224 | ||
| 237 | // Configure the textures for this shader stage. | 225 | // Configure the textures for this shader stage. |
| 238 | current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, | 226 | current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, |
| @@ -245,15 +233,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr | |||
| 245 | } | 233 | } |
| 246 | } | 234 | } |
| 247 | 235 | ||
| 248 | shader_program_manager->UseTrivialGeometryShader(); | 236 | state.Apply(); |
| 249 | 237 | ||
| 250 | return {buffer_ptr, buffer_offset}; | 238 | shader_program_manager->UseTrivialGeometryShader(); |
| 251 | } | 239 | } |
| 252 | 240 | ||
| 253 | size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | 241 | std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
| 254 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 242 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 255 | 243 | ||
| 256 | size_t size = 0; | 244 | std::size_t size = 0; |
| 257 | for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { | 245 | for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 258 | if (!regs.vertex_array[index].IsEnabled()) | 246 | if (!regs.vertex_array[index].IsEnabled()) |
| 259 | continue; | 247 | continue; |
| @@ -309,60 +297,80 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { | |||
| 309 | cached_pages.add({pages_interval, delta}); | 297 | cached_pages.add({pages_interval, delta}); |
| 310 | } | 298 | } |
| 311 | 299 | ||
| 312 | std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, | 300 | void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, |
| 313 | bool using_depth_fb, | 301 | bool preserve_contents, |
| 314 | bool preserve_contents) { | 302 | boost::optional<std::size_t> single_color_target) { |
| 303 | MICROPROFILE_SCOPE(OpenGL_Framebuffer); | ||
| 315 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 304 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 316 | 305 | ||
| 317 | if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) { | 306 | Surface depth_surface; |
| 318 | LOG_ERROR(HW_GPU, "RenderTargetFormat is not configured"); | 307 | if (using_depth_fb) { |
| 319 | using_color_fb = false; | 308 | depth_surface = res_cache.GetDepthBufferSurface(preserve_contents); |
| 320 | } | 309 | } |
| 321 | 310 | ||
| 322 | const bool has_stencil = regs.stencil_enable; | 311 | // TODO(bunnei): Figure out how the below register works. According to envytools, this should be |
| 323 | const bool write_color_fb = | 312 | // used to enable multiple render targets. However, it is left unset on all games that I have |
| 324 | state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || | 313 | // tested. |
| 325 | state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE; | 314 | ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented"); |
| 326 | 315 | ||
| 327 | const bool write_depth_fb = | 316 | // Bind the framebuffer surfaces |
| 328 | (state.depth.test_enabled && state.depth.write_mask == GL_TRUE) || | 317 | state.draw.draw_framebuffer = framebuffer.handle; |
| 329 | (has_stencil && (state.stencil.front.write_mask || state.stencil.back.write_mask)); | 318 | state.Apply(); |
| 330 | |||
| 331 | Surface color_surface; | ||
| 332 | Surface depth_surface; | ||
| 333 | MathUtil::Rectangle<u32> surfaces_rect; | ||
| 334 | std::tie(color_surface, depth_surface, surfaces_rect) = | ||
| 335 | res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, preserve_contents); | ||
| 336 | 319 | ||
| 337 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; | 320 | if (using_color_fb) { |
| 338 | const MathUtil::Rectangle<u32> draw_rect{ | 321 | if (single_color_target) { |
| 339 | static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left, | 322 | // Used when just a single color attachment is enabled, e.g. for clearing a color buffer |
| 340 | surfaces_rect.left, surfaces_rect.right)), // Left | 323 | Surface color_surface = |
| 341 | static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top, | 324 | res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); |
| 342 | surfaces_rect.bottom, surfaces_rect.top)), // Top | 325 | glFramebufferTexture2D( |
| 343 | static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right, | 326 | GL_DRAW_FRAMEBUFFER, |
| 344 | surfaces_rect.left, surfaces_rect.right)), // Right | 327 | GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, |
| 345 | static_cast<u32>( | 328 | color_surface != nullptr ? color_surface->Texture().handle : 0, 0); |
| 346 | std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom, | 329 | glDrawBuffer(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target)); |
| 347 | surfaces_rect.bottom, surfaces_rect.top))}; // Bottom | 330 | } else { |
| 331 | // Multiple color attachments are enabled | ||
| 332 | std::array<GLenum, Maxwell::NumRenderTargets> buffers; | ||
| 333 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | ||
| 334 | Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); | ||
| 335 | buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); | ||
| 336 | glFramebufferTexture2D( | ||
| 337 | GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), | ||
| 338 | GL_TEXTURE_2D, color_surface != nullptr ? color_surface->Texture().handle : 0, | ||
| 339 | 0); | ||
| 340 | } | ||
| 341 | glDrawBuffers(regs.rt_control.count, buffers.data()); | ||
| 342 | } | ||
| 343 | } else { | ||
| 344 | // No color attachments are enabled - zero out all of them | ||
| 345 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | ||
| 346 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, | ||
| 347 | GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D, | ||
| 348 | 0, 0); | ||
| 349 | } | ||
| 350 | glDrawBuffer(GL_NONE); | ||
| 351 | } | ||
| 348 | 352 | ||
| 349 | // Bind the framebuffer surfaces | 353 | if (depth_surface) { |
| 350 | BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); | 354 | if (regs.stencil_enable) { |
| 355 | // Attach both depth and stencil | ||
| 356 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||
| 357 | depth_surface->Texture().handle, 0); | ||
| 358 | } else { | ||
| 359 | // Attach depth | ||
| 360 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | ||
| 361 | depth_surface->Texture().handle, 0); | ||
| 362 | // Clear stencil attachment | ||
| 363 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||
| 364 | } | ||
| 365 | } else { | ||
| 366 | // Clear both depth and stencil attachment | ||
| 367 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||
| 368 | 0); | ||
| 369 | } | ||
| 351 | 370 | ||
| 352 | SyncViewport(surfaces_rect); | 371 | SyncViewport(); |
| 353 | 372 | ||
| 354 | // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable | ||
| 355 | // scissor test to prevent drawing outside of the framebuffer region | ||
| 356 | state.scissor.enabled = true; | ||
| 357 | state.scissor.x = draw_rect.left; | ||
| 358 | state.scissor.y = draw_rect.bottom; | ||
| 359 | state.scissor.width = draw_rect.GetWidth(); | ||
| 360 | state.scissor.height = draw_rect.GetHeight(); | ||
| 361 | state.Apply(); | 373 | state.Apply(); |
| 362 | |||
| 363 | // Only return the surface to be marked as dirty if writing to it is enabled. | ||
| 364 | return std::make_pair(write_color_fb ? color_surface : nullptr, | ||
| 365 | write_depth_fb ? depth_surface : nullptr); | ||
| 366 | } | 374 | } |
| 367 | 375 | ||
| 368 | void RasterizerOpenGL::Clear() { | 376 | void RasterizerOpenGL::Clear() { |
| @@ -370,32 +378,24 @@ void RasterizerOpenGL::Clear() { | |||
| 370 | SCOPE_EXIT({ prev_state.Apply(); }); | 378 | SCOPE_EXIT({ prev_state.Apply(); }); |
| 371 | 379 | ||
| 372 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 380 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 373 | bool use_color_fb = false; | 381 | bool use_color{}; |
| 374 | bool use_depth_fb = false; | 382 | bool use_depth{}; |
| 383 | bool use_stencil{}; | ||
| 375 | 384 | ||
| 376 | OpenGLState clear_state; | 385 | OpenGLState clear_state; |
| 377 | clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer; | 386 | clear_state.draw.draw_framebuffer = framebuffer.handle; |
| 378 | clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; | 387 | clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; |
| 379 | clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; | 388 | clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; |
| 380 | clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; | 389 | clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; |
| 381 | clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; | 390 | clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; |
| 382 | 391 | ||
| 383 | GLbitfield clear_mask{}; | ||
| 384 | if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || | 392 | if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || |
| 385 | regs.clear_buffers.A) { | 393 | regs.clear_buffers.A) { |
| 386 | if (regs.clear_buffers.RT == 0) { | 394 | use_color = true; |
| 387 | // We only support clearing the first color attachment for now | ||
| 388 | clear_mask |= GL_COLOR_BUFFER_BIT; | ||
| 389 | use_color_fb = true; | ||
| 390 | } else { | ||
| 391 | // TODO(subv): Add support for the other color attachments | ||
| 392 | LOG_CRITICAL(HW_GPU, "Clear unimplemented for RT {}", regs.clear_buffers.RT); | ||
| 393 | } | ||
| 394 | } | 395 | } |
| 395 | if (regs.clear_buffers.Z) { | 396 | if (regs.clear_buffers.Z) { |
| 396 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); | 397 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); |
| 397 | use_depth_fb = true; | 398 | use_depth = true; |
| 398 | clear_mask |= GL_DEPTH_BUFFER_BIT; | ||
| 399 | 399 | ||
| 400 | // Always enable the depth write when clearing the depth buffer. The depth write mask is | 400 | // Always enable the depth write when clearing the depth buffer. The depth write mask is |
| 401 | // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. | 401 | // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. |
| @@ -404,59 +404,33 @@ void RasterizerOpenGL::Clear() { | |||
| 404 | } | 404 | } |
| 405 | if (regs.clear_buffers.S) { | 405 | if (regs.clear_buffers.S) { |
| 406 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); | 406 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); |
| 407 | use_depth_fb = true; | 407 | use_stencil = true; |
| 408 | clear_mask |= GL_STENCIL_BUFFER_BIT; | ||
| 409 | clear_state.stencil.test_enabled = true; | 408 | clear_state.stencil.test_enabled = true; |
| 410 | } | 409 | } |
| 411 | 410 | ||
| 412 | if (!use_color_fb && !use_depth_fb) { | 411 | if (!use_color && !use_depth && !use_stencil) { |
| 413 | // No color surface nor depth/stencil surface are enabled | 412 | // No color surface nor depth/stencil surface are enabled |
| 414 | return; | 413 | return; |
| 415 | } | 414 | } |
| 416 | 415 | ||
| 417 | if (clear_mask == 0) { | ||
| 418 | // No clear mask is enabled | ||
| 419 | return; | ||
| 420 | } | ||
| 421 | |||
| 422 | ScopeAcquireGLContext acquire_context{emu_window}; | 416 | ScopeAcquireGLContext acquire_context{emu_window}; |
| 423 | 417 | ||
| 424 | auto [dirty_color_surface, dirty_depth_surface] = | 418 | ConfigureFramebuffers(use_color, use_depth || use_stencil, false, |
| 425 | ConfigureFramebuffers(use_color_fb, use_depth_fb, false); | 419 | regs.clear_buffers.RT.Value()); |
| 426 | 420 | ||
| 427 | clear_state.Apply(); | 421 | clear_state.Apply(); |
| 428 | 422 | ||
| 429 | glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], | 423 | if (use_color) { |
| 430 | regs.clear_color[3]); | 424 | glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); |
| 431 | glClearDepth(regs.clear_depth); | 425 | } |
| 432 | glClearStencil(regs.clear_stencil); | ||
| 433 | |||
| 434 | glClear(clear_mask); | ||
| 435 | } | ||
| 436 | |||
| 437 | std::pair<u8*, GLintptr> RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, | ||
| 438 | size_t alignment) { | ||
| 439 | // Align the offset, not the mapped pointer | ||
| 440 | GLintptr offset_aligned = | ||
| 441 | static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment)); | ||
| 442 | return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned}; | ||
| 443 | } | ||
| 444 | |||
| 445 | std::tuple<u8*, GLintptr, GLintptr> RasterizerOpenGL::UploadMemory(u8* buffer_ptr, | ||
| 446 | GLintptr buffer_offset, | ||
| 447 | Tegra::GPUVAddr gpu_addr, | ||
| 448 | size_t size, size_t alignment) { | ||
| 449 | std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment); | ||
| 450 | GLintptr uploaded_offset = buffer_offset; | ||
| 451 | |||
| 452 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); | ||
| 453 | const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | ||
| 454 | Memory::ReadBlock(*cpu_addr, buffer_ptr, size); | ||
| 455 | |||
| 456 | buffer_ptr += size; | ||
| 457 | buffer_offset += size; | ||
| 458 | 426 | ||
| 459 | return {buffer_ptr, buffer_offset, uploaded_offset}; | 427 | if (use_depth && use_stencil) { |
| 428 | glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil); | ||
| 429 | } else if (use_depth) { | ||
| 430 | glClearBufferfv(GL_DEPTH, 0, ®s.clear_depth); | ||
| 431 | } else if (use_stencil) { | ||
| 432 | glClearBufferiv(GL_STENCIL, 0, ®s.clear_stencil); | ||
| 433 | } | ||
| 460 | } | 434 | } |
| 461 | 435 | ||
| 462 | void RasterizerOpenGL::DrawArrays() { | 436 | void RasterizerOpenGL::DrawArrays() { |
| @@ -464,12 +438,12 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 464 | return; | 438 | return; |
| 465 | 439 | ||
| 466 | MICROPROFILE_SCOPE(OpenGL_Drawing); | 440 | MICROPROFILE_SCOPE(OpenGL_Drawing); |
| 467 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 441 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 442 | const auto& regs = gpu.regs; | ||
| 468 | 443 | ||
| 469 | ScopeAcquireGLContext acquire_context{emu_window}; | 444 | ScopeAcquireGLContext acquire_context{emu_window}; |
| 470 | 445 | ||
| 471 | auto [dirty_color_surface, dirty_depth_surface] = | 446 | ConfigureFramebuffers(); |
| 472 | ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0, true); | ||
| 473 | 447 | ||
| 474 | SyncDepthTestState(); | 448 | SyncDepthTestState(); |
| 475 | SyncStencilTestState(); | 449 | SyncStencilTestState(); |
| @@ -482,43 +456,46 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 482 | 456 | ||
| 483 | // Draw the vertex batch | 457 | // Draw the vertex batch |
| 484 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; | 458 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; |
| 485 | const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; | 459 | const u64 index_buffer_size{static_cast<u64>(regs.index_array.count) * |
| 460 | static_cast<u64>(regs.index_array.FormatSizeInBytes())}; | ||
| 486 | 461 | ||
| 487 | state.draw.vertex_buffer = stream_buffer.GetHandle(); | 462 | state.draw.vertex_buffer = buffer_cache.GetHandle(); |
| 488 | state.Apply(); | 463 | state.Apply(); |
| 489 | 464 | ||
| 490 | size_t buffer_size = CalculateVertexArraysSize(); | 465 | std::size_t buffer_size = CalculateVertexArraysSize(); |
| 491 | 466 | ||
| 492 | if (is_indexed) { | 467 | if (is_indexed) { |
| 493 | buffer_size = Common::AlignUp<size_t>(buffer_size, 4) + index_buffer_size; | 468 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + index_buffer_size; |
| 494 | } | 469 | } |
| 495 | 470 | ||
| 496 | // Uniform space for the 5 shader stages | 471 | // Uniform space for the 5 shader stages |
| 497 | buffer_size = | 472 | buffer_size = |
| 498 | Common::AlignUp<size_t>(buffer_size, 4) + | 473 | Common::AlignUp<std::size_t>(buffer_size, 4) + |
| 499 | (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; | 474 | (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; |
| 500 | 475 | ||
| 501 | // Add space for at least 18 constant buffers | 476 | // Add space for at least 18 constant buffers |
| 502 | buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); | 477 | buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); |
| 503 | 478 | ||
| 504 | u8* buffer_ptr; | 479 | buffer_cache.Map(buffer_size); |
| 505 | GLintptr buffer_offset; | ||
| 506 | std::tie(buffer_ptr, buffer_offset, std::ignore) = | ||
| 507 | stream_buffer.Map(static_cast<GLsizeiptr>(buffer_size), 4); | ||
| 508 | u8* buffer_ptr_base = buffer_ptr; | ||
| 509 | 480 | ||
| 510 | std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); | 481 | SetupVertexArrays(); |
| 511 | 482 | ||
| 512 | // If indexed mode, copy the index buffer | 483 | // If indexed mode, copy the index buffer |
| 513 | GLintptr index_buffer_offset = 0; | 484 | GLintptr index_buffer_offset = 0; |
| 514 | if (is_indexed) { | 485 | if (is_indexed) { |
| 515 | std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory( | 486 | MICROPROFILE_SCOPE(OpenGL_Index); |
| 516 | buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size); | 487 | |
| 488 | // Adjust the index buffer offset so it points to the first desired index. | ||
| 489 | auto index_start = regs.index_array.StartAddress(); | ||
| 490 | index_start += static_cast<size_t>(regs.index_array.first) * | ||
| 491 | static_cast<size_t>(regs.index_array.FormatSizeInBytes()); | ||
| 492 | |||
| 493 | index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size); | ||
| 517 | } | 494 | } |
| 518 | 495 | ||
| 519 | std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset); | 496 | SetupShaders(); |
| 520 | 497 | ||
| 521 | stream_buffer.Unmap(buffer_ptr - buffer_ptr_base); | 498 | buffer_cache.Unmap(); |
| 522 | 499 | ||
| 523 | shader_program_manager->ApplyTo(state); | 500 | shader_program_manager->ApplyTo(state); |
| 524 | state.Apply(); | 501 | state.Apply(); |
| @@ -527,14 +504,26 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 527 | if (is_indexed) { | 504 | if (is_indexed) { |
| 528 | const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; | 505 | const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; |
| 529 | 506 | ||
| 530 | // Adjust the index buffer offset so it points to the first desired index. | 507 | if (gpu.state.current_instance > 0) { |
| 531 | index_buffer_offset += regs.index_array.first * regs.index_array.FormatSizeInBytes(); | 508 | glDrawElementsInstancedBaseVertexBaseInstance( |
| 532 | 509 | primitive_mode, regs.index_array.count, | |
| 533 | glDrawElementsBaseVertex(primitive_mode, regs.index_array.count, | 510 | MaxwellToGL::IndexFormat(regs.index_array.format), |
| 534 | MaxwellToGL::IndexFormat(regs.index_array.format), | 511 | reinterpret_cast<const void*>(index_buffer_offset), 1, base_vertex, |
| 535 | reinterpret_cast<const void*>(index_buffer_offset), base_vertex); | 512 | gpu.state.current_instance); |
| 513 | } else { | ||
| 514 | glDrawElementsBaseVertex(primitive_mode, regs.index_array.count, | ||
| 515 | MaxwellToGL::IndexFormat(regs.index_array.format), | ||
| 516 | reinterpret_cast<const void*>(index_buffer_offset), | ||
| 517 | base_vertex); | ||
| 518 | } | ||
| 536 | } else { | 519 | } else { |
| 537 | glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count); | 520 | if (gpu.state.current_instance > 0) { |
| 521 | glDrawArraysInstancedBaseInstance(primitive_mode, regs.vertex_buffer.first, | ||
| 522 | regs.vertex_buffer.count, 1, | ||
| 523 | gpu.state.current_instance); | ||
| 524 | } else { | ||
| 525 | glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count); | ||
| 526 | } | ||
| 538 | } | 527 | } |
| 539 | 528 | ||
| 540 | // Disable scissor test | 529 | // Disable scissor test |
| @@ -549,24 +538,18 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 549 | state.Apply(); | 538 | state.Apply(); |
| 550 | } | 539 | } |
| 551 | 540 | ||
| 552 | void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {} | 541 | void RasterizerOpenGL::FlushAll() {} |
| 553 | 542 | ||
| 554 | void RasterizerOpenGL::FlushAll() { | 543 | void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} |
| 555 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 556 | } | ||
| 557 | |||
| 558 | void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { | ||
| 559 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 560 | } | ||
| 561 | 544 | ||
| 562 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { | 545 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { |
| 563 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 546 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 564 | res_cache.InvalidateRegion(addr, size); | 547 | res_cache.InvalidateRegion(addr, size); |
| 565 | shader_cache.InvalidateRegion(addr, size); | 548 | shader_cache.InvalidateRegion(addr, size); |
| 549 | buffer_cache.InvalidateRegion(addr, size); | ||
| 566 | } | 550 | } |
| 567 | 551 | ||
| 568 | void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 552 | void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 569 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 570 | InvalidateRegion(addr, size); | 553 | InvalidateRegion(addr, size); |
| 571 | } | 554 | } |
| 572 | 555 | ||
| @@ -614,7 +597,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, | |||
| 614 | void RasterizerOpenGL::SamplerInfo::Create() { | 597 | void RasterizerOpenGL::SamplerInfo::Create() { |
| 615 | sampler.Create(); | 598 | sampler.Create(); |
| 616 | mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; | 599 | mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; |
| 617 | wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap; | 600 | wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap; |
| 618 | 601 | ||
| 619 | // default is GL_LINEAR_MIPMAP_LINEAR | 602 | // default is GL_LINEAR_MIPMAP_LINEAR |
| 620 | glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 603 | glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| @@ -622,7 +605,7 @@ void RasterizerOpenGL::SamplerInfo::Create() { | |||
| 622 | } | 605 | } |
| 623 | 606 | ||
| 624 | void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { | 607 | void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { |
| 625 | GLuint s = sampler.handle; | 608 | const GLuint s = sampler.handle; |
| 626 | 609 | ||
| 627 | if (mag_filter != config.mag_filter) { | 610 | if (mag_filter != config.mag_filter) { |
| 628 | mag_filter = config.mag_filter; | 611 | mag_filter = config.mag_filter; |
| @@ -641,8 +624,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr | |||
| 641 | wrap_v = config.wrap_v; | 624 | wrap_v = config.wrap_v; |
| 642 | glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v)); | 625 | glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v)); |
| 643 | } | 626 | } |
| 627 | if (wrap_p != config.wrap_p) { | ||
| 628 | wrap_p = config.wrap_p; | ||
| 629 | glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p)); | ||
| 630 | } | ||
| 644 | 631 | ||
| 645 | if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) { | 632 | if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || |
| 633 | wrap_p == Tegra::Texture::WrapMode::Border) { | ||
| 646 | const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, | 634 | const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, |
| 647 | config.border_color_b, config.border_color_a}}; | 635 | config.border_color_b, config.border_color_a}}; |
| 648 | if (border_color != new_border_color) { | 636 | if (border_color != new_border_color) { |
| @@ -652,26 +640,35 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr | |||
| 652 | } | 640 | } |
| 653 | } | 641 | } |
| 654 | 642 | ||
| 655 | std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_ptr, | 643 | u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, |
| 656 | GLintptr buffer_offset, | 644 | u32 current_bindpoint) { |
| 657 | Maxwell::ShaderStage stage, | 645 | MICROPROFILE_SCOPE(OpenGL_UBO); |
| 658 | Shader& shader, | ||
| 659 | u32 current_bindpoint) { | ||
| 660 | const auto& gpu = Core::System::GetInstance().GPU(); | 646 | const auto& gpu = Core::System::GetInstance().GPU(); |
| 661 | const auto& maxwell3d = gpu.Maxwell3D(); | 647 | const auto& maxwell3d = gpu.Maxwell3D(); |
| 662 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; | 648 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; |
| 663 | const auto& entries = shader->GetShaderEntries().const_buffer_entries; | 649 | const auto& entries = shader->GetShaderEntries().const_buffer_entries; |
| 664 | 650 | ||
| 651 | constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers; | ||
| 652 | std::array<GLuint, max_binds> bind_buffers; | ||
| 653 | std::array<GLintptr, max_binds> bind_offsets; | ||
| 654 | std::array<GLsizeiptr, max_binds> bind_sizes; | ||
| 655 | |||
| 656 | ASSERT_MSG(entries.size() <= max_binds, "Exceeded expected number of binding points."); | ||
| 657 | |||
| 665 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage | 658 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage |
| 666 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | 659 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { |
| 667 | const auto& used_buffer = entries[bindpoint]; | 660 | const auto& used_buffer = entries[bindpoint]; |
| 668 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; | 661 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; |
| 669 | 662 | ||
| 670 | if (!buffer.enabled) { | 663 | if (!buffer.enabled) { |
| 664 | // With disabled buffers set values as zero to unbind them | ||
| 665 | bind_buffers[bindpoint] = 0; | ||
| 666 | bind_offsets[bindpoint] = 0; | ||
| 667 | bind_sizes[bindpoint] = 0; | ||
| 671 | continue; | 668 | continue; |
| 672 | } | 669 | } |
| 673 | 670 | ||
| 674 | size_t size = 0; | 671 | std::size_t size = 0; |
| 675 | 672 | ||
| 676 | if (used_buffer.IsIndirect()) { | 673 | if (used_buffer.IsIndirect()) { |
| 677 | // Buffer is accessed indirectly, so upload the entire thing | 674 | // Buffer is accessed indirectly, so upload the entire thing |
| @@ -692,26 +689,28 @@ std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_pt | |||
| 692 | size = Common::AlignUp(size, sizeof(GLvec4)); | 689 | size = Common::AlignUp(size, sizeof(GLvec4)); |
| 693 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); | 690 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); |
| 694 | 691 | ||
| 695 | GLintptr const_buffer_offset; | 692 | GLintptr const_buffer_offset = buffer_cache.UploadMemory( |
| 696 | std::tie(buffer_ptr, buffer_offset, const_buffer_offset) = | 693 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); |
| 697 | UploadMemory(buffer_ptr, buffer_offset, buffer.address, size, | ||
| 698 | static_cast<size_t>(uniform_buffer_alignment)); | ||
| 699 | |||
| 700 | glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint, | ||
| 701 | stream_buffer.GetHandle(), const_buffer_offset, size); | ||
| 702 | 694 | ||
| 703 | // Now configure the bindpoint of the buffer inside the shader | 695 | // Now configure the bindpoint of the buffer inside the shader |
| 704 | glUniformBlockBinding(shader->GetProgramHandle(), | 696 | glUniformBlockBinding(shader->GetProgramHandle(), |
| 705 | shader->GetProgramResourceIndex(used_buffer.GetName()), | 697 | shader->GetProgramResourceIndex(used_buffer), |
| 706 | current_bindpoint + bindpoint); | 698 | current_bindpoint + bindpoint); |
| 699 | |||
| 700 | // Prepare values for multibind | ||
| 701 | bind_buffers[bindpoint] = buffer_cache.GetHandle(); | ||
| 702 | bind_offsets[bindpoint] = const_buffer_offset; | ||
| 703 | bind_sizes[bindpoint] = size; | ||
| 707 | } | 704 | } |
| 708 | 705 | ||
| 709 | state.Apply(); | 706 | glBindBuffersRange(GL_UNIFORM_BUFFER, current_bindpoint, static_cast<GLsizei>(entries.size()), |
| 707 | bind_buffers.data(), bind_offsets.data(), bind_sizes.data()); | ||
| 710 | 708 | ||
| 711 | return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())}; | 709 | return current_bindpoint + static_cast<u32>(entries.size()); |
| 712 | } | 710 | } |
| 713 | 711 | ||
| 714 | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { | 712 | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { |
| 713 | MICROPROFILE_SCOPE(OpenGL_Texture); | ||
| 715 | const auto& gpu = Core::System::GetInstance().GPU(); | 714 | const auto& gpu = Core::System::GetInstance().GPU(); |
| 716 | const auto& maxwell3d = gpu.Maxwell3D(); | 715 | const auto& maxwell3d = gpu.Maxwell3D(); |
| 717 | const auto& entries = shader->GetShaderEntries().texture_samplers; | 716 | const auto& entries = shader->GetShaderEntries().texture_samplers; |
| @@ -721,24 +720,25 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | |||
| 721 | 720 | ||
| 722 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | 721 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { |
| 723 | const auto& entry = entries[bindpoint]; | 722 | const auto& entry = entries[bindpoint]; |
| 724 | u32 current_bindpoint = current_unit + bindpoint; | 723 | const u32 current_bindpoint = current_unit + bindpoint; |
| 725 | 724 | ||
| 726 | // Bind the uniform to the sampler. | 725 | // Bind the uniform to the sampler. |
| 727 | 726 | ||
| 728 | glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry.GetName()), | 727 | glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry), |
| 729 | current_bindpoint); | 728 | current_bindpoint); |
| 730 | 729 | ||
| 731 | const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); | 730 | const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); |
| 732 | 731 | ||
| 733 | if (!texture.enabled) { | 732 | if (!texture.enabled) { |
| 734 | state.texture_units[current_bindpoint].texture_2d = 0; | 733 | state.texture_units[current_bindpoint].texture = 0; |
| 735 | continue; | 734 | continue; |
| 736 | } | 735 | } |
| 737 | 736 | ||
| 738 | texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); | 737 | texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); |
| 739 | Surface surface = res_cache.GetTextureSurface(texture); | 738 | Surface surface = res_cache.GetTextureSurface(texture); |
| 740 | if (surface != nullptr) { | 739 | if (surface != nullptr) { |
| 741 | state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle; | 740 | state.texture_units[current_bindpoint].texture = surface->Texture().handle; |
| 741 | state.texture_units[current_bindpoint].target = surface->Target(); | ||
| 742 | state.texture_units[current_bindpoint].swizzle.r = | 742 | state.texture_units[current_bindpoint].swizzle.r = |
| 743 | MaxwellToGL::SwizzleSource(texture.tic.x_source); | 743 | MaxwellToGL::SwizzleSource(texture.tic.x_source); |
| 744 | state.texture_units[current_bindpoint].swizzle.g = | 744 | state.texture_units[current_bindpoint].swizzle.g = |
| @@ -749,47 +749,19 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | |||
| 749 | MaxwellToGL::SwizzleSource(texture.tic.w_source); | 749 | MaxwellToGL::SwizzleSource(texture.tic.w_source); |
| 750 | } else { | 750 | } else { |
| 751 | // Can occur when texture addr is null or its memory is unmapped/invalid | 751 | // Can occur when texture addr is null or its memory is unmapped/invalid |
| 752 | state.texture_units[current_bindpoint].texture_2d = 0; | 752 | state.texture_units[current_bindpoint].texture = 0; |
| 753 | } | 753 | } |
| 754 | } | 754 | } |
| 755 | 755 | ||
| 756 | state.Apply(); | ||
| 757 | |||
| 758 | return current_unit + static_cast<u32>(entries.size()); | 756 | return current_unit + static_cast<u32>(entries.size()); |
| 759 | } | 757 | } |
| 760 | 758 | ||
| 761 | void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, | 759 | void RasterizerOpenGL::SyncViewport() { |
| 762 | const Surface& depth_surface, bool has_stencil) { | ||
| 763 | state.draw.draw_framebuffer = framebuffer.handle; | ||
| 764 | state.Apply(); | ||
| 765 | |||
| 766 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||
| 767 | color_surface != nullptr ? color_surface->Texture().handle : 0, 0); | ||
| 768 | if (depth_surface != nullptr) { | ||
| 769 | if (has_stencil) { | ||
| 770 | // attach both depth and stencil | ||
| 771 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||
| 772 | depth_surface->Texture().handle, 0); | ||
| 773 | } else { | ||
| 774 | // attach depth | ||
| 775 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | ||
| 776 | depth_surface->Texture().handle, 0); | ||
| 777 | // clear stencil attachment | ||
| 778 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||
| 779 | } | ||
| 780 | } else { | ||
| 781 | // clear both depth and stencil attachment | ||
| 782 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||
| 783 | 0); | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) { | ||
| 788 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 760 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 789 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; | 761 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; |
| 790 | 762 | ||
| 791 | state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left; | 763 | state.viewport.x = viewport_rect.left; |
| 792 | state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom; | 764 | state.viewport.y = viewport_rect.bottom; |
| 793 | state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); | 765 | state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); |
| 794 | state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); | 766 | state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); |
| 795 | } | 767 | } |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 30045ebff..bf9560bdc 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -6,19 +6,23 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <map> | ||
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <tuple> | 11 | #include <tuple> |
| 11 | #include <utility> | 12 | #include <utility> |
| 12 | #include <vector> | 13 | #include <vector> |
| 13 | 14 | ||
| 14 | #include <boost/icl/interval_map.hpp> | 15 | #include <boost/icl/interval_map.hpp> |
| 16 | #include <boost/optional.hpp> | ||
| 15 | #include <boost/range/iterator_range.hpp> | 17 | #include <boost/range/iterator_range.hpp> |
| 16 | #include <glad/glad.h> | 18 | #include <glad/glad.h> |
| 17 | 19 | ||
| 18 | #include "common/common_types.h" | 20 | #include "common/common_types.h" |
| 19 | #include "video_core/engines/maxwell_3d.h" | 21 | #include "video_core/engines/maxwell_3d.h" |
| 20 | #include "video_core/memory_manager.h" | 22 | #include "video_core/memory_manager.h" |
| 23 | #include "video_core/rasterizer_cache.h" | ||
| 21 | #include "video_core/rasterizer_interface.h" | 24 | #include "video_core/rasterizer_interface.h" |
| 25 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | ||
| 22 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | 26 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
| 23 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 27 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 24 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 28 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| @@ -42,7 +46,6 @@ public: | |||
| 42 | 46 | ||
| 43 | void DrawArrays() override; | 47 | void DrawArrays() override; |
| 44 | void Clear() override; | 48 | void Clear() override; |
| 45 | void NotifyMaxwellRegisterChanged(u32 method) override; | ||
| 46 | void FlushAll() override; | 49 | void FlushAll() override; |
| 47 | void FlushRegion(VAddr addr, u64 size) override; | 50 | void FlushRegion(VAddr addr, u64 size) override; |
| 48 | void InvalidateRegion(VAddr addr, u64 size) override; | 51 | void InvalidateRegion(VAddr addr, u64 size) override; |
| @@ -70,7 +73,7 @@ public: | |||
| 70 | }; | 73 | }; |
| 71 | 74 | ||
| 72 | /// Maximum supported size that a constbuffer can have in bytes. | 75 | /// Maximum supported size that a constbuffer can have in bytes. |
| 73 | static constexpr size_t MaxConstbufferSize = 0x10000; | 76 | static constexpr std::size_t MaxConstbufferSize = 0x10000; |
| 74 | static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, | 77 | static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, |
| 75 | "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); | 78 | "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); |
| 76 | 79 | ||
| @@ -90,17 +93,20 @@ private: | |||
| 90 | Tegra::Texture::TextureFilter min_filter; | 93 | Tegra::Texture::TextureFilter min_filter; |
| 91 | Tegra::Texture::WrapMode wrap_u; | 94 | Tegra::Texture::WrapMode wrap_u; |
| 92 | Tegra::Texture::WrapMode wrap_v; | 95 | Tegra::Texture::WrapMode wrap_v; |
| 96 | Tegra::Texture::WrapMode wrap_p; | ||
| 93 | GLvec4 border_color; | 97 | GLvec4 border_color; |
| 94 | }; | 98 | }; |
| 95 | 99 | ||
| 96 | /// Configures the color and depth framebuffer states and returns the dirty <Color, Depth> | 100 | /** |
| 97 | /// surfaces if writing was enabled. | 101 | * Configures the color and depth framebuffer states. |
| 98 | std::pair<Surface, Surface> ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, | 102 | * @param use_color_fb If true, configure color framebuffers. |
| 99 | bool preserve_contents); | 103 | * @param using_depth_fb If true, configure the depth/stencil framebuffer. |
| 100 | 104 | * @param preserve_contents If true, tries to preserve data from a previously used framebuffer. | |
| 101 | /// Binds the framebuffer color and depth surface | 105 | * @param single_color_target Specifies if a single color buffer target should be used. |
| 102 | void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, | 106 | */ |
| 103 | bool has_stencil); | 107 | void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, |
| 108 | bool preserve_contents = true, | ||
| 109 | boost::optional<std::size_t> single_color_target = {}); | ||
| 104 | 110 | ||
| 105 | /* | 111 | /* |
| 106 | * Configures the current constbuffers to use for the draw command. | 112 | * Configures the current constbuffers to use for the draw command. |
| @@ -109,9 +115,8 @@ private: | |||
| 109 | * @param current_bindpoint The offset at which to start counting new buffer bindpoints. | 115 | * @param current_bindpoint The offset at which to start counting new buffer bindpoints. |
| 110 | * @returns The next available bindpoint for use in the next shader stage. | 116 | * @returns The next available bindpoint for use in the next shader stage. |
| 111 | */ | 117 | */ |
| 112 | std::tuple<u8*, GLintptr, u32> SetupConstBuffers( | 118 | u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, |
| 113 | u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, | 119 | u32 current_bindpoint); |
| 114 | Shader& shader, u32 current_bindpoint); | ||
| 115 | 120 | ||
| 116 | /* | 121 | /* |
| 117 | * Configures the current textures to use for the draw command. | 122 | * Configures the current textures to use for the draw command. |
| @@ -124,7 +129,7 @@ private: | |||
| 124 | u32 current_unit); | 129 | u32 current_unit); |
| 125 | 130 | ||
| 126 | /// Syncs the viewport to match the guest state | 131 | /// Syncs the viewport to match the guest state |
| 127 | void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect); | 132 | void SyncViewport(); |
| 128 | 133 | ||
| 129 | /// Syncs the clip enabled status to match the guest state | 134 | /// Syncs the clip enabled status to match the guest state |
| 130 | void SyncClipEnabled(); | 135 | void SyncClipEnabled(); |
| @@ -154,6 +159,7 @@ private: | |||
| 154 | void SyncLogicOpState(); | 159 | void SyncLogicOpState(); |
| 155 | 160 | ||
| 156 | bool has_ARB_direct_state_access = false; | 161 | bool has_ARB_direct_state_access = false; |
| 162 | bool has_ARB_multi_bind = false; | ||
| 157 | bool has_ARB_separate_shader_objects = false; | 163 | bool has_ARB_separate_shader_objects = false; |
| 158 | bool has_ARB_vertex_attrib_binding = false; | 164 | bool has_ARB_vertex_attrib_binding = false; |
| 159 | 165 | ||
| @@ -167,28 +173,23 @@ private: | |||
| 167 | ScreenInfo& screen_info; | 173 | ScreenInfo& screen_info; |
| 168 | 174 | ||
| 169 | std::unique_ptr<GLShader::ProgramManager> shader_program_manager; | 175 | std::unique_ptr<GLShader::ProgramManager> shader_program_manager; |
| 170 | OGLVertexArray sw_vao; | 176 | std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute, |
| 171 | OGLVertexArray hw_vao; | 177 | Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>, |
| 178 | OGLVertexArray> | ||
| 179 | vertex_array_cache; | ||
| 172 | 180 | ||
| 173 | std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; | 181 | std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; |
| 174 | 182 | ||
| 175 | static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 183 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 176 | OGLStreamBuffer stream_buffer; | 184 | OGLBufferCache buffer_cache; |
| 177 | OGLBuffer uniform_buffer; | ||
| 178 | OGLFramebuffer framebuffer; | 185 | OGLFramebuffer framebuffer; |
| 179 | GLint uniform_buffer_alignment; | 186 | GLint uniform_buffer_alignment; |
| 180 | 187 | ||
| 181 | size_t CalculateVertexArraysSize() const; | 188 | std::size_t CalculateVertexArraysSize() const; |
| 182 | |||
| 183 | std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset); | ||
| 184 | |||
| 185 | std::pair<u8*, GLintptr> SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); | ||
| 186 | 189 | ||
| 187 | std::pair<u8*, GLintptr> AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); | 190 | void SetupVertexArrays(); |
| 188 | 191 | ||
| 189 | std::tuple<u8*, GLintptr, GLintptr> UploadMemory(u8* buffer_ptr, GLintptr buffer_offset, | 192 | void SetupShaders(); |
| 190 | Tegra::GPUVAddr gpu_addr, size_t size, | ||
| 191 | size_t alignment = 4); | ||
| 192 | 193 | ||
| 193 | enum class AccelDraw { Disabled, Arrays, Indexed }; | 194 | enum class AccelDraw { Disabled, Arrays, Indexed }; |
| 194 | AccelDraw accelerate_draw = AccelDraw::Disabled; | 195 | AccelDraw accelerate_draw = AccelDraw::Disabled; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 1965ab7d5..86682d7cb 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/alignment.h" | 8 | #include "common/alignment.h" |
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/logging/log.h" | ||
| 10 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 11 | #include "common/scope_exit.h" | 12 | #include "common/scope_exit.h" |
| 12 | #include "core/core.h" | 13 | #include "core/core.h" |
| @@ -52,14 +53,30 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 52 | params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); | 53 | params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); |
| 53 | params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); | 54 | params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); |
| 54 | params.unaligned_height = config.tic.Height(); | 55 | params.unaligned_height = config.tic.Height(); |
| 56 | params.target = SurfaceTargetFromTextureType(config.tic.texture_type); | ||
| 57 | |||
| 58 | switch (params.target) { | ||
| 59 | case SurfaceTarget::Texture1D: | ||
| 60 | case SurfaceTarget::Texture2D: | ||
| 61 | params.depth = 1; | ||
| 62 | break; | ||
| 63 | case SurfaceTarget::Texture3D: | ||
| 64 | case SurfaceTarget::Texture2DArray: | ||
| 65 | params.depth = config.tic.Depth(); | ||
| 66 | break; | ||
| 67 | default: | ||
| 68 | LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target)); | ||
| 69 | UNREACHABLE(); | ||
| 70 | params.depth = 1; | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | |||
| 55 | params.size_in_bytes = params.SizeInBytes(); | 74 | params.size_in_bytes = params.SizeInBytes(); |
| 56 | params.cache_width = Common::AlignUp(params.width, 16); | ||
| 57 | params.cache_height = Common::AlignUp(params.height, 16); | ||
| 58 | return params; | 75 | return params; |
| 59 | } | 76 | } |
| 60 | 77 | ||
| 61 | /*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer( | 78 | /*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { |
| 62 | const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config) { | 79 | const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; |
| 63 | SurfaceParams params{}; | 80 | SurfaceParams params{}; |
| 64 | params.addr = TryGetCpuAddr(config.Address()); | 81 | params.addr = TryGetCpuAddr(config.Address()); |
| 65 | params.is_tiled = true; | 82 | params.is_tiled = true; |
| @@ -70,9 +87,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 70 | params.width = config.width; | 87 | params.width = config.width; |
| 71 | params.height = config.height; | 88 | params.height = config.height; |
| 72 | params.unaligned_height = config.height; | 89 | params.unaligned_height = config.height; |
| 90 | params.target = SurfaceTarget::Texture2D; | ||
| 91 | params.depth = 1; | ||
| 73 | params.size_in_bytes = params.SizeInBytes(); | 92 | params.size_in_bytes = params.SizeInBytes(); |
| 74 | params.cache_width = Common::AlignUp(params.width, 16); | ||
| 75 | params.cache_height = Common::AlignUp(params.height, 16); | ||
| 76 | return params; | 93 | return params; |
| 77 | } | 94 | } |
| 78 | 95 | ||
| @@ -86,13 +103,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 86 | params.pixel_format = PixelFormatFromDepthFormat(format); | 103 | params.pixel_format = PixelFormatFromDepthFormat(format); |
| 87 | params.component_type = ComponentTypeFromDepthFormat(format); | 104 | params.component_type = ComponentTypeFromDepthFormat(format); |
| 88 | params.type = GetFormatType(params.pixel_format); | 105 | params.type = GetFormatType(params.pixel_format); |
| 89 | params.size_in_bytes = params.SizeInBytes(); | ||
| 90 | params.width = zeta_width; | 106 | params.width = zeta_width; |
| 91 | params.height = zeta_height; | 107 | params.height = zeta_height; |
| 92 | params.unaligned_height = zeta_height; | 108 | params.unaligned_height = zeta_height; |
| 109 | params.target = SurfaceTarget::Texture2D; | ||
| 110 | params.depth = 1; | ||
| 93 | params.size_in_bytes = params.SizeInBytes(); | 111 | params.size_in_bytes = params.SizeInBytes(); |
| 94 | params.cache_width = Common::AlignUp(params.width, 16); | ||
| 95 | params.cache_height = Common::AlignUp(params.height, 16); | ||
| 96 | return params; | 112 | return params; |
| 97 | } | 113 | } |
| 98 | 114 | ||
| @@ -100,7 +116,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 100 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U | 116 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U |
| 101 | {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S | 117 | {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S |
| 102 | {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI | 118 | {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI |
| 103 | {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U | 119 | {GL_RGB8, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U |
| 104 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, | 120 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, |
| 105 | false}, // A2B10G10R10U | 121 | false}, // A2B10G10R10U |
| 106 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U | 122 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U |
| @@ -151,6 +167,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 151 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S | 167 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S |
| 152 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI | 168 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI |
| 153 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI | 169 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI |
| 170 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 | ||
| 154 | 171 | ||
| 155 | // Depth formats | 172 | // Depth formats |
| 156 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F | 173 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F |
| @@ -166,8 +183,28 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 166 | ComponentType::Float, false}, // Z32FS8 | 183 | ComponentType::Float, false}, // Z32FS8 |
| 167 | }}; | 184 | }}; |
| 168 | 185 | ||
| 186 | static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { | ||
| 187 | switch (target) { | ||
| 188 | case SurfaceParams::SurfaceTarget::Texture1D: | ||
| 189 | return GL_TEXTURE_1D; | ||
| 190 | case SurfaceParams::SurfaceTarget::Texture2D: | ||
| 191 | return GL_TEXTURE_2D; | ||
| 192 | case SurfaceParams::SurfaceTarget::Texture3D: | ||
| 193 | return GL_TEXTURE_3D; | ||
| 194 | case SurfaceParams::SurfaceTarget::Texture1DArray: | ||
| 195 | return GL_TEXTURE_1D_ARRAY; | ||
| 196 | case SurfaceParams::SurfaceTarget::Texture2DArray: | ||
| 197 | return GL_TEXTURE_2D_ARRAY; | ||
| 198 | case SurfaceParams::SurfaceTarget::TextureCubemap: | ||
| 199 | return GL_TEXTURE_CUBE_MAP; | ||
| 200 | } | ||
| 201 | LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); | ||
| 202 | UNREACHABLE(); | ||
| 203 | return {}; | ||
| 204 | } | ||
| 205 | |||
| 169 | static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { | 206 | static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { |
| 170 | ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); | 207 | ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size()); |
| 171 | auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; | 208 | auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; |
| 172 | ASSERT(component_type == format.component_type); | 209 | ASSERT(component_type == format.component_type); |
| 173 | 210 | ||
| @@ -177,6 +214,7 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType | |||
| 177 | static bool IsPixelFormatASTC(PixelFormat format) { | 214 | static bool IsPixelFormatASTC(PixelFormat format) { |
| 178 | switch (format) { | 215 | switch (format) { |
| 179 | case PixelFormat::ASTC_2D_4X4: | 216 | case PixelFormat::ASTC_2D_4X4: |
| 217 | case PixelFormat::ASTC_2D_8X8: | ||
| 180 | return true; | 218 | return true; |
| 181 | default: | 219 | default: |
| 182 | return false; | 220 | return false; |
| @@ -187,6 +225,8 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { | |||
| 187 | switch (format) { | 225 | switch (format) { |
| 188 | case PixelFormat::ASTC_2D_4X4: | 226 | case PixelFormat::ASTC_2D_4X4: |
| 189 | return {4, 4}; | 227 | return {4, 4}; |
| 228 | case PixelFormat::ASTC_2D_8X8: | ||
| 229 | return {8, 8}; | ||
| 190 | default: | 230 | default: |
| 191 | LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); | 231 | LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); |
| 192 | UNREACHABLE(); | 232 | UNREACHABLE(); |
| @@ -220,7 +260,8 @@ static bool IsFormatBCn(PixelFormat format) { | |||
| 220 | } | 260 | } |
| 221 | 261 | ||
| 222 | template <bool morton_to_gl, PixelFormat format> | 262 | template <bool morton_to_gl, PixelFormat format> |
| 223 | void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) { | 263 | void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, |
| 264 | VAddr addr) { | ||
| 224 | constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; | 265 | constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; |
| 225 | constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); | 266 | constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); |
| 226 | 267 | ||
| @@ -230,18 +271,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu | |||
| 230 | const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; | 271 | const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; |
| 231 | const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( | 272 | const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( |
| 232 | addr, tile_size, bytes_per_pixel, stride, height, block_height); | 273 | addr, tile_size, bytes_per_pixel, stride, height, block_height); |
| 233 | const size_t size_to_copy{std::min(gl_buffer.size(), data.size())}; | 274 | const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; |
| 234 | gl_buffer.assign(data.begin(), data.begin() + size_to_copy); | 275 | memcpy(gl_buffer, data.data(), size_to_copy); |
| 235 | } else { | 276 | } else { |
| 236 | // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should | 277 | // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should |
| 237 | // check the configuration for this and perform more generic un/swizzle | 278 | // check the configuration for this and perform more generic un/swizzle |
| 238 | LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); | 279 | LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); |
| 239 | VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, | 280 | VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, |
| 240 | Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl); | 281 | Memory::GetPointer(addr), gl_buffer, morton_to_gl); |
| 241 | } | 282 | } |
| 242 | } | 283 | } |
| 243 | 284 | ||
| 244 | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | 285 | static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), |
| 245 | SurfaceParams::MaxPixelFormat> | 286 | SurfaceParams::MaxPixelFormat> |
| 246 | morton_to_gl_fns = { | 287 | morton_to_gl_fns = { |
| 247 | // clang-format off | 288 | // clang-format off |
| @@ -290,6 +331,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | |||
| 290 | MortonCopy<true, PixelFormat::RG8S>, | 331 | MortonCopy<true, PixelFormat::RG8S>, |
| 291 | MortonCopy<true, PixelFormat::RG32UI>, | 332 | MortonCopy<true, PixelFormat::RG32UI>, |
| 292 | MortonCopy<true, PixelFormat::R32UI>, | 333 | MortonCopy<true, PixelFormat::R32UI>, |
| 334 | MortonCopy<true, PixelFormat::ASTC_2D_8X8>, | ||
| 293 | MortonCopy<true, PixelFormat::Z32F>, | 335 | MortonCopy<true, PixelFormat::Z32F>, |
| 294 | MortonCopy<true, PixelFormat::Z16>, | 336 | MortonCopy<true, PixelFormat::Z16>, |
| 295 | MortonCopy<true, PixelFormat::Z24S8>, | 337 | MortonCopy<true, PixelFormat::Z24S8>, |
| @@ -298,7 +340,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | |||
| 298 | // clang-format on | 340 | // clang-format on |
| 299 | }; | 341 | }; |
| 300 | 342 | ||
| 301 | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | 343 | static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), |
| 302 | SurfaceParams::MaxPixelFormat> | 344 | SurfaceParams::MaxPixelFormat> |
| 303 | gl_to_morton_fns = { | 345 | gl_to_morton_fns = { |
| 304 | // clang-format off | 346 | // clang-format off |
| @@ -349,6 +391,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | |||
| 349 | MortonCopy<false, PixelFormat::RG8S>, | 391 | MortonCopy<false, PixelFormat::RG8S>, |
| 350 | MortonCopy<false, PixelFormat::RG32UI>, | 392 | MortonCopy<false, PixelFormat::RG32UI>, |
| 351 | MortonCopy<false, PixelFormat::R32UI>, | 393 | MortonCopy<false, PixelFormat::R32UI>, |
| 394 | nullptr, | ||
| 352 | MortonCopy<false, PixelFormat::Z32F>, | 395 | MortonCopy<false, PixelFormat::Z32F>, |
| 353 | MortonCopy<false, PixelFormat::Z16>, | 396 | MortonCopy<false, PixelFormat::Z16>, |
| 354 | MortonCopy<false, PixelFormat::Z24S8>, | 397 | MortonCopy<false, PixelFormat::Z24S8>, |
| @@ -357,33 +400,6 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | |||
| 357 | // clang-format on | 400 | // clang-format on |
| 358 | }; | 401 | }; |
| 359 | 402 | ||
| 360 | // Allocate an uninitialized texture of appropriate size and format for the surface | ||
| 361 | static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width, | ||
| 362 | u32 height) { | ||
| 363 | OpenGLState cur_state = OpenGLState::GetCurState(); | ||
| 364 | |||
| 365 | // Keep track of previous texture bindings | ||
| 366 | GLuint old_tex = cur_state.texture_units[0].texture_2d; | ||
| 367 | cur_state.texture_units[0].texture_2d = texture; | ||
| 368 | cur_state.Apply(); | ||
| 369 | glActiveTexture(GL_TEXTURE0); | ||
| 370 | |||
| 371 | if (!format_tuple.compressed) { | ||
| 372 | // Only pre-create the texture for non-compressed textures. | ||
| 373 | glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0, | ||
| 374 | format_tuple.format, format_tuple.type, nullptr); | ||
| 375 | } | ||
| 376 | |||
| 377 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||
| 378 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 379 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 380 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 381 | |||
| 382 | // Restore previous texture bindings | ||
| 383 | cur_state.texture_units[0].texture_2d = old_tex; | ||
| 384 | cur_state.Apply(); | ||
| 385 | } | ||
| 386 | |||
| 387 | static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, | 403 | static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, |
| 388 | const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, | 404 | const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, |
| 389 | GLuint read_fb_handle, GLuint draw_fb_handle) { | 405 | GLuint read_fb_handle, GLuint draw_fb_handle) { |
| @@ -438,12 +454,53 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec | |||
| 438 | return true; | 454 | return true; |
| 439 | } | 455 | } |
| 440 | 456 | ||
| 441 | CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) { | 457 | CachedSurface::CachedSurface(const SurfaceParams& params) |
| 458 | : params(params), gl_target(SurfaceTargetToGL(params.target)) { | ||
| 442 | texture.Create(); | 459 | texture.Create(); |
| 443 | const auto& rect{params.GetRect()}; | 460 | const auto& rect{params.GetRect()}; |
| 444 | AllocateSurfaceTexture(texture.handle, | 461 | |
| 445 | GetFormatTuple(params.pixel_format, params.component_type), | 462 | // Keep track of previous texture bindings |
| 463 | OpenGLState cur_state = OpenGLState::GetCurState(); | ||
| 464 | const auto& old_tex = cur_state.texture_units[0]; | ||
| 465 | SCOPE_EXIT({ | ||
| 466 | cur_state.texture_units[0] = old_tex; | ||
| 467 | cur_state.Apply(); | ||
| 468 | }); | ||
| 469 | |||
| 470 | cur_state.texture_units[0].texture = texture.handle; | ||
| 471 | cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); | ||
| 472 | cur_state.Apply(); | ||
| 473 | glActiveTexture(GL_TEXTURE0); | ||
| 474 | |||
| 475 | const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type); | ||
| 476 | if (!format_tuple.compressed) { | ||
| 477 | // Only pre-create the texture for non-compressed textures. | ||
| 478 | switch (params.target) { | ||
| 479 | case SurfaceParams::SurfaceTarget::Texture1D: | ||
| 480 | glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, | ||
| 481 | rect.GetWidth()); | ||
| 482 | break; | ||
| 483 | case SurfaceParams::SurfaceTarget::Texture2D: | ||
| 484 | glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, | ||
| 446 | rect.GetWidth(), rect.GetHeight()); | 485 | rect.GetWidth(), rect.GetHeight()); |
| 486 | break; | ||
| 487 | case SurfaceParams::SurfaceTarget::Texture3D: | ||
| 488 | case SurfaceParams::SurfaceTarget::Texture2DArray: | ||
| 489 | glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, | ||
| 490 | rect.GetWidth(), rect.GetHeight(), params.depth); | ||
| 491 | break; | ||
| 492 | default: | ||
| 493 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||
| 494 | static_cast<u32>(params.target)); | ||
| 495 | UNREACHABLE(); | ||
| 496 | glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(), | ||
| 497 | rect.GetHeight()); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 502 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 503 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 447 | } | 504 | } |
| 448 | 505 | ||
| 449 | static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | 506 | static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { |
| @@ -461,10 +518,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 461 | 518 | ||
| 462 | S8Z24 input_pixel{}; | 519 | S8Z24 input_pixel{}; |
| 463 | Z24S8 output_pixel{}; | 520 | Z24S8 output_pixel{}; |
| 464 | const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; | 521 | constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; |
| 465 | for (size_t y = 0; y < height; ++y) { | 522 | for (std::size_t y = 0; y < height; ++y) { |
| 466 | for (size_t x = 0; x < width; ++x) { | 523 | for (std::size_t x = 0; x < width; ++x) { |
| 467 | const size_t offset{bpp * (y * width + x)}; | 524 | const std::size_t offset{bpp * (y * width + x)}; |
| 468 | std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); | 525 | std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); |
| 469 | output_pixel.s8.Assign(input_pixel.s8); | 526 | output_pixel.s8.Assign(input_pixel.s8); |
| 470 | output_pixel.z24.Assign(input_pixel.z24); | 527 | output_pixel.z24.Assign(input_pixel.z24); |
| @@ -474,10 +531,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 474 | } | 531 | } |
| 475 | 532 | ||
| 476 | static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { | 533 | static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { |
| 477 | const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; | 534 | constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; |
| 478 | for (size_t y = 0; y < height; ++y) { | 535 | for (std::size_t y = 0; y < height; ++y) { |
| 479 | for (size_t x = 0; x < width; ++x) { | 536 | for (std::size_t x = 0; x < width; ++x) { |
| 480 | const size_t offset{bpp * (y * width + x)}; | 537 | const std::size_t offset{bpp * (y * width + x)}; |
| 481 | const u8 temp{data[offset]}; | 538 | const u8 temp{data[offset]}; |
| 482 | data[offset] = data[offset + 1]; | 539 | data[offset] = data[offset + 1]; |
| 483 | data[offset + 1] = temp; | 540 | data[offset + 1] = temp; |
| @@ -493,7 +550,8 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 493 | static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, | 550 | static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, |
| 494 | u32 width, u32 height) { | 551 | u32 width, u32 height) { |
| 495 | switch (pixel_format) { | 552 | switch (pixel_format) { |
| 496 | case PixelFormat::ASTC_2D_4X4: { | 553 | case PixelFormat::ASTC_2D_4X4: |
| 554 | case PixelFormat::ASTC_2D_8X8: { | ||
| 497 | // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. | 555 | // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. |
| 498 | u32 block_width{}; | 556 | u32 block_width{}; |
| 499 | u32 block_height{}; | 557 | u32 block_height{}; |
| @@ -514,23 +572,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma | |||
| 514 | } | 572 | } |
| 515 | } | 573 | } |
| 516 | 574 | ||
| 517 | /** | ||
| 518 | * Helper function to perform software conversion (as needed) when flushing a buffer to Switch | ||
| 519 | * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with | ||
| 520 | * typical desktop GPUs. | ||
| 521 | */ | ||
| 522 | static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format, | ||
| 523 | u32 /*width*/, u32 /*height*/) { | ||
| 524 | switch (pixel_format) { | ||
| 525 | case PixelFormat::ASTC_2D_4X4: | ||
| 526 | case PixelFormat::S8Z24: | ||
| 527 | LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}", | ||
| 528 | static_cast<u32>(pixel_format)); | ||
| 529 | UNREACHABLE(); | ||
| 530 | break; | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); | 575 | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); |
| 535 | void CachedSurface::LoadGLBuffer() { | 576 | void CachedSurface::LoadGLBuffer() { |
| 536 | ASSERT(params.type != SurfaceType::Fill); | 577 | ASSERT(params.type != SurfaceType::Fill); |
| @@ -545,13 +586,25 @@ void CachedSurface::LoadGLBuffer() { | |||
| 545 | MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); | 586 | MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); |
| 546 | 587 | ||
| 547 | if (params.is_tiled) { | 588 | if (params.is_tiled) { |
| 548 | gl_buffer.resize(copy_size); | 589 | // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do |
| 590 | // this for 3D textures, etc. | ||
| 591 | switch (params.target) { | ||
| 592 | case SurfaceParams::SurfaceTarget::Texture2D: | ||
| 593 | // Pass impl. to the fallback code below | ||
| 594 | break; | ||
| 595 | default: | ||
| 596 | LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}", | ||
| 597 | static_cast<u32>(params.target)); | ||
| 598 | UNREACHABLE(); | ||
| 599 | } | ||
| 549 | 600 | ||
| 550 | morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( | 601 | gl_buffer.resize(static_cast<std::size_t>(params.depth) * copy_size); |
| 551 | params.width, params.block_height, params.height, gl_buffer, params.addr); | 602 | morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( |
| 603 | params.width, params.block_height, params.height, gl_buffer.data(), copy_size, | ||
| 604 | params.addr); | ||
| 552 | } else { | 605 | } else { |
| 553 | const u8* const texture_src_data_end = texture_src_data + copy_size; | 606 | const u8* const texture_src_data_end{texture_src_data + |
| 554 | 607 | (static_cast<std::size_t>(params.depth) * copy_size)}; | |
| 555 | gl_buffer.assign(texture_src_data, texture_src_data_end); | 608 | gl_buffer.assign(texture_src_data, texture_src_data_end); |
| 556 | } | 609 | } |
| 557 | 610 | ||
| @@ -560,23 +613,7 @@ void CachedSurface::LoadGLBuffer() { | |||
| 560 | 613 | ||
| 561 | MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); | 614 | MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); |
| 562 | void CachedSurface::FlushGLBuffer() { | 615 | void CachedSurface::FlushGLBuffer() { |
| 563 | u8* const dst_buffer = Memory::GetPointer(params.addr); | 616 | ASSERT_MSG(false, "Unimplemented"); |
| 564 | |||
| 565 | ASSERT(dst_buffer); | ||
| 566 | ASSERT(gl_buffer.size() == | ||
| 567 | params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); | ||
| 568 | |||
| 569 | MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); | ||
| 570 | |||
| 571 | ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, | ||
| 572 | params.height); | ||
| 573 | |||
| 574 | if (!params.is_tiled) { | ||
| 575 | std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes); | ||
| 576 | } else { | ||
| 577 | gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( | ||
| 578 | params.width, params.block_height, params.height, gl_buffer, params.addr); | ||
| 579 | } | ||
| 580 | } | 617 | } |
| 581 | 618 | ||
| 582 | MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); | 619 | MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); |
| @@ -586,22 +623,30 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | |||
| 586 | 623 | ||
| 587 | MICROPROFILE_SCOPE(OpenGL_TextureUL); | 624 | MICROPROFILE_SCOPE(OpenGL_TextureUL); |
| 588 | 625 | ||
| 589 | ASSERT(gl_buffer.size() == | 626 | ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height * |
| 590 | params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); | 627 | GetGLBytesPerPixel(params.pixel_format) * params.depth); |
| 591 | 628 | ||
| 592 | const auto& rect{params.GetRect()}; | 629 | const auto& rect{params.GetRect()}; |
| 593 | 630 | ||
| 594 | // Load data from memory to the surface | 631 | // Load data from memory to the surface |
| 595 | GLint x0 = static_cast<GLint>(rect.left); | 632 | const GLint x0 = static_cast<GLint>(rect.left); |
| 596 | GLint y0 = static_cast<GLint>(rect.bottom); | 633 | const GLint y0 = static_cast<GLint>(rect.bottom); |
| 597 | size_t buffer_offset = (y0 * params.width + x0) * GetGLBytesPerPixel(params.pixel_format); | 634 | const std::size_t buffer_offset = |
| 635 | static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + | ||
| 636 | static_cast<std::size_t>(x0)) * | ||
| 637 | GetGLBytesPerPixel(params.pixel_format); | ||
| 598 | 638 | ||
| 599 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | 639 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); |
| 600 | GLuint target_tex = texture.handle; | 640 | const GLuint target_tex = texture.handle; |
| 601 | OpenGLState cur_state = OpenGLState::GetCurState(); | 641 | OpenGLState cur_state = OpenGLState::GetCurState(); |
| 602 | 642 | ||
| 603 | GLuint old_tex = cur_state.texture_units[0].texture_2d; | 643 | const auto& old_tex = cur_state.texture_units[0]; |
| 604 | cur_state.texture_units[0].texture_2d = target_tex; | 644 | SCOPE_EXIT({ |
| 645 | cur_state.texture_units[0] = old_tex; | ||
| 646 | cur_state.Apply(); | ||
| 647 | }); | ||
| 648 | cur_state.texture_units[0].texture = target_tex; | ||
| 649 | cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); | ||
| 605 | cur_state.Apply(); | 650 | cur_state.Apply(); |
| 606 | 651 | ||
| 607 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT | 652 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT |
| @@ -610,136 +655,102 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | |||
| 610 | 655 | ||
| 611 | glActiveTexture(GL_TEXTURE0); | 656 | glActiveTexture(GL_TEXTURE0); |
| 612 | if (tuple.compressed) { | 657 | if (tuple.compressed) { |
| 613 | glCompressedTexImage2D( | 658 | switch (params.target) { |
| 614 | GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), | 659 | case SurfaceParams::SurfaceTarget::Texture2D: |
| 615 | static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes), | 660 | glCompressedTexImage2D( |
| 616 | &gl_buffer[buffer_offset]); | 661 | SurfaceTargetToGL(params.target), 0, tuple.internal_format, |
| 662 | static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, | ||
| 663 | static_cast<GLsizei>(params.size_in_bytes), &gl_buffer[buffer_offset]); | ||
| 664 | break; | ||
| 665 | case SurfaceParams::SurfaceTarget::Texture3D: | ||
| 666 | case SurfaceParams::SurfaceTarget::Texture2DArray: | ||
| 667 | glCompressedTexImage3D( | ||
| 668 | SurfaceTargetToGL(params.target), 0, tuple.internal_format, | ||
| 669 | static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), | ||
| 670 | static_cast<GLsizei>(params.depth), 0, static_cast<GLsizei>(params.size_in_bytes), | ||
| 671 | &gl_buffer[buffer_offset]); | ||
| 672 | break; | ||
| 673 | default: | ||
| 674 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||
| 675 | static_cast<u32>(params.target)); | ||
| 676 | UNREACHABLE(); | ||
| 677 | glCompressedTexImage2D( | ||
| 678 | GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), | ||
| 679 | static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes), | ||
| 680 | &gl_buffer[buffer_offset]); | ||
| 681 | } | ||
| 617 | } else { | 682 | } else { |
| 618 | glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), | ||
| 619 | static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||
| 620 | &gl_buffer[buffer_offset]); | ||
| 621 | } | ||
| 622 | |||
| 623 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||
| 624 | |||
| 625 | cur_state.texture_units[0].texture_2d = old_tex; | ||
| 626 | cur_state.Apply(); | ||
| 627 | } | ||
| 628 | |||
| 629 | MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64)); | ||
| 630 | void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { | ||
| 631 | if (params.type == SurfaceType::Fill) | ||
| 632 | return; | ||
| 633 | |||
| 634 | MICROPROFILE_SCOPE(OpenGL_TextureDL); | ||
| 635 | |||
| 636 | gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); | ||
| 637 | |||
| 638 | OpenGLState state = OpenGLState::GetCurState(); | ||
| 639 | OpenGLState prev_state = state; | ||
| 640 | SCOPE_EXIT({ prev_state.Apply(); }); | ||
| 641 | |||
| 642 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | ||
| 643 | |||
| 644 | // Ensure no bad interactions with GL_PACK_ALIGNMENT | ||
| 645 | ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); | ||
| 646 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); | ||
| 647 | |||
| 648 | const auto& rect{params.GetRect()}; | ||
| 649 | size_t buffer_offset = | ||
| 650 | (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format); | ||
| 651 | |||
| 652 | state.UnbindTexture(texture.handle); | ||
| 653 | state.draw.read_framebuffer = read_fb_handle; | ||
| 654 | state.Apply(); | ||
| 655 | 683 | ||
| 656 | if (params.type == SurfaceType::ColorTexture) { | 684 | switch (params.target) { |
| 657 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | 685 | case SurfaceParams::SurfaceTarget::Texture1D: |
| 658 | texture.handle, 0); | 686 | glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, |
| 659 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | 687 | static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, |
| 660 | 0); | 688 | &gl_buffer[buffer_offset]); |
| 661 | } else if (params.type == SurfaceType::Depth) { | 689 | break; |
| 662 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | 690 | case SurfaceParams::SurfaceTarget::Texture2D: |
| 663 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | 691 | glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, |
| 664 | texture.handle, 0); | 692 | static_cast<GLsizei>(rect.GetWidth()), |
| 665 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | 693 | static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, |
| 666 | } else { | 694 | &gl_buffer[buffer_offset]); |
| 667 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | 695 | break; |
| 668 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | 696 | case SurfaceParams::SurfaceTarget::Texture3D: |
| 669 | texture.handle, 0); | 697 | case SurfaceParams::SurfaceTarget::Texture2DArray: |
| 698 | glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0, | ||
| 699 | static_cast<GLsizei>(rect.GetWidth()), | ||
| 700 | static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, | ||
| 701 | tuple.type, &gl_buffer[buffer_offset]); | ||
| 702 | break; | ||
| 703 | default: | ||
| 704 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||
| 705 | static_cast<u32>(params.target)); | ||
| 706 | UNREACHABLE(); | ||
| 707 | glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), | ||
| 708 | static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||
| 709 | &gl_buffer[buffer_offset]); | ||
| 710 | } | ||
| 670 | } | 711 | } |
| 671 | glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom), | ||
| 672 | static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), | ||
| 673 | tuple.format, tuple.type, &gl_buffer[buffer_offset]); | ||
| 674 | 712 | ||
| 675 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); | 713 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| 676 | } | 714 | } |
| 677 | 715 | ||
| 678 | RasterizerCacheOpenGL::RasterizerCacheOpenGL() { | 716 | RasterizerCacheOpenGL::RasterizerCacheOpenGL() { |
| 679 | read_framebuffer.Create(); | 717 | read_framebuffer.Create(); |
| 680 | draw_framebuffer.Create(); | 718 | draw_framebuffer.Create(); |
| 719 | copy_pbo.Create(); | ||
| 681 | } | 720 | } |
| 682 | 721 | ||
| 683 | Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { | 722 | Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { |
| 684 | return GetSurface(SurfaceParams::CreateForTexture(config)); | 723 | return GetSurface(SurfaceParams::CreateForTexture(config)); |
| 685 | } | 724 | } |
| 686 | 725 | ||
| 687 | SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb, | 726 | Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) { |
| 688 | bool using_depth_fb, | 727 | const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs}; |
| 689 | bool preserve_contents) { | 728 | if (!regs.zeta.Address() || !regs.zeta_enable) { |
| 690 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 729 | return {}; |
| 730 | } | ||
| 691 | 731 | ||
| 692 | // TODO(bunnei): This is hard corded to use just the first render buffer | 732 | SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( |
| 693 | LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); | 733 | regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)}; |
| 694 | 734 | ||
| 695 | // get color and depth surfaces | 735 | return GetSurface(depth_params, preserve_contents); |
| 696 | SurfaceParams color_params{}; | 736 | } |
| 697 | SurfaceParams depth_params{}; | ||
| 698 | 737 | ||
| 699 | if (using_color_fb) { | 738 | Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool preserve_contents) { |
| 700 | color_params = SurfaceParams::CreateForFramebuffer(regs.rt[0]); | 739 | const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs}; |
| 701 | } | ||
| 702 | 740 | ||
| 703 | if (using_depth_fb) { | 741 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); |
| 704 | depth_params = SurfaceParams::CreateForDepthBuffer(regs.zeta_width, regs.zeta_height, | ||
| 705 | regs.zeta.Address(), regs.zeta.format); | ||
| 706 | } | ||
| 707 | 742 | ||
| 708 | MathUtil::Rectangle<u32> color_rect{}; | 743 | if (index >= regs.rt_control.count) { |
| 709 | Surface color_surface; | 744 | return {}; |
| 710 | if (using_color_fb) { | ||
| 711 | color_surface = GetSurface(color_params, preserve_contents); | ||
| 712 | if (color_surface) { | ||
| 713 | color_rect = color_surface->GetSurfaceParams().GetRect(); | ||
| 714 | } | ||
| 715 | } | 745 | } |
| 716 | 746 | ||
| 717 | MathUtil::Rectangle<u32> depth_rect{}; | 747 | if (regs.rt[index].Address() == 0 || regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { |
| 718 | Surface depth_surface; | 748 | return {}; |
| 719 | if (using_depth_fb) { | ||
| 720 | depth_surface = GetSurface(depth_params, preserve_contents); | ||
| 721 | if (depth_surface) { | ||
| 722 | depth_rect = depth_surface->GetSurfaceParams().GetRect(); | ||
| 723 | } | ||
| 724 | } | 749 | } |
| 725 | 750 | ||
| 726 | MathUtil::Rectangle<u32> fb_rect{}; | 751 | const SurfaceParams color_params{SurfaceParams::CreateForFramebuffer(index)}; |
| 727 | if (color_surface && depth_surface) { | ||
| 728 | fb_rect = color_rect; | ||
| 729 | // Color and Depth surfaces must have the same dimensions and offsets | ||
| 730 | if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top || | ||
| 731 | color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) { | ||
| 732 | color_surface = GetSurface(color_params); | ||
| 733 | depth_surface = GetSurface(depth_params); | ||
| 734 | fb_rect = color_surface->GetSurfaceParams().GetRect(); | ||
| 735 | } | ||
| 736 | } else if (color_surface) { | ||
| 737 | fb_rect = color_rect; | ||
| 738 | } else if (depth_surface) { | ||
| 739 | fb_rect = depth_rect; | ||
| 740 | } | ||
| 741 | 752 | ||
| 742 | return std::make_tuple(color_surface, depth_surface, fb_rect); | 753 | return GetSurface(color_params, preserve_contents); |
| 743 | } | 754 | } |
| 744 | 755 | ||
| 745 | void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { | 756 | void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { |
| @@ -748,7 +759,6 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { | |||
| 748 | } | 759 | } |
| 749 | 760 | ||
| 750 | void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { | 761 | void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { |
| 751 | surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); | ||
| 752 | surface->FlushGLBuffer(); | 762 | surface->FlushGLBuffer(); |
| 753 | } | 763 | } |
| 754 | 764 | ||
| @@ -806,27 +816,26 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | |||
| 806 | // Get a new surface with the new parameters, and blit the previous surface to it | 816 | // Get a new surface with the new parameters, and blit the previous surface to it |
| 807 | Surface new_surface{GetUncachedSurface(new_params)}; | 817 | Surface new_surface{GetUncachedSurface(new_params)}; |
| 808 | 818 | ||
| 809 | // If format is unchanged, we can do a faster blit without reinterpreting pixel data | 819 | if (params.pixel_format == new_params.pixel_format || |
| 810 | if (params.pixel_format == new_params.pixel_format) { | 820 | !Settings::values.use_accurate_framebuffers) { |
| 821 | // If the format is the same, just do a framebuffer blit. This is significantly faster than | ||
| 822 | // using PBOs. The is also likely less accurate, as textures will be converted rather than | ||
| 823 | // reinterpreted. | ||
| 824 | |||
| 811 | BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle, | 825 | BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle, |
| 812 | new_surface->GetSurfaceParams().GetRect(), params.type, | 826 | params.GetRect(), params.type, read_framebuffer.handle, |
| 813 | read_framebuffer.handle, draw_framebuffer.handle); | 827 | draw_framebuffer.handle); |
| 814 | return new_surface; | 828 | } else { |
| 815 | } | 829 | // When use_accurate_framebuffers setting is enabled, perform a more accurate surface copy, |
| 830 | // where pixels are reinterpreted as a new format (without conversion). This code path uses | ||
| 831 | // OpenGL PBOs and is quite slow. | ||
| 816 | 832 | ||
| 817 | // When using accurate framebuffers, always copy old data to new surface, regardless of format | ||
| 818 | if (Settings::values.use_accurate_framebuffers) { | ||
| 819 | auto source_format = GetFormatTuple(params.pixel_format, params.component_type); | 833 | auto source_format = GetFormatTuple(params.pixel_format, params.component_type); |
| 820 | auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type); | 834 | auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type); |
| 821 | 835 | ||
| 822 | size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); | 836 | std::size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); |
| 823 | 837 | ||
| 824 | // Use a Pixel Buffer Object to download the previous texture and then upload it to the new | 838 | glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle); |
| 825 | // one using the new format. | ||
| 826 | OGLBuffer pbo; | ||
| 827 | pbo.Create(); | ||
| 828 | |||
| 829 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle); | ||
| 830 | glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); | 839 | glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); |
| 831 | if (source_format.compressed) { | 840 | if (source_format.compressed) { |
| 832 | glGetCompressedTextureImage(surface->Texture().handle, 0, | 841 | glGetCompressedTextureImage(surface->Texture().handle, 0, |
| @@ -845,10 +854,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | |||
| 845 | // of the data in this case. Games like Super Mario Odyssey seem to hit this case | 854 | // of the data in this case. Games like Super Mario Odyssey seem to hit this case |
| 846 | // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer | 855 | // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer |
| 847 | // but it doesn't clear it beforehand, the texture is already full of zeros. | 856 | // but it doesn't clear it beforehand, the texture is already full of zeros. |
| 848 | LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during " | 857 | LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " |
| 849 | "reinterpretation but the texture is tiled."); | 858 | "reinterpretation but the texture is tiled."); |
| 850 | } | 859 | } |
| 851 | size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); | 860 | std::size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); |
| 852 | std::vector<u8> data(remaining_size); | 861 | std::vector<u8> data(remaining_size); |
| 853 | Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size()); | 862 | Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size()); |
| 854 | glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size, | 863 | glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size, |
| @@ -859,21 +868,38 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | |||
| 859 | 868 | ||
| 860 | const auto& dest_rect{new_params.GetRect()}; | 869 | const auto& dest_rect{new_params.GetRect()}; |
| 861 | 870 | ||
| 862 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle); | 871 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle); |
| 863 | if (dest_format.compressed) { | 872 | if (dest_format.compressed) { |
| 864 | glCompressedTexSubImage2D( | 873 | LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!"); |
| 865 | GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()), | 874 | UNREACHABLE(); |
| 866 | static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, | ||
| 867 | static_cast<GLsizei>(new_params.SizeInBytes()), nullptr); | ||
| 868 | } else { | 875 | } else { |
| 869 | glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0, | 876 | switch (new_params.target) { |
| 870 | static_cast<GLsizei>(dest_rect.GetWidth()), | 877 | case SurfaceParams::SurfaceTarget::Texture1D: |
| 871 | static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, | 878 | glTextureSubImage1D(new_surface->Texture().handle, 0, 0, |
| 872 | dest_format.type, nullptr); | 879 | static_cast<GLsizei>(dest_rect.GetWidth()), dest_format.format, |
| 880 | dest_format.type, nullptr); | ||
| 881 | break; | ||
| 882 | case SurfaceParams::SurfaceTarget::Texture2D: | ||
| 883 | glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0, | ||
| 884 | static_cast<GLsizei>(dest_rect.GetWidth()), | ||
| 885 | static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, | ||
| 886 | dest_format.type, nullptr); | ||
| 887 | break; | ||
| 888 | case SurfaceParams::SurfaceTarget::Texture3D: | ||
| 889 | case SurfaceParams::SurfaceTarget::Texture2DArray: | ||
| 890 | glTextureSubImage3D(new_surface->Texture().handle, 0, 0, 0, 0, | ||
| 891 | static_cast<GLsizei>(dest_rect.GetWidth()), | ||
| 892 | static_cast<GLsizei>(dest_rect.GetHeight()), | ||
| 893 | static_cast<GLsizei>(new_params.depth), dest_format.format, | ||
| 894 | dest_format.type, nullptr); | ||
| 895 | break; | ||
| 896 | default: | ||
| 897 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||
| 898 | static_cast<u32>(params.target)); | ||
| 899 | UNREACHABLE(); | ||
| 900 | } | ||
| 873 | } | 901 | } |
| 874 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | 902 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 875 | |||
| 876 | pbo.Release(); | ||
| 877 | } | 903 | } |
| 878 | 904 | ||
| 879 | return new_surface; | 905 | return new_surface; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index aad75f200..d7a4bc37f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -70,19 +70,20 @@ struct SurfaceParams { | |||
| 70 | RG8S = 42, | 70 | RG8S = 42, |
| 71 | RG32UI = 43, | 71 | RG32UI = 43, |
| 72 | R32UI = 44, | 72 | R32UI = 44, |
| 73 | ASTC_2D_8X8 = 45, | ||
| 73 | 74 | ||
| 74 | MaxColorFormat, | 75 | MaxColorFormat, |
| 75 | 76 | ||
| 76 | // Depth formats | 77 | // Depth formats |
| 77 | Z32F = 45, | 78 | Z32F = 46, |
| 78 | Z16 = 46, | 79 | Z16 = 47, |
| 79 | 80 | ||
| 80 | MaxDepthFormat, | 81 | MaxDepthFormat, |
| 81 | 82 | ||
| 82 | // DepthStencil formats | 83 | // DepthStencil formats |
| 83 | Z24S8 = 47, | 84 | Z24S8 = 48, |
| 84 | S8Z24 = 48, | 85 | S8Z24 = 49, |
| 85 | Z32FS8 = 49, | 86 | Z32FS8 = 50, |
| 86 | 87 | ||
| 87 | MaxDepthStencilFormat, | 88 | MaxDepthStencilFormat, |
| 88 | 89 | ||
| @@ -90,7 +91,7 @@ struct SurfaceParams { | |||
| 90 | Invalid = 255, | 91 | Invalid = 255, |
| 91 | }; | 92 | }; |
| 92 | 93 | ||
| 93 | static constexpr size_t MaxPixelFormat = static_cast<size_t>(PixelFormat::Max); | 94 | static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max); |
| 94 | 95 | ||
| 95 | enum class ComponentType { | 96 | enum class ComponentType { |
| 96 | Invalid = 0, | 97 | Invalid = 0, |
| @@ -109,6 +110,33 @@ struct SurfaceParams { | |||
| 109 | Invalid = 4, | 110 | Invalid = 4, |
| 110 | }; | 111 | }; |
| 111 | 112 | ||
| 113 | enum class SurfaceTarget { | ||
| 114 | Texture1D, | ||
| 115 | Texture2D, | ||
| 116 | Texture3D, | ||
| 117 | Texture1DArray, | ||
| 118 | Texture2DArray, | ||
| 119 | TextureCubemap, | ||
| 120 | }; | ||
| 121 | |||
| 122 | static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) { | ||
| 123 | switch (texture_type) { | ||
| 124 | case Tegra::Texture::TextureType::Texture1D: | ||
| 125 | return SurfaceTarget::Texture1D; | ||
| 126 | case Tegra::Texture::TextureType::Texture2D: | ||
| 127 | case Tegra::Texture::TextureType::Texture2DNoMipmap: | ||
| 128 | return SurfaceTarget::Texture2D; | ||
| 129 | case Tegra::Texture::TextureType::Texture1DArray: | ||
| 130 | return SurfaceTarget::Texture1DArray; | ||
| 131 | case Tegra::Texture::TextureType::Texture2DArray: | ||
| 132 | return SurfaceTarget::Texture2DArray; | ||
| 133 | default: | ||
| 134 | LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type)); | ||
| 135 | UNREACHABLE(); | ||
| 136 | return SurfaceTarget::Texture2D; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 112 | /** | 140 | /** |
| 113 | * Gets the compression factor for the specified PixelFormat. This applies to just the | 141 | * Gets the compression factor for the specified PixelFormat. This applies to just the |
| 114 | * "compressed width" and "compressed height", not the overall compression factor of a | 142 | * "compressed width" and "compressed height", not the overall compression factor of a |
| @@ -165,6 +193,7 @@ struct SurfaceParams { | |||
| 165 | 1, // RG8S | 193 | 1, // RG8S |
| 166 | 1, // RG32UI | 194 | 1, // RG32UI |
| 167 | 1, // R32UI | 195 | 1, // R32UI |
| 196 | 4, // ASTC_2D_8X8 | ||
| 168 | 1, // Z32F | 197 | 1, // Z32F |
| 169 | 1, // Z16 | 198 | 1, // Z16 |
| 170 | 1, // Z24S8 | 199 | 1, // Z24S8 |
| @@ -172,8 +201,8 @@ struct SurfaceParams { | |||
| 172 | 1, // Z32FS8 | 201 | 1, // Z32FS8 |
| 173 | }}; | 202 | }}; |
| 174 | 203 | ||
| 175 | ASSERT(static_cast<size_t>(format) < compression_factor_table.size()); | 204 | ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size()); |
| 176 | return compression_factor_table[static_cast<size_t>(format)]; | 205 | return compression_factor_table[static_cast<std::size_t>(format)]; |
| 177 | } | 206 | } |
| 178 | 207 | ||
| 179 | static constexpr u32 GetFormatBpp(PixelFormat format) { | 208 | static constexpr u32 GetFormatBpp(PixelFormat format) { |
| @@ -226,6 +255,7 @@ struct SurfaceParams { | |||
| 226 | 16, // RG8S | 255 | 16, // RG8S |
| 227 | 64, // RG32UI | 256 | 64, // RG32UI |
| 228 | 32, // R32UI | 257 | 32, // R32UI |
| 258 | 16, // ASTC_2D_8X8 | ||
| 229 | 32, // Z32F | 259 | 32, // Z32F |
| 230 | 16, // Z16 | 260 | 16, // Z16 |
| 231 | 32, // Z24S8 | 261 | 32, // Z24S8 |
| @@ -233,8 +263,8 @@ struct SurfaceParams { | |||
| 233 | 64, // Z32FS8 | 263 | 64, // Z32FS8 |
| 234 | }}; | 264 | }}; |
| 235 | 265 | ||
| 236 | ASSERT(static_cast<size_t>(format) < bpp_table.size()); | 266 | ASSERT(static_cast<std::size_t>(format) < bpp_table.size()); |
| 237 | return bpp_table[static_cast<size_t>(format)]; | 267 | return bpp_table[static_cast<std::size_t>(format)]; |
| 238 | } | 268 | } |
| 239 | 269 | ||
| 240 | u32 GetFormatBpp() const { | 270 | u32 GetFormatBpp() const { |
| @@ -270,6 +300,7 @@ struct SurfaceParams { | |||
| 270 | return PixelFormat::ABGR8S; | 300 | return PixelFormat::ABGR8S; |
| 271 | case Tegra::RenderTargetFormat::RGBA8_UINT: | 301 | case Tegra::RenderTargetFormat::RGBA8_UINT: |
| 272 | return PixelFormat::ABGR8UI; | 302 | return PixelFormat::ABGR8UI; |
| 303 | case Tegra::RenderTargetFormat::BGRA8_SRGB: | ||
| 273 | case Tegra::RenderTargetFormat::BGRA8_UNORM: | 304 | case Tegra::RenderTargetFormat::BGRA8_UNORM: |
| 274 | return PixelFormat::BGRA8; | 305 | return PixelFormat::BGRA8; |
| 275 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | 306 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: |
| @@ -288,6 +319,8 @@ struct SurfaceParams { | |||
| 288 | return PixelFormat::R11FG11FB10F; | 319 | return PixelFormat::R11FG11FB10F; |
| 289 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: | 320 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: |
| 290 | return PixelFormat::B5G6R5U; | 321 | return PixelFormat::B5G6R5U; |
| 322 | case Tegra::RenderTargetFormat::BGR5A1_UNORM: | ||
| 323 | return PixelFormat::A1B5G5R5U; | ||
| 291 | case Tegra::RenderTargetFormat::RGBA32_UINT: | 324 | case Tegra::RenderTargetFormat::RGBA32_UINT: |
| 292 | return PixelFormat::RGBA32UI; | 325 | return PixelFormat::RGBA32UI; |
| 293 | case Tegra::RenderTargetFormat::R8_UNORM: | 326 | case Tegra::RenderTargetFormat::R8_UNORM: |
| @@ -494,6 +527,8 @@ struct SurfaceParams { | |||
| 494 | return PixelFormat::BC6H_SF16; | 527 | return PixelFormat::BC6H_SF16; |
| 495 | case Tegra::Texture::TextureFormat::ASTC_2D_4X4: | 528 | case Tegra::Texture::TextureFormat::ASTC_2D_4X4: |
| 496 | return PixelFormat::ASTC_2D_4X4; | 529 | return PixelFormat::ASTC_2D_4X4; |
| 530 | case Tegra::Texture::TextureFormat::ASTC_2D_8X8: | ||
| 531 | return PixelFormat::ASTC_2D_8X8; | ||
| 497 | case Tegra::Texture::TextureFormat::R16_G16: | 532 | case Tegra::Texture::TextureFormat::R16_G16: |
| 498 | switch (component_type) { | 533 | switch (component_type) { |
| 499 | case Tegra::Texture::ComponentType::FLOAT: | 534 | case Tegra::Texture::ComponentType::FLOAT: |
| @@ -542,11 +577,13 @@ struct SurfaceParams { | |||
| 542 | case Tegra::RenderTargetFormat::RGBA8_UNORM: | 577 | case Tegra::RenderTargetFormat::RGBA8_UNORM: |
| 543 | case Tegra::RenderTargetFormat::RGBA8_SRGB: | 578 | case Tegra::RenderTargetFormat::RGBA8_SRGB: |
| 544 | case Tegra::RenderTargetFormat::BGRA8_UNORM: | 579 | case Tegra::RenderTargetFormat::BGRA8_UNORM: |
| 580 | case Tegra::RenderTargetFormat::BGRA8_SRGB: | ||
| 545 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | 581 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: |
| 546 | case Tegra::RenderTargetFormat::R8_UNORM: | 582 | case Tegra::RenderTargetFormat::R8_UNORM: |
| 547 | case Tegra::RenderTargetFormat::RG16_UNORM: | 583 | case Tegra::RenderTargetFormat::RG16_UNORM: |
| 548 | case Tegra::RenderTargetFormat::R16_UNORM: | 584 | case Tegra::RenderTargetFormat::R16_UNORM: |
| 549 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: | 585 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: |
| 586 | case Tegra::RenderTargetFormat::BGR5A1_UNORM: | ||
| 550 | case Tegra::RenderTargetFormat::RG8_UNORM: | 587 | case Tegra::RenderTargetFormat::RG8_UNORM: |
| 551 | case Tegra::RenderTargetFormat::RGBA16_UNORM: | 588 | case Tegra::RenderTargetFormat::RGBA16_UNORM: |
| 552 | return ComponentType::UNorm; | 589 | return ComponentType::UNorm; |
| @@ -607,16 +644,18 @@ struct SurfaceParams { | |||
| 607 | } | 644 | } |
| 608 | 645 | ||
| 609 | static SurfaceType GetFormatType(PixelFormat pixel_format) { | 646 | static SurfaceType GetFormatType(PixelFormat pixel_format) { |
| 610 | if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxColorFormat)) { | 647 | if (static_cast<std::size_t>(pixel_format) < |
| 648 | static_cast<std::size_t>(PixelFormat::MaxColorFormat)) { | ||
| 611 | return SurfaceType::ColorTexture; | 649 | return SurfaceType::ColorTexture; |
| 612 | } | 650 | } |
| 613 | 651 | ||
| 614 | if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxDepthFormat)) { | 652 | if (static_cast<std::size_t>(pixel_format) < |
| 653 | static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) { | ||
| 615 | return SurfaceType::Depth; | 654 | return SurfaceType::Depth; |
| 616 | } | 655 | } |
| 617 | 656 | ||
| 618 | if (static_cast<size_t>(pixel_format) < | 657 | if (static_cast<std::size_t>(pixel_format) < |
| 619 | static_cast<size_t>(PixelFormat::MaxDepthStencilFormat)) { | 658 | static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { |
| 620 | return SurfaceType::DepthStencil; | 659 | return SurfaceType::DepthStencil; |
| 621 | } | 660 | } |
| 622 | 661 | ||
| @@ -630,20 +669,19 @@ struct SurfaceParams { | |||
| 630 | MathUtil::Rectangle<u32> GetRect() const; | 669 | MathUtil::Rectangle<u32> GetRect() const; |
| 631 | 670 | ||
| 632 | /// Returns the size of this surface in bytes, adjusted for compression | 671 | /// Returns the size of this surface in bytes, adjusted for compression |
| 633 | size_t SizeInBytes() const { | 672 | std::size_t SizeInBytes() const { |
| 634 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; | 673 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; |
| 635 | ASSERT(width % compression_factor == 0); | 674 | ASSERT(width % compression_factor == 0); |
| 636 | ASSERT(height % compression_factor == 0); | 675 | ASSERT(height % compression_factor == 0); |
| 637 | return (width / compression_factor) * (height / compression_factor) * | 676 | return (width / compression_factor) * (height / compression_factor) * |
| 638 | GetFormatBpp(pixel_format) / CHAR_BIT; | 677 | GetFormatBpp(pixel_format) * depth / CHAR_BIT; |
| 639 | } | 678 | } |
| 640 | 679 | ||
| 641 | /// Creates SurfaceParams from a texture configuration | 680 | /// Creates SurfaceParams from a texture configuration |
| 642 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config); | 681 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config); |
| 643 | 682 | ||
| 644 | /// Creates SurfaceParams from a framebuffer configuration | 683 | /// Creates SurfaceParams from a framebuffer configuration |
| 645 | static SurfaceParams CreateForFramebuffer( | 684 | static SurfaceParams CreateForFramebuffer(std::size_t index); |
| 646 | const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config); | ||
| 647 | 685 | ||
| 648 | /// Creates SurfaceParams for a depth buffer configuration | 686 | /// Creates SurfaceParams for a depth buffer configuration |
| 649 | static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, | 687 | static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, |
| @@ -652,8 +690,8 @@ struct SurfaceParams { | |||
| 652 | 690 | ||
| 653 | /// Checks if surfaces are compatible for caching | 691 | /// Checks if surfaces are compatible for caching |
| 654 | bool IsCompatibleSurface(const SurfaceParams& other) const { | 692 | bool IsCompatibleSurface(const SurfaceParams& other) const { |
| 655 | return std::tie(pixel_format, type, cache_width, cache_height) == | 693 | return std::tie(pixel_format, type, width, height) == |
| 656 | std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height); | 694 | std::tie(other.pixel_format, other.type, other.width, other.height); |
| 657 | } | 695 | } |
| 658 | 696 | ||
| 659 | VAddr addr; | 697 | VAddr addr; |
| @@ -664,12 +702,10 @@ struct SurfaceParams { | |||
| 664 | SurfaceType type; | 702 | SurfaceType type; |
| 665 | u32 width; | 703 | u32 width; |
| 666 | u32 height; | 704 | u32 height; |
| 705 | u32 depth; | ||
| 667 | u32 unaligned_height; | 706 | u32 unaligned_height; |
| 668 | size_t size_in_bytes; | 707 | std::size_t size_in_bytes; |
| 669 | 708 | SurfaceTarget target; | |
| 670 | // Parameters used for caching only | ||
| 671 | u32 cache_width; | ||
| 672 | u32 cache_height; | ||
| 673 | }; | 709 | }; |
| 674 | 710 | ||
| 675 | }; // namespace OpenGL | 711 | }; // namespace OpenGL |
| @@ -685,7 +721,7 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> { | |||
| 685 | namespace std { | 721 | namespace std { |
| 686 | template <> | 722 | template <> |
| 687 | struct hash<SurfaceReserveKey> { | 723 | struct hash<SurfaceReserveKey> { |
| 688 | size_t operator()(const SurfaceReserveKey& k) const { | 724 | std::size_t operator()(const SurfaceReserveKey& k) const { |
| 689 | return k.Hash(); | 725 | return k.Hash(); |
| 690 | } | 726 | } |
| 691 | }; | 727 | }; |
| @@ -701,7 +737,7 @@ public: | |||
| 701 | return params.addr; | 737 | return params.addr; |
| 702 | } | 738 | } |
| 703 | 739 | ||
| 704 | size_t GetSizeInBytes() const { | 740 | std::size_t GetSizeInBytes() const { |
| 705 | return params.size_in_bytes; | 741 | return params.size_in_bytes; |
| 706 | } | 742 | } |
| 707 | 743 | ||
| @@ -709,6 +745,10 @@ public: | |||
| 709 | return texture; | 745 | return texture; |
| 710 | } | 746 | } |
| 711 | 747 | ||
| 748 | GLenum Target() const { | ||
| 749 | return gl_target; | ||
| 750 | } | ||
| 751 | |||
| 712 | static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { | 752 | static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { |
| 713 | if (format == SurfaceParams::PixelFormat::Invalid) | 753 | if (format == SurfaceParams::PixelFormat::Invalid) |
| 714 | return 0; | 754 | return 0; |
| @@ -724,14 +764,14 @@ public: | |||
| 724 | void LoadGLBuffer(); | 764 | void LoadGLBuffer(); |
| 725 | void FlushGLBuffer(); | 765 | void FlushGLBuffer(); |
| 726 | 766 | ||
| 727 | // Upload/Download data in gl_buffer in/to this surface's texture | 767 | // Upload data in gl_buffer to this surface's texture |
| 728 | void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); | 768 | void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); |
| 729 | void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); | ||
| 730 | 769 | ||
| 731 | private: | 770 | private: |
| 732 | OGLTexture texture; | 771 | OGLTexture texture; |
| 733 | std::vector<u8> gl_buffer; | 772 | std::vector<u8> gl_buffer; |
| 734 | SurfaceParams params; | 773 | SurfaceParams params; |
| 774 | GLenum gl_target; | ||
| 735 | }; | 775 | }; |
| 736 | 776 | ||
| 737 | class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { | 777 | class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { |
| @@ -741,9 +781,11 @@ public: | |||
| 741 | /// Get a surface based on the texture configuration | 781 | /// Get a surface based on the texture configuration |
| 742 | Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); | 782 | Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); |
| 743 | 783 | ||
| 744 | /// Get the color and depth surfaces based on the framebuffer configuration | 784 | /// Get the depth surface based on the framebuffer configuration |
| 745 | SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, | 785 | Surface GetDepthBufferSurface(bool preserve_contents); |
| 746 | bool preserve_contents); | 786 | |
| 787 | /// Get the color surface based on the framebuffer configuration and the specified render target | ||
| 788 | Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); | ||
| 747 | 789 | ||
| 748 | /// Flushes the surface to Switch memory | 790 | /// Flushes the surface to Switch memory |
| 749 | void FlushSurface(const Surface& surface); | 791 | void FlushSurface(const Surface& surface); |
| @@ -774,6 +816,10 @@ private: | |||
| 774 | 816 | ||
| 775 | OGLFramebuffer read_framebuffer; | 817 | OGLFramebuffer read_framebuffer; |
| 776 | OGLFramebuffer draw_framebuffer; | 818 | OGLFramebuffer draw_framebuffer; |
| 819 | |||
| 820 | /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one | ||
| 821 | /// using the new format. | ||
| 822 | OGLBuffer copy_pbo; | ||
| 777 | }; | 823 | }; |
| 778 | 824 | ||
| 779 | } // namespace OpenGL | 825 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index ac9adfd83..894fe6eae 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -13,8 +13,8 @@ namespace OpenGL { | |||
| 13 | 13 | ||
| 14 | /// Gets the address for the specified shader stage program | 14 | /// Gets the address for the specified shader stage program |
| 15 | static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { | 15 | static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { |
| 16 | auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 16 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 17 | auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)]; | 17 | const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; |
| 18 | return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + | 18 | return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + |
| 19 | shader_config.offset); | 19 | shader_config.offset); |
| 20 | } | 20 | } |
| @@ -28,7 +28,7 @@ static GLShader::ProgramCode GetShaderCode(VAddr addr) { | |||
| 28 | 28 | ||
| 29 | /// Helper function to set shader uniform block bindings for a single shader stage | 29 | /// Helper function to set shader uniform block bindings for a single shader stage |
| 30 | static void SetShaderUniformBlockBinding(GLuint shader, const char* name, | 30 | static void SetShaderUniformBlockBinding(GLuint shader, const char* name, |
| 31 | Maxwell::ShaderStage binding, size_t expected_size) { | 31 | Maxwell::ShaderStage binding, std::size_t expected_size) { |
| 32 | const GLuint ub_index = glGetUniformBlockIndex(shader, name); | 32 | const GLuint ub_index = glGetUniformBlockIndex(shader, name); |
| 33 | if (ub_index == GL_INVALID_INDEX) { | 33 | if (ub_index == GL_INVALID_INDEX) { |
| 34 | return; | 34 | return; |
| @@ -36,7 +36,7 @@ static void SetShaderUniformBlockBinding(GLuint shader, const char* name, | |||
| 36 | 36 | ||
| 37 | GLint ub_size = 0; | 37 | GLint ub_size = 0; |
| 38 | glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); | 38 | glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); |
| 39 | ASSERT_MSG(static_cast<size_t>(ub_size) == expected_size, | 39 | ASSERT_MSG(static_cast<std::size_t>(ub_size) == expected_size, |
| 40 | "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size); | 40 | "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size); |
| 41 | glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); | 41 | glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); |
| 42 | } | 42 | } |
| @@ -85,23 +85,23 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | |||
| 85 | SetShaderUniformBlockBindings(program.handle); | 85 | SetShaderUniformBlockBindings(program.handle); |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | GLuint CachedShader::GetProgramResourceIndex(const std::string& name) { | 88 | GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { |
| 89 | auto search{resource_cache.find(name)}; | 89 | const auto search{resource_cache.find(buffer.GetHash())}; |
| 90 | if (search == resource_cache.end()) { | 90 | if (search == resource_cache.end()) { |
| 91 | const GLuint index{ | 91 | const GLuint index{ |
| 92 | glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, name.c_str())}; | 92 | glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, buffer.GetName().c_str())}; |
| 93 | resource_cache[name] = index; | 93 | resource_cache[buffer.GetHash()] = index; |
| 94 | return index; | 94 | return index; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | return search->second; | 97 | return search->second; |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | GLint CachedShader::GetUniformLocation(const std::string& name) { | 100 | GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) { |
| 101 | auto search{uniform_cache.find(name)}; | 101 | const auto search{uniform_cache.find(sampler.GetHash())}; |
| 102 | if (search == uniform_cache.end()) { | 102 | if (search == uniform_cache.end()) { |
| 103 | const GLint index{glGetUniformLocation(program.handle, name.c_str())}; | 103 | const GLint index{glGetUniformLocation(program.handle, sampler.GetName().c_str())}; |
| 104 | uniform_cache[name] = index; | 104 | uniform_cache[sampler.GetHash()] = index; |
| 105 | return index; | 105 | return index; |
| 106 | } | 106 | } |
| 107 | 107 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 759987604..9bafe43a9 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <unordered_map> | ||
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/rasterizer_cache.h" | 11 | #include "video_core/rasterizer_cache.h" |
| @@ -28,7 +28,7 @@ public: | |||
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /// Gets the size of the shader in guest memory, required for cache management | 30 | /// Gets the size of the shader in guest memory, required for cache management |
| 31 | size_t GetSizeInBytes() const { | 31 | std::size_t GetSizeInBytes() const { |
| 32 | return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); | 32 | return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| @@ -43,10 +43,10 @@ public: | |||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | /// Gets the GL program resource location for the specified resource, caching as needed | 45 | /// Gets the GL program resource location for the specified resource, caching as needed |
| 46 | GLuint GetProgramResourceIndex(const std::string& name); | 46 | GLuint GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer); |
| 47 | 47 | ||
| 48 | /// Gets the GL uniform location for the specified resource, caching as needed | 48 | /// Gets the GL uniform location for the specified resource, caching as needed |
| 49 | GLint GetUniformLocation(const std::string& name); | 49 | GLint GetUniformLocation(const GLShader::SamplerEntry& sampler); |
| 50 | 50 | ||
| 51 | private: | 51 | private: |
| 52 | VAddr addr; | 52 | VAddr addr; |
| @@ -55,8 +55,8 @@ private: | |||
| 55 | GLShader::ShaderEntries entries; | 55 | GLShader::ShaderEntries entries; |
| 56 | OGLProgram program; | 56 | OGLProgram program; |
| 57 | 57 | ||
| 58 | std::unordered_map<std::string, GLuint> resource_cache; | 58 | std::map<u32, GLuint> resource_cache; |
| 59 | std::unordered_map<std::string, GLint> uniform_cache; | 59 | std::map<u32, GLint> uniform_cache; |
| 60 | }; | 60 | }; |
| 61 | 61 | ||
| 62 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { | 62 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 391c92d47..b3e95187e 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "video_core/engines/shader_bytecode.h" | 14 | #include "video_core/engines/shader_bytecode.h" |
| 15 | #include "video_core/engines/shader_header.h" | ||
| 15 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 16 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 16 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 17 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 17 | 18 | ||
| @@ -26,7 +27,7 @@ using Tegra::Shader::Sampler; | |||
| 26 | using Tegra::Shader::SubOp; | 27 | using Tegra::Shader::SubOp; |
| 27 | 28 | ||
| 28 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; | 29 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
| 29 | constexpr u32 PROGRAM_HEADER_SIZE = 0x50; | 30 | constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); |
| 30 | 31 | ||
| 31 | class DecompileFail : public std::runtime_error { | 32 | class DecompileFail : public std::runtime_error { |
| 32 | public: | 33 | public: |
| @@ -113,7 +114,7 @@ private: | |||
| 113 | 114 | ||
| 114 | /// Scans a range of code for labels and determines the exit method. | 115 | /// Scans a range of code for labels and determines the exit method. |
| 115 | ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) { | 116 | ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) { |
| 116 | auto [iter, inserted] = | 117 | const auto [iter, inserted] = |
| 117 | exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined); | 118 | exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined); |
| 118 | ExitMethod& exit_method = iter->second; | 119 | ExitMethod& exit_method = iter->second; |
| 119 | if (!inserted) | 120 | if (!inserted) |
| @@ -131,22 +132,22 @@ private: | |||
| 131 | if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) { | 132 | if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) { |
| 132 | return exit_method = ExitMethod::AlwaysEnd; | 133 | return exit_method = ExitMethod::AlwaysEnd; |
| 133 | } else { | 134 | } else { |
| 134 | ExitMethod not_met = Scan(offset + 1, end, labels); | 135 | const ExitMethod not_met = Scan(offset + 1, end, labels); |
| 135 | return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met); | 136 | return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met); |
| 136 | } | 137 | } |
| 137 | } | 138 | } |
| 138 | case OpCode::Id::BRA: { | 139 | case OpCode::Id::BRA: { |
| 139 | u32 target = offset + instr.bra.GetBranchTarget(); | 140 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 140 | labels.insert(target); | 141 | labels.insert(target); |
| 141 | ExitMethod no_jmp = Scan(offset + 1, end, labels); | 142 | const ExitMethod no_jmp = Scan(offset + 1, end, labels); |
| 142 | ExitMethod jmp = Scan(target, end, labels); | 143 | const ExitMethod jmp = Scan(target, end, labels); |
| 143 | return exit_method = ParallelExit(no_jmp, jmp); | 144 | return exit_method = ParallelExit(no_jmp, jmp); |
| 144 | } | 145 | } |
| 145 | case OpCode::Id::SSY: { | 146 | case OpCode::Id::SSY: { |
| 146 | // The SSY instruction uses a similar encoding as the BRA instruction. | 147 | // The SSY instruction uses a similar encoding as the BRA instruction. |
| 147 | ASSERT_MSG(instr.bra.constant_buffer == 0, | 148 | ASSERT_MSG(instr.bra.constant_buffer == 0, |
| 148 | "Constant buffer SSY is not supported"); | 149 | "Constant buffer SSY is not supported"); |
| 149 | u32 target = offset + instr.bra.GetBranchTarget(); | 150 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 150 | labels.insert(target); | 151 | labels.insert(target); |
| 151 | // Continue scanning for an exit method. | 152 | // Continue scanning for an exit method. |
| 152 | break; | 153 | break; |
| @@ -189,7 +190,7 @@ public: | |||
| 189 | 190 | ||
| 190 | private: | 191 | private: |
| 191 | void AppendIndentation() { | 192 | void AppendIndentation() { |
| 192 | shader_source.append(static_cast<size_t>(scope) * 4, ' '); | 193 | shader_source.append(static_cast<std::size_t>(scope) * 4, ' '); |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | std::string shader_source; | 196 | std::string shader_source; |
| @@ -208,7 +209,7 @@ public: | |||
| 208 | UnsignedInteger, | 209 | UnsignedInteger, |
| 209 | }; | 210 | }; |
| 210 | 211 | ||
| 211 | GLSLRegister(size_t index, const std::string& suffix) : index{index}, suffix{suffix} {} | 212 | GLSLRegister(std::size_t index, const std::string& suffix) : index{index}, suffix{suffix} {} |
| 212 | 213 | ||
| 213 | /// Gets the GLSL type string for a register | 214 | /// Gets the GLSL type string for a register |
| 214 | static std::string GetTypeString() { | 215 | static std::string GetTypeString() { |
| @@ -226,15 +227,23 @@ public: | |||
| 226 | } | 227 | } |
| 227 | 228 | ||
| 228 | /// Returns the index of the register | 229 | /// Returns the index of the register |
| 229 | size_t GetIndex() const { | 230 | std::size_t GetIndex() const { |
| 230 | return index; | 231 | return index; |
| 231 | } | 232 | } |
| 232 | 233 | ||
| 233 | private: | 234 | private: |
| 234 | const size_t index; | 235 | const std::size_t index; |
| 235 | const std::string& suffix; | 236 | const std::string& suffix; |
| 236 | }; | 237 | }; |
| 237 | 238 | ||
| 239 | enum class InternalFlag : u64 { | ||
| 240 | ZeroFlag = 0, | ||
| 241 | CarryFlag = 1, | ||
| 242 | OverflowFlag = 2, | ||
| 243 | NaNFlag = 3, | ||
| 244 | Amount | ||
| 245 | }; | ||
| 246 | |||
| 238 | /** | 247 | /** |
| 239 | * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state | 248 | * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state |
| 240 | * of all registers (e.g. whether they are currently being used as Floats or Integers), and | 249 | * of all registers (e.g. whether they are currently being used as Floats or Integers), and |
| @@ -247,6 +256,7 @@ public: | |||
| 247 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) | 256 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) |
| 248 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { | 257 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { |
| 249 | BuildRegisterList(); | 258 | BuildRegisterList(); |
| 259 | BuildInputList(); | ||
| 250 | } | 260 | } |
| 251 | 261 | ||
| 252 | /** | 262 | /** |
| @@ -327,13 +337,19 @@ public: | |||
| 327 | void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, | 337 | void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, |
| 328 | const std::string& value, u64 dest_num_components, | 338 | const std::string& value, u64 dest_num_components, |
| 329 | u64 value_num_components, bool is_saturated = false, | 339 | u64 value_num_components, bool is_saturated = false, |
| 330 | u64 dest_elem = 0, Register::Size size = Register::Size::Word) { | 340 | u64 dest_elem = 0, Register::Size size = Register::Size::Word, |
| 341 | bool sets_cc = false) { | ||
| 331 | ASSERT_MSG(!is_saturated, "Unimplemented"); | 342 | ASSERT_MSG(!is_saturated, "Unimplemented"); |
| 332 | 343 | ||
| 333 | const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; | 344 | const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; |
| 334 | 345 | ||
| 335 | SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', | 346 | SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', |
| 336 | dest_num_components, value_num_components, dest_elem); | 347 | dest_num_components, value_num_components, dest_elem); |
| 348 | |||
| 349 | if (sets_cc) { | ||
| 350 | const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; | ||
| 351 | SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); | ||
| 352 | } | ||
| 337 | } | 353 | } |
| 338 | 354 | ||
| 339 | /** | 355 | /** |
| @@ -343,12 +359,33 @@ public: | |||
| 343 | * @param elem The element to use for the operation. | 359 | * @param elem The element to use for the operation. |
| 344 | * @param attribute The input attribute to use as the source value. | 360 | * @param attribute The input attribute to use as the source value. |
| 345 | */ | 361 | */ |
| 346 | void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute) { | 362 | void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, |
| 347 | std::string dest = GetRegisterAsFloat(reg); | 363 | const Tegra::Shader::IpaMode& input_mode) { |
| 348 | std::string src = GetInputAttribute(attribute) + GetSwizzle(elem); | 364 | const std::string dest = GetRegisterAsFloat(reg); |
| 365 | const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem); | ||
| 349 | shader.AddLine(dest + " = " + src + ';'); | 366 | shader.AddLine(dest + " = " + src + ';'); |
| 350 | } | 367 | } |
| 351 | 368 | ||
| 369 | std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { | ||
| 370 | switch (cc) { | ||
| 371 | case Tegra::Shader::ControlCode::NEU: | ||
| 372 | return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; | ||
| 373 | default: | ||
| 374 | LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); | ||
| 375 | UNREACHABLE(); | ||
| 376 | return "false"; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | std::string GetInternalFlag(const InternalFlag ii) const { | ||
| 381 | const u32 code = static_cast<u32>(ii); | ||
| 382 | return "internalFlag_" + std::to_string(code) + suffix; | ||
| 383 | } | ||
| 384 | |||
| 385 | void SetInternalFlag(const InternalFlag ii, const std::string& value) const { | ||
| 386 | shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); | ||
| 387 | } | ||
| 388 | |||
| 352 | /** | 389 | /** |
| 353 | * Writes code that does a output attribute assignment to register operation. Output attributes | 390 | * Writes code that does a output attribute assignment to register operation. Output attributes |
| 354 | * are stored as floats, so this may require conversion. | 391 | * are stored as floats, so this may require conversion. |
| @@ -357,8 +394,8 @@ public: | |||
| 357 | * @param reg The register to use as the source value. | 394 | * @param reg The register to use as the source value. |
| 358 | */ | 395 | */ |
| 359 | void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { | 396 | void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { |
| 360 | std::string dest = GetOutputAttribute(attribute); | 397 | const std::string dest = GetOutputAttribute(attribute); |
| 361 | std::string src = GetRegisterAsFloat(reg); | 398 | const std::string src = GetRegisterAsFloat(reg); |
| 362 | 399 | ||
| 363 | if (!dest.empty()) { | 400 | if (!dest.empty()) { |
| 364 | // Can happen with unknown/unimplemented output attributes, in which case we ignore the | 401 | // Can happen with unknown/unimplemented output attributes, in which case we ignore the |
| @@ -391,9 +428,9 @@ public: | |||
| 391 | GLSLRegister::Type type) { | 428 | GLSLRegister::Type type) { |
| 392 | declr_const_buffers[cbuf_index].MarkAsUsedIndirect(cbuf_index, stage); | 429 | declr_const_buffers[cbuf_index].MarkAsUsedIndirect(cbuf_index, stage); |
| 393 | 430 | ||
| 394 | std::string final_offset = fmt::format("({} + {})", index_str, offset / 4); | 431 | const std::string final_offset = fmt::format("({} + {})", index_str, offset / 4); |
| 395 | std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" + | 432 | const std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" + |
| 396 | final_offset + " % 4]"; | 433 | final_offset + " % 4]"; |
| 397 | 434 | ||
| 398 | if (type == GLSLRegister::Type::Float) { | 435 | if (type == GLSLRegister::Type::Float) { |
| 399 | return value; | 436 | return value; |
| @@ -412,12 +449,19 @@ public: | |||
| 412 | } | 449 | } |
| 413 | declarations.AddNewLine(); | 450 | declarations.AddNewLine(); |
| 414 | 451 | ||
| 415 | for (const auto& index : declr_input_attribute) { | 452 | for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { |
| 453 | const InternalFlag code = static_cast<InternalFlag>(ii); | ||
| 454 | declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); | ||
| 455 | } | ||
| 456 | declarations.AddNewLine(); | ||
| 457 | |||
| 458 | for (const auto element : declr_input_attribute) { | ||
| 416 | // TODO(bunnei): Use proper number of elements for these | 459 | // TODO(bunnei): Use proper number of elements for these |
| 417 | declarations.AddLine("layout(location = " + | 460 | u32 idx = |
| 418 | std::to_string(static_cast<u32>(index) - | 461 | static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); |
| 419 | static_cast<u32>(Attribute::Index::Attribute_0)) + | 462 | declarations.AddLine("layout(location = " + std::to_string(idx) + ")" + |
| 420 | ") in vec4 " + GetInputAttribute(index) + ';'); | 463 | GetInputFlags(element.first) + "in vec4 " + |
| 464 | GetInputAttribute(element.first, element.second) + ';'); | ||
| 421 | } | 465 | } |
| 422 | declarations.AddNewLine(); | 466 | declarations.AddNewLine(); |
| 423 | 467 | ||
| @@ -440,13 +484,12 @@ public: | |||
| 440 | } | 484 | } |
| 441 | declarations.AddNewLine(); | 485 | declarations.AddNewLine(); |
| 442 | 486 | ||
| 443 | // Append the sampler2D array for the used textures. | 487 | const auto& samplers = GetSamplers(); |
| 444 | size_t num_samplers = GetSamplers().size(); | 488 | for (const auto& sampler : samplers) { |
| 445 | if (num_samplers > 0) { | 489 | declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + |
| 446 | declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + | 490 | ';'); |
| 447 | std::to_string(num_samplers) + "];"); | ||
| 448 | declarations.AddNewLine(); | ||
| 449 | } | 491 | } |
| 492 | declarations.AddNewLine(); | ||
| 450 | } | 493 | } |
| 451 | 494 | ||
| 452 | /// Returns a list of constant buffer declarations | 495 | /// Returns a list of constant buffer declarations |
| @@ -458,27 +501,29 @@ public: | |||
| 458 | } | 501 | } |
| 459 | 502 | ||
| 460 | /// Returns a list of samplers used in the shader | 503 | /// Returns a list of samplers used in the shader |
| 461 | std::vector<SamplerEntry> GetSamplers() const { | 504 | const std::vector<SamplerEntry>& GetSamplers() const { |
| 462 | return used_samplers; | 505 | return used_samplers; |
| 463 | } | 506 | } |
| 464 | 507 | ||
| 465 | /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if | 508 | /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if |
| 466 | /// necessary. | 509 | /// necessary. |
| 467 | std::string AccessSampler(const Sampler& sampler) { | 510 | std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, |
| 468 | size_t offset = static_cast<size_t>(sampler.index.Value()); | 511 | bool is_array) { |
| 512 | const std::size_t offset = static_cast<std::size_t>(sampler.index.Value()); | ||
| 469 | 513 | ||
| 470 | // If this sampler has already been used, return the existing mapping. | 514 | // If this sampler has already been used, return the existing mapping. |
| 471 | auto itr = | 515 | const auto itr = |
| 472 | std::find_if(used_samplers.begin(), used_samplers.end(), | 516 | std::find_if(used_samplers.begin(), used_samplers.end(), |
| 473 | [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); | 517 | [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); |
| 474 | 518 | ||
| 475 | if (itr != used_samplers.end()) { | 519 | if (itr != used_samplers.end()) { |
| 520 | ASSERT(itr->GetType() == type && itr->IsArray() == is_array); | ||
| 476 | return itr->GetName(); | 521 | return itr->GetName(); |
| 477 | } | 522 | } |
| 478 | 523 | ||
| 479 | // Otherwise create a new mapping for this sampler | 524 | // Otherwise create a new mapping for this sampler |
| 480 | size_t next_index = used_samplers.size(); | 525 | const std::size_t next_index = used_samplers.size(); |
| 481 | SamplerEntry entry{stage, offset, next_index}; | 526 | const SamplerEntry entry{stage, offset, next_index, type, is_array}; |
| 482 | used_samplers.emplace_back(entry); | 527 | used_samplers.emplace_back(entry); |
| 483 | return entry.GetName(); | 528 | return entry.GetName(); |
| 484 | } | 529 | } |
| @@ -527,16 +572,29 @@ private: | |||
| 527 | void BuildRegisterList() { | 572 | void BuildRegisterList() { |
| 528 | regs.reserve(Register::NumRegisters); | 573 | regs.reserve(Register::NumRegisters); |
| 529 | 574 | ||
| 530 | for (size_t index = 0; index < Register::NumRegisters; ++index) { | 575 | for (std::size_t index = 0; index < Register::NumRegisters; ++index) { |
| 531 | regs.emplace_back(index, suffix); | 576 | regs.emplace_back(index, suffix); |
| 532 | } | 577 | } |
| 533 | } | 578 | } |
| 534 | 579 | ||
| 580 | void BuildInputList() { | ||
| 581 | const u32 size = static_cast<u32>(Attribute::Index::Attribute_31) - | ||
| 582 | static_cast<u32>(Attribute::Index::Attribute_0) + 1; | ||
| 583 | declr_input_attribute.reserve(size); | ||
| 584 | } | ||
| 585 | |||
| 535 | /// Generates code representing an input attribute register. | 586 | /// Generates code representing an input attribute register. |
| 536 | std::string GetInputAttribute(Attribute::Index attribute) { | 587 | std::string GetInputAttribute(Attribute::Index attribute, |
| 588 | const Tegra::Shader::IpaMode& input_mode) { | ||
| 537 | switch (attribute) { | 589 | switch (attribute) { |
| 538 | case Attribute::Index::Position: | 590 | case Attribute::Index::Position: |
| 539 | return "position"; | 591 | if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { |
| 592 | return "position"; | ||
| 593 | } else { | ||
| 594 | return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; | ||
| 595 | } | ||
| 596 | case Attribute::Index::PointCoord: | ||
| 597 | return "vec4(gl_PointCoord.x, gl_PointCoord.y, 0, 0)"; | ||
| 540 | case Attribute::Index::TessCoordInstanceIDVertexID: | 598 | case Attribute::Index::TessCoordInstanceIDVertexID: |
| 541 | // TODO(Subv): Find out what the values are for the first two elements when inside a | 599 | // TODO(Subv): Find out what the values are for the first two elements when inside a |
| 542 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval | 600 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval |
| @@ -552,7 +610,14 @@ private: | |||
| 552 | static_cast<u32>(Attribute::Index::Attribute_0)}; | 610 | static_cast<u32>(Attribute::Index::Attribute_0)}; |
| 553 | if (attribute >= Attribute::Index::Attribute_0 && | 611 | if (attribute >= Attribute::Index::Attribute_0 && |
| 554 | attribute <= Attribute::Index::Attribute_31) { | 612 | attribute <= Attribute::Index::Attribute_31) { |
| 555 | declr_input_attribute.insert(attribute); | 613 | if (declr_input_attribute.count(attribute) == 0) { |
| 614 | declr_input_attribute[attribute] = input_mode; | ||
| 615 | } else { | ||
| 616 | if (declr_input_attribute[attribute] != input_mode) { | ||
| 617 | LOG_CRITICAL(HW_GPU, "Same Input multiple input modes"); | ||
| 618 | UNREACHABLE(); | ||
| 619 | } | ||
| 620 | } | ||
| 556 | return "input_attribute_" + std::to_string(index); | 621 | return "input_attribute_" + std::to_string(index); |
| 557 | } | 622 | } |
| 558 | 623 | ||
| @@ -563,6 +628,49 @@ private: | |||
| 563 | return "vec4(0, 0, 0, 0)"; | 628 | return "vec4(0, 0, 0, 0)"; |
| 564 | } | 629 | } |
| 565 | 630 | ||
| 631 | std::string GetInputFlags(const Attribute::Index attribute) { | ||
| 632 | const Tegra::Shader::IpaSampleMode sample_mode = | ||
| 633 | declr_input_attribute[attribute].sampling_mode; | ||
| 634 | const Tegra::Shader::IpaInterpMode interp_mode = | ||
| 635 | declr_input_attribute[attribute].interpolation_mode; | ||
| 636 | std::string out; | ||
| 637 | switch (interp_mode) { | ||
| 638 | case Tegra::Shader::IpaInterpMode::Flat: { | ||
| 639 | out += "flat "; | ||
| 640 | break; | ||
| 641 | } | ||
| 642 | case Tegra::Shader::IpaInterpMode::Linear: { | ||
| 643 | out += "noperspective "; | ||
| 644 | break; | ||
| 645 | } | ||
| 646 | case Tegra::Shader::IpaInterpMode::Perspective: { | ||
| 647 | // Default, Smooth | ||
| 648 | break; | ||
| 649 | } | ||
| 650 | default: { | ||
| 651 | LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode)); | ||
| 652 | UNREACHABLE(); | ||
| 653 | } | ||
| 654 | } | ||
| 655 | switch (sample_mode) { | ||
| 656 | case Tegra::Shader::IpaSampleMode::Centroid: { | ||
| 657 | // Note not implemented, it can be implemented with the "centroid " keyword in glsl; | ||
| 658 | LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented"); | ||
| 659 | UNREACHABLE(); | ||
| 660 | break; | ||
| 661 | } | ||
| 662 | case Tegra::Shader::IpaSampleMode::Default: { | ||
| 663 | // Default, n/a | ||
| 664 | break; | ||
| 665 | } | ||
| 666 | default: { | ||
| 667 | LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode)); | ||
| 668 | UNREACHABLE(); | ||
| 669 | } | ||
| 670 | } | ||
| 671 | return out; | ||
| 672 | } | ||
| 673 | |||
| 566 | /// Generates code representing an output attribute register. | 674 | /// Generates code representing an output attribute register. |
| 567 | std::string GetOutputAttribute(Attribute::Index attribute) { | 675 | std::string GetOutputAttribute(Attribute::Index attribute) { |
| 568 | switch (attribute) { | 676 | switch (attribute) { |
| @@ -593,7 +701,7 @@ private: | |||
| 593 | ShaderWriter& shader; | 701 | ShaderWriter& shader; |
| 594 | ShaderWriter& declarations; | 702 | ShaderWriter& declarations; |
| 595 | std::vector<GLSLRegister> regs; | 703 | std::vector<GLSLRegister> regs; |
| 596 | std::set<Attribute::Index> declr_input_attribute; | 704 | std::unordered_map<Attribute::Index, Tegra::Shader::IpaMode> declr_input_attribute; |
| 597 | std::set<Attribute::Index> declr_output_attribute; | 705 | std::set<Attribute::Index> declr_output_attribute; |
| 598 | std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; | 706 | std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; |
| 599 | std::vector<SamplerEntry> used_samplers; | 707 | std::vector<SamplerEntry> used_samplers; |
| @@ -607,7 +715,7 @@ public: | |||
| 607 | u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) | 715 | u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) |
| 608 | : subroutines(subroutines), program_code(program_code), main_offset(main_offset), | 716 | : subroutines(subroutines), program_code(program_code), main_offset(main_offset), |
| 609 | stage(stage), suffix(suffix) { | 717 | stage(stage), suffix(suffix) { |
| 610 | 718 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); | |
| 611 | Generate(suffix); | 719 | Generate(suffix); |
| 612 | } | 720 | } |
| 613 | 721 | ||
| @@ -621,26 +729,9 @@ public: | |||
| 621 | } | 729 | } |
| 622 | 730 | ||
| 623 | private: | 731 | private: |
| 624 | // Shader program header for a Fragment Shader. | ||
| 625 | struct FragmentHeader { | ||
| 626 | INSERT_PADDING_WORDS(5); | ||
| 627 | INSERT_PADDING_WORDS(13); | ||
| 628 | u32 enabled_color_outputs; | ||
| 629 | union { | ||
| 630 | BitField<0, 1, u32> writes_samplemask; | ||
| 631 | BitField<1, 1, u32> writes_depth; | ||
| 632 | }; | ||
| 633 | |||
| 634 | bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { | ||
| 635 | u32 bit = render_target * 4 + component; | ||
| 636 | return enabled_color_outputs & (1 << bit); | ||
| 637 | } | ||
| 638 | }; | ||
| 639 | static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong"); | ||
| 640 | |||
| 641 | /// Gets the Subroutine object corresponding to the specified address. | 732 | /// Gets the Subroutine object corresponding to the specified address. |
| 642 | const Subroutine& GetSubroutine(u32 begin, u32 end) const { | 733 | const Subroutine& GetSubroutine(u32 begin, u32 end) const { |
| 643 | auto iter = subroutines.find(Subroutine{begin, end, suffix}); | 734 | const auto iter = subroutines.find(Subroutine{begin, end, suffix}); |
| 644 | ASSERT(iter != subroutines.end()); | 735 | ASSERT(iter != subroutines.end()); |
| 645 | return *iter; | 736 | return *iter; |
| 646 | } | 737 | } |
| @@ -656,8 +747,8 @@ private: | |||
| 656 | } | 747 | } |
| 657 | 748 | ||
| 658 | /// Generates code representing a texture sampler. | 749 | /// Generates code representing a texture sampler. |
| 659 | std::string GetSampler(const Sampler& sampler) { | 750 | std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) { |
| 660 | return regs.AccessSampler(sampler); | 751 | return regs.AccessSampler(sampler, type, is_array); |
| 661 | } | 752 | } |
| 662 | 753 | ||
| 663 | /** | 754 | /** |
| @@ -685,7 +776,7 @@ private: | |||
| 685 | // Can't assign to the constant predicate. | 776 | // Can't assign to the constant predicate. |
| 686 | ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); | 777 | ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); |
| 687 | 778 | ||
| 688 | std::string variable = 'p' + std::to_string(pred) + '_' + suffix; | 779 | const std::string variable = 'p' + std::to_string(pred) + '_' + suffix; |
| 689 | shader.AddLine(variable + " = " + value + ';'); | 780 | shader.AddLine(variable + " = " + value + ';'); |
| 690 | declr_predicates.insert(std::move(variable)); | 781 | declr_predicates.insert(std::move(variable)); |
| 691 | } | 782 | } |
| @@ -795,7 +886,7 @@ private: | |||
| 795 | */ | 886 | */ |
| 796 | bool IsSchedInstruction(u32 offset) const { | 887 | bool IsSchedInstruction(u32 offset) const { |
| 797 | // sched instructions appear once every 4 instructions. | 888 | // sched instructions appear once every 4 instructions. |
| 798 | static constexpr size_t SchedPeriod = 4; | 889 | static constexpr std::size_t SchedPeriod = 4; |
| 799 | u32 absolute_offset = offset - main_offset; | 890 | u32 absolute_offset = offset - main_offset; |
| 800 | 891 | ||
| 801 | return (absolute_offset % SchedPeriod) == 0; | 892 | return (absolute_offset % SchedPeriod) == 0; |
| @@ -863,7 +954,7 @@ private: | |||
| 863 | std::string result; | 954 | std::string result; |
| 864 | result += '('; | 955 | result += '('; |
| 865 | 956 | ||
| 866 | for (size_t i = 0; i < shift_amounts.size(); ++i) { | 957 | for (std::size_t i = 0; i < shift_amounts.size(); ++i) { |
| 867 | if (i) | 958 | if (i) |
| 868 | result += '|'; | 959 | result += '|'; |
| 869 | result += "(((" + imm_lut + " >> (((" + op_c + " >> " + shift_amounts[i] + | 960 | result += "(((" + imm_lut + " >> (((" + op_c + " >> " + shift_amounts[i] + |
| @@ -887,7 +978,7 @@ private: | |||
| 887 | // TEXS has two destination registers and a swizzle. The first two elements in the swizzle | 978 | // TEXS has two destination registers and a swizzle. The first two elements in the swizzle |
| 888 | // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 | 979 | // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 |
| 889 | 980 | ||
| 890 | size_t written_components = 0; | 981 | std::size_t written_components = 0; |
| 891 | for (u32 component = 0; component < 4; ++component) { | 982 | for (u32 component = 0; component < 4; ++component) { |
| 892 | if (!instr.texs.IsComponentEnabled(component)) { | 983 | if (!instr.texs.IsComponentEnabled(component)) { |
| 893 | continue; | 984 | continue; |
| @@ -941,10 +1032,8 @@ private: | |||
| 941 | /// Writes the output values from a fragment shader to the corresponding GLSL output variables. | 1032 | /// Writes the output values from a fragment shader to the corresponding GLSL output variables. |
| 942 | void EmitFragmentOutputsWrite() { | 1033 | void EmitFragmentOutputsWrite() { |
| 943 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); | 1034 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); |
| 944 | FragmentHeader header; | ||
| 945 | std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE); | ||
| 946 | 1035 | ||
| 947 | ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented"); | 1036 | ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); |
| 948 | 1037 | ||
| 949 | // Write the color outputs using the data in the shader registers, disabled | 1038 | // Write the color outputs using the data in the shader registers, disabled |
| 950 | // rendertargets/components are skipped in the register assignment. | 1039 | // rendertargets/components are skipped in the register assignment. |
| @@ -953,18 +1042,22 @@ private: | |||
| 953 | ++render_target) { | 1042 | ++render_target) { |
| 954 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | 1043 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. |
| 955 | for (u32 component = 0; component < 4; ++component) { | 1044 | for (u32 component = 0; component < 4; ++component) { |
| 956 | if (header.IsColorComponentOutputEnabled(render_target, component)) { | 1045 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { |
| 957 | shader.AddLine(fmt::format("color[{}][{}] = {};", render_target, component, | 1046 | shader.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, |
| 958 | regs.GetRegisterAsFloat(current_reg))); | 1047 | regs.GetRegisterAsFloat(current_reg))); |
| 959 | ++current_reg; | 1048 | ++current_reg; |
| 960 | } | 1049 | } |
| 961 | } | 1050 | } |
| 962 | } | 1051 | } |
| 963 | 1052 | ||
| 964 | if (header.writes_depth) { | 1053 | if (header.ps.omap.depth) { |
| 965 | // The depth output is always 2 registers after the last color output, and current_reg | 1054 | // The depth output is always 2 registers after the last color output, and current_reg |
| 966 | // already contains one past the last color register. | 1055 | // already contains one past the last color register. |
| 967 | shader.AddLine("gl_FragDepth = " + regs.GetRegisterAsFloat(current_reg + 1) + ';'); | 1056 | |
| 1057 | shader.AddLine( | ||
| 1058 | "gl_FragDepth = " + | ||
| 1059 | regs.GetRegisterAsFloat(static_cast<Tegra::Shader::Register>(current_reg) + 1) + | ||
| 1060 | ';'); | ||
| 968 | } | 1061 | } |
| 969 | } | 1062 | } |
| 970 | 1063 | ||
| @@ -1038,6 +1131,15 @@ private: | |||
| 1038 | case OpCode::Id::FMUL_R: | 1131 | case OpCode::Id::FMUL_R: |
| 1039 | case OpCode::Id::FMUL_IMM: { | 1132 | case OpCode::Id::FMUL_IMM: { |
| 1040 | // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. | 1133 | // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. |
| 1134 | ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented", | ||
| 1135 | instr.fmul.tab5cb8_2.Value()); | ||
| 1136 | ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented", | ||
| 1137 | instr.fmul.tab5c68_1.Value()); | ||
| 1138 | ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented", | ||
| 1139 | instr.fmul.tab5c68_0 | ||
| 1140 | .Value()); // SMO typical sends 1 here which seems to be the default | ||
| 1141 | ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); | ||
| 1142 | |||
| 1041 | op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); | 1143 | op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); |
| 1042 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, | 1144 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, |
| 1043 | instr.alu.saturate_d); | 1145 | instr.alu.saturate_d); |
| @@ -1357,7 +1459,7 @@ private: | |||
| 1357 | if (instr.alu_integer.negate_b) | 1459 | if (instr.alu_integer.negate_b) |
| 1358 | op_b = "-(" + op_b + ')'; | 1460 | op_b = "-(" + op_b + ')'; |
| 1359 | 1461 | ||
| 1360 | std::string shift = std::to_string(instr.alu_integer.shift_amount.Value()); | 1462 | const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value()); |
| 1361 | 1463 | ||
| 1362 | regs.SetRegisterToInteger(instr.gpr0, true, 0, | 1464 | regs.SetRegisterToInteger(instr.gpr0, true, 0, |
| 1363 | "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); | 1465 | "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); |
| @@ -1375,7 +1477,7 @@ private: | |||
| 1375 | case OpCode::Id::SEL_C: | 1477 | case OpCode::Id::SEL_C: |
| 1376 | case OpCode::Id::SEL_R: | 1478 | case OpCode::Id::SEL_R: |
| 1377 | case OpCode::Id::SEL_IMM: { | 1479 | case OpCode::Id::SEL_IMM: { |
| 1378 | std::string condition = | 1480 | const std::string condition = |
| 1379 | GetPredicateCondition(instr.sel.pred, instr.sel.neg_pred != 0); | 1481 | GetPredicateCondition(instr.sel.pred, instr.sel.neg_pred != 0); |
| 1380 | regs.SetRegisterToInteger(instr.gpr0, true, 0, | 1482 | regs.SetRegisterToInteger(instr.gpr0, true, 0, |
| 1381 | '(' + condition + ") ? " + op_a + " : " + op_b, 1, 1); | 1483 | '(' + condition + ") ? " + op_a + " : " + op_b, 1, 1); |
| @@ -1397,8 +1499,9 @@ private: | |||
| 1397 | case OpCode::Id::LOP3_C: | 1499 | case OpCode::Id::LOP3_C: |
| 1398 | case OpCode::Id::LOP3_R: | 1500 | case OpCode::Id::LOP3_R: |
| 1399 | case OpCode::Id::LOP3_IMM: { | 1501 | case OpCode::Id::LOP3_IMM: { |
| 1400 | std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); | 1502 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); |
| 1401 | std::string lut; | 1503 | std::string lut; |
| 1504 | |||
| 1402 | if (opcode->GetId() == OpCode::Id::LOP3_R) { | 1505 | if (opcode->GetId() == OpCode::Id::LOP3_R) { |
| 1403 | lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')'; | 1506 | lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')'; |
| 1404 | } else { | 1507 | } else { |
| @@ -1413,15 +1516,80 @@ private: | |||
| 1413 | case OpCode::Id::IMNMX_IMM: { | 1516 | case OpCode::Id::IMNMX_IMM: { |
| 1414 | ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, | 1517 | ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, |
| 1415 | "Unimplemented"); | 1518 | "Unimplemented"); |
| 1416 | std::string condition = | 1519 | const std::string condition = |
| 1417 | GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); | 1520 | GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); |
| 1418 | std::string parameters = op_a + ',' + op_b; | 1521 | const std::string parameters = op_a + ',' + op_b; |
| 1419 | regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0, | 1522 | regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0, |
| 1420 | '(' + condition + ") ? min(" + parameters + ") : max(" + | 1523 | '(' + condition + ") ? min(" + parameters + ") : max(" + |
| 1421 | parameters + ')', | 1524 | parameters + ')', |
| 1422 | 1, 1); | 1525 | 1, 1); |
| 1423 | break; | 1526 | break; |
| 1424 | } | 1527 | } |
| 1528 | case OpCode::Id::LEA_R2: | ||
| 1529 | case OpCode::Id::LEA_R1: | ||
| 1530 | case OpCode::Id::LEA_IMM: | ||
| 1531 | case OpCode::Id::LEA_RZ: | ||
| 1532 | case OpCode::Id::LEA_HI: { | ||
| 1533 | std::string op_c; | ||
| 1534 | |||
| 1535 | switch (opcode->GetId()) { | ||
| 1536 | case OpCode::Id::LEA_R2: { | ||
| 1537 | op_a = regs.GetRegisterAsInteger(instr.gpr20); | ||
| 1538 | op_b = regs.GetRegisterAsInteger(instr.gpr39); | ||
| 1539 | op_c = std::to_string(instr.lea.r2.entry_a); | ||
| 1540 | break; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | case OpCode::Id::LEA_R1: { | ||
| 1544 | const bool neg = instr.lea.r1.neg != 0; | ||
| 1545 | op_a = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 1546 | if (neg) | ||
| 1547 | op_a = "-(" + op_a + ')'; | ||
| 1548 | op_b = regs.GetRegisterAsInteger(instr.gpr20); | ||
| 1549 | op_c = std::to_string(instr.lea.r1.entry_a); | ||
| 1550 | break; | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | case OpCode::Id::LEA_IMM: { | ||
| 1554 | const bool neg = instr.lea.imm.neg != 0; | ||
| 1555 | op_b = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 1556 | if (neg) | ||
| 1557 | op_b = "-(" + op_b + ')'; | ||
| 1558 | op_a = std::to_string(instr.lea.imm.entry_a); | ||
| 1559 | op_c = std::to_string(instr.lea.imm.entry_b); | ||
| 1560 | break; | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | case OpCode::Id::LEA_RZ: { | ||
| 1564 | const bool neg = instr.lea.rz.neg != 0; | ||
| 1565 | op_b = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 1566 | if (neg) | ||
| 1567 | op_b = "-(" + op_b + ')'; | ||
| 1568 | op_a = regs.GetUniform(instr.lea.rz.cb_index, instr.lea.rz.cb_offset, | ||
| 1569 | GLSLRegister::Type::Integer); | ||
| 1570 | op_c = std::to_string(instr.lea.rz.entry_a); | ||
| 1571 | |||
| 1572 | break; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | case OpCode::Id::LEA_HI: | ||
| 1576 | default: { | ||
| 1577 | op_b = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 1578 | op_a = std::to_string(instr.lea.imm.entry_a); | ||
| 1579 | op_c = std::to_string(instr.lea.imm.entry_b); | ||
| 1580 | LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", opcode->GetName()); | ||
| 1581 | UNREACHABLE(); | ||
| 1582 | } | ||
| 1583 | } | ||
| 1584 | if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 1585 | LOG_ERROR(HW_GPU, "Unhandled LEA Predicate"); | ||
| 1586 | UNREACHABLE(); | ||
| 1587 | } | ||
| 1588 | const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; | ||
| 1589 | regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); | ||
| 1590 | |||
| 1591 | break; | ||
| 1592 | } | ||
| 1425 | default: { | 1593 | default: { |
| 1426 | LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", | 1594 | LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", |
| 1427 | opcode->GetName()); | 1595 | opcode->GetName()); |
| @@ -1432,10 +1600,16 @@ private: | |||
| 1432 | break; | 1600 | break; |
| 1433 | } | 1601 | } |
| 1434 | case OpCode::Type::Ffma: { | 1602 | case OpCode::Type::Ffma: { |
| 1435 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 1603 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 1436 | std::string op_b = instr.ffma.negate_b ? "-" : ""; | 1604 | std::string op_b = instr.ffma.negate_b ? "-" : ""; |
| 1437 | std::string op_c = instr.ffma.negate_c ? "-" : ""; | 1605 | std::string op_c = instr.ffma.negate_c ? "-" : ""; |
| 1438 | 1606 | ||
| 1607 | ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented"); | ||
| 1608 | ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented", | ||
| 1609 | instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO | ||
| 1610 | ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", | ||
| 1611 | instr.ffma.tab5980_1.Value()); | ||
| 1612 | |||
| 1439 | switch (opcode->GetId()) { | 1613 | switch (opcode->GetId()) { |
| 1440 | case OpCode::Id::FFMA_CR: { | 1614 | case OpCode::Id::FFMA_CR: { |
| 1441 | op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | 1615 | op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, |
| @@ -1486,7 +1660,8 @@ private: | |||
| 1486 | } | 1660 | } |
| 1487 | 1661 | ||
| 1488 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, | 1662 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, |
| 1489 | 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); | 1663 | 1, instr.alu.saturate_d, 0, instr.conversion.dest_size, |
| 1664 | instr.generates_cc.Value() != 0); | ||
| 1490 | break; | 1665 | break; |
| 1491 | } | 1666 | } |
| 1492 | case OpCode::Id::I2F_R: | 1667 | case OpCode::Id::I2F_R: |
| @@ -1616,9 +1791,34 @@ private: | |||
| 1616 | case OpCode::Type::Memory: { | 1791 | case OpCode::Type::Memory: { |
| 1617 | switch (opcode->GetId()) { | 1792 | switch (opcode->GetId()) { |
| 1618 | case OpCode::Id::LD_A: { | 1793 | case OpCode::Id::LD_A: { |
| 1619 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | 1794 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. |
| 1620 | regs.SetRegisterToInputAttibute(instr.gpr0, instr.attribute.fmt20.element, | 1795 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, |
| 1621 | instr.attribute.fmt20.index); | 1796 | "Indirect attribute loads are not supported"); |
| 1797 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, | ||
| 1798 | "Unaligned attribute loads are not supported"); | ||
| 1799 | |||
| 1800 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, | ||
| 1801 | Tegra::Shader::IpaSampleMode::Default}; | ||
| 1802 | |||
| 1803 | u64 next_element = instr.attribute.fmt20.element; | ||
| 1804 | u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); | ||
| 1805 | |||
| 1806 | const auto LoadNextElement = [&](u32 reg_offset) { | ||
| 1807 | regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, | ||
| 1808 | static_cast<Attribute::Index>(next_index), | ||
| 1809 | input_mode); | ||
| 1810 | |||
| 1811 | // Load the next attribute element into the following register. If the element | ||
| 1812 | // to load goes beyond the vec4 size, load the first element of the next | ||
| 1813 | // attribute. | ||
| 1814 | next_element = (next_element + 1) % 4; | ||
| 1815 | next_index = next_index + (next_element == 0 ? 1 : 0); | ||
| 1816 | }; | ||
| 1817 | |||
| 1818 | const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1; | ||
| 1819 | for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) { | ||
| 1820 | LoadNextElement(reg_offset); | ||
| 1821 | } | ||
| 1622 | break; | 1822 | break; |
| 1623 | } | 1823 | } |
| 1624 | case OpCode::Id::LD_C: { | 1824 | case OpCode::Id::LD_C: { |
| @@ -1632,7 +1832,7 @@ private: | |||
| 1632 | shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + | 1832 | shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + |
| 1633 | " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);"); | 1833 | " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);"); |
| 1634 | 1834 | ||
| 1635 | std::string op_a = | 1835 | const std::string op_a = |
| 1636 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, "index", | 1836 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, "index", |
| 1637 | GLSLRegister::Type::Float); | 1837 | GLSLRegister::Type::Float); |
| 1638 | 1838 | ||
| @@ -1642,7 +1842,7 @@ private: | |||
| 1642 | break; | 1842 | break; |
| 1643 | 1843 | ||
| 1644 | case Tegra::Shader::UniformType::Double: { | 1844 | case Tegra::Shader::UniformType::Double: { |
| 1645 | std::string op_b = | 1845 | const std::string op_b = |
| 1646 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, | 1846 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, |
| 1647 | "index", GLSLRegister::Type::Float); | 1847 | "index", GLSLRegister::Type::Float); |
| 1648 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); | 1848 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); |
| @@ -1660,25 +1860,111 @@ private: | |||
| 1660 | break; | 1860 | break; |
| 1661 | } | 1861 | } |
| 1662 | case OpCode::Id::ST_A: { | 1862 | case OpCode::Id::ST_A: { |
| 1663 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | 1863 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, |
| 1664 | regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index, | 1864 | "Indirect attribute loads are not supported"); |
| 1665 | instr.attribute.fmt20.element, instr.gpr0); | 1865 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, |
| 1866 | "Unaligned attribute loads are not supported"); | ||
| 1867 | |||
| 1868 | u64 next_element = instr.attribute.fmt20.element; | ||
| 1869 | u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); | ||
| 1870 | |||
| 1871 | const auto StoreNextElement = [&](u32 reg_offset) { | ||
| 1872 | regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), | ||
| 1873 | next_element, | ||
| 1874 | instr.gpr0.Value() + reg_offset); | ||
| 1875 | |||
| 1876 | // Load the next attribute element into the following register. If the element | ||
| 1877 | // to load goes beyond the vec4 size, load the first element of the next | ||
| 1878 | // attribute. | ||
| 1879 | next_element = (next_element + 1) % 4; | ||
| 1880 | next_index = next_index + (next_element == 0 ? 1 : 0); | ||
| 1881 | }; | ||
| 1882 | |||
| 1883 | const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1; | ||
| 1884 | for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) { | ||
| 1885 | StoreNextElement(reg_offset); | ||
| 1886 | } | ||
| 1887 | |||
| 1666 | break; | 1888 | break; |
| 1667 | } | 1889 | } |
| 1668 | case OpCode::Id::TEX: { | 1890 | case OpCode::Id::TEX: { |
| 1669 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 1891 | ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented"); |
| 1670 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 1892 | Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; |
| 1671 | const std::string sampler = GetSampler(instr.sampler); | 1893 | std::string coord; |
| 1672 | const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | 1894 | |
| 1895 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 1896 | "NODEP is not implemented"); | ||
| 1897 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 1898 | "AOFFI is not implemented"); | ||
| 1899 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 1900 | "DC is not implemented"); | ||
| 1901 | |||
| 1902 | switch (texture_type) { | ||
| 1903 | case Tegra::Shader::TextureType::Texture1D: { | ||
| 1904 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 1905 | coord = "float coords = " + x + ';'; | ||
| 1906 | break; | ||
| 1907 | } | ||
| 1908 | case Tegra::Shader::TextureType::Texture2D: { | ||
| 1909 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 1910 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 1911 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||
| 1912 | break; | ||
| 1913 | } | ||
| 1914 | default: | ||
| 1915 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||
| 1916 | static_cast<u32>(texture_type)); | ||
| 1917 | UNREACHABLE(); | ||
| 1918 | |||
| 1919 | // Fallback to interpreting as a 2D texture for now | ||
| 1920 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 1921 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 1922 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||
| 1923 | texture_type = Tegra::Shader::TextureType::Texture2D; | ||
| 1924 | } | ||
| 1925 | // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias | ||
| 1926 | // or lod. | ||
| 1927 | const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 1928 | |||
| 1929 | const std::string sampler = GetSampler(instr.sampler, texture_type, false); | ||
| 1673 | // Add an extra scope and declare the texture coords inside to prevent | 1930 | // Add an extra scope and declare the texture coords inside to prevent |
| 1674 | // overwriting them in case they are used as outputs of the texs instruction. | 1931 | // overwriting them in case they are used as outputs of the texs instruction. |
| 1932 | |||
| 1675 | shader.AddLine("{"); | 1933 | shader.AddLine("{"); |
| 1676 | ++shader.scope; | 1934 | ++shader.scope; |
| 1677 | shader.AddLine(coord); | 1935 | shader.AddLine(coord); |
| 1678 | const std::string texture = "texture(" + sampler + ", coords)"; | 1936 | std::string texture; |
| 1679 | 1937 | ||
| 1680 | size_t dest_elem{}; | 1938 | switch (instr.tex.process_mode) { |
| 1681 | for (size_t elem = 0; elem < 4; ++elem) { | 1939 | case Tegra::Shader::TextureProcessMode::None: { |
| 1940 | texture = "texture(" + sampler + ", coords)"; | ||
| 1941 | break; | ||
| 1942 | } | ||
| 1943 | case Tegra::Shader::TextureProcessMode::LZ: { | ||
| 1944 | texture = "textureLod(" + sampler + ", coords, 0.0)"; | ||
| 1945 | break; | ||
| 1946 | } | ||
| 1947 | case Tegra::Shader::TextureProcessMode::LB: | ||
| 1948 | case Tegra::Shader::TextureProcessMode::LBA: { | ||
| 1949 | // TODO: Figure if A suffix changes the equation at all. | ||
| 1950 | texture = "texture(" + sampler + ", coords, " + op_c + ')'; | ||
| 1951 | break; | ||
| 1952 | } | ||
| 1953 | case Tegra::Shader::TextureProcessMode::LL: | ||
| 1954 | case Tegra::Shader::TextureProcessMode::LLA: { | ||
| 1955 | // TODO: Figure if A suffix changes the equation at all. | ||
| 1956 | texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; | ||
| 1957 | break; | ||
| 1958 | } | ||
| 1959 | default: { | ||
| 1960 | texture = "texture(" + sampler + ", coords)"; | ||
| 1961 | LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", | ||
| 1962 | static_cast<u32>(instr.tex.process_mode.Value())); | ||
| 1963 | UNREACHABLE(); | ||
| 1964 | } | ||
| 1965 | } | ||
| 1966 | std::size_t dest_elem{}; | ||
| 1967 | for (std::size_t elem = 0; elem < 4; ++elem) { | ||
| 1682 | if (!instr.tex.IsComponentEnabled(elem)) { | 1968 | if (!instr.tex.IsComponentEnabled(elem)) { |
| 1683 | // Skip disabled components | 1969 | // Skip disabled components |
| 1684 | continue; | 1970 | continue; |
| @@ -1691,20 +1977,77 @@ private: | |||
| 1691 | break; | 1977 | break; |
| 1692 | } | 1978 | } |
| 1693 | case OpCode::Id::TEXS: { | 1979 | case OpCode::Id::TEXS: { |
| 1694 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 1980 | std::string coord; |
| 1695 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | 1981 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; |
| 1696 | const std::string sampler = GetSampler(instr.sampler); | 1982 | bool is_array{instr.texs.IsArrayTexture()}; |
| 1697 | const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | 1983 | |
| 1984 | ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 1985 | "NODEP is not implemented"); | ||
| 1986 | ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 1987 | "DC is not implemented"); | ||
| 1988 | |||
| 1989 | switch (texture_type) { | ||
| 1990 | case Tegra::Shader::TextureType::Texture2D: { | ||
| 1991 | if (is_array) { | ||
| 1992 | const std::string index = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 1993 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 1994 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 1995 | coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; | ||
| 1996 | } else { | ||
| 1997 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 1998 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 1999 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||
| 2000 | } | ||
| 2001 | break; | ||
| 2002 | } | ||
| 2003 | default: | ||
| 2004 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||
| 2005 | static_cast<u32>(texture_type)); | ||
| 2006 | UNREACHABLE(); | ||
| 1698 | 2007 | ||
| 2008 | // Fallback to interpreting as a 2D texture for now | ||
| 2009 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2010 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2011 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||
| 2012 | texture_type = Tegra::Shader::TextureType::Texture2D; | ||
| 2013 | is_array = false; | ||
| 2014 | } | ||
| 2015 | const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); | ||
| 1699 | const std::string texture = "texture(" + sampler + ", coords)"; | 2016 | const std::string texture = "texture(" + sampler + ", coords)"; |
| 1700 | WriteTexsInstruction(instr, coord, texture); | 2017 | WriteTexsInstruction(instr, coord, texture); |
| 1701 | break; | 2018 | break; |
| 1702 | } | 2019 | } |
| 1703 | case OpCode::Id::TLDS: { | 2020 | case OpCode::Id::TLDS: { |
| 1704 | const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); | 2021 | ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D); |
| 1705 | const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20); | 2022 | ASSERT(instr.tlds.IsArrayTexture() == false); |
| 1706 | const std::string sampler = GetSampler(instr.sampler); | 2023 | std::string coord; |
| 1707 | const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");"; | 2024 | |
| 2025 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2026 | "NODEP is not implemented"); | ||
| 2027 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2028 | "AOFFI is not implemented"); | ||
| 2029 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), | ||
| 2030 | "MZ is not implemented"); | ||
| 2031 | |||
| 2032 | switch (instr.tlds.GetTextureType()) { | ||
| 2033 | case Tegra::Shader::TextureType::Texture2D: { | ||
| 2034 | if (instr.tlds.IsArrayTexture()) { | ||
| 2035 | LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); | ||
| 2036 | UNREACHABLE(); | ||
| 2037 | } else { | ||
| 2038 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 2039 | const std::string y = regs.GetRegisterAsInteger(instr.gpr20); | ||
| 2040 | coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; | ||
| 2041 | } | ||
| 2042 | break; | ||
| 2043 | } | ||
| 2044 | default: | ||
| 2045 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||
| 2046 | static_cast<u32>(instr.tlds.GetTextureType())); | ||
| 2047 | UNREACHABLE(); | ||
| 2048 | } | ||
| 2049 | const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(), | ||
| 2050 | instr.tlds.IsArrayTexture()); | ||
| 1708 | const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; | 2051 | const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; |
| 1709 | WriteTexsInstruction(instr, coord, texture); | 2052 | WriteTexsInstruction(instr, coord, texture); |
| 1710 | break; | 2053 | break; |
| @@ -1712,12 +2055,23 @@ private: | |||
| 1712 | case OpCode::Id::TLD4: { | 2055 | case OpCode::Id::TLD4: { |
| 1713 | ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); | 2056 | ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); |
| 1714 | ASSERT(instr.tld4.array == 0); | 2057 | ASSERT(instr.tld4.array == 0); |
| 1715 | std::string coord{}; | 2058 | std::string coord; |
| 2059 | |||
| 2060 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2061 | "NODEP is not implemented"); | ||
| 2062 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2063 | "AOFFI is not implemented"); | ||
| 2064 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 2065 | "DC is not implemented"); | ||
| 2066 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | ||
| 2067 | "NDV is not implemented"); | ||
| 2068 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), | ||
| 2069 | "PTP is not implemented"); | ||
| 1716 | 2070 | ||
| 1717 | switch (instr.tld4.texture_type) { | 2071 | switch (instr.tld4.texture_type) { |
| 1718 | case Tegra::Shader::TextureType::Texture2D: { | 2072 | case Tegra::Shader::TextureType::Texture2D: { |
| 1719 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2073 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 1720 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2074 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 1721 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2075 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; |
| 1722 | break; | 2076 | break; |
| 1723 | } | 2077 | } |
| @@ -1727,7 +2081,8 @@ private: | |||
| 1727 | UNREACHABLE(); | 2081 | UNREACHABLE(); |
| 1728 | } | 2082 | } |
| 1729 | 2083 | ||
| 1730 | const std::string sampler = GetSampler(instr.sampler); | 2084 | const std::string sampler = |
| 2085 | GetSampler(instr.sampler, instr.tld4.texture_type, false); | ||
| 1731 | // Add an extra scope and declare the texture coords inside to prevent | 2086 | // Add an extra scope and declare the texture coords inside to prevent |
| 1732 | // overwriting them in case they are used as outputs of the texs instruction. | 2087 | // overwriting them in case they are used as outputs of the texs instruction. |
| 1733 | shader.AddLine("{"); | 2088 | shader.AddLine("{"); |
| @@ -1736,8 +2091,8 @@ private: | |||
| 1736 | const std::string texture = "textureGather(" + sampler + ", coords, " + | 2091 | const std::string texture = "textureGather(" + sampler + ", coords, " + |
| 1737 | std::to_string(instr.tld4.component) + ')'; | 2092 | std::to_string(instr.tld4.component) + ')'; |
| 1738 | 2093 | ||
| 1739 | size_t dest_elem{}; | 2094 | std::size_t dest_elem{}; |
| 1740 | for (size_t elem = 0; elem < 4; ++elem) { | 2095 | for (std::size_t elem = 0; elem < 4; ++elem) { |
| 1741 | if (!instr.tex.IsComponentEnabled(elem)) { | 2096 | if (!instr.tex.IsComponentEnabled(elem)) { |
| 1742 | // Skip disabled components | 2097 | // Skip disabled components |
| 1743 | continue; | 2098 | continue; |
| @@ -1750,16 +2105,100 @@ private: | |||
| 1750 | break; | 2105 | break; |
| 1751 | } | 2106 | } |
| 1752 | case OpCode::Id::TLD4S: { | 2107 | case OpCode::Id::TLD4S: { |
| 2108 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2109 | "NODEP is not implemented"); | ||
| 2110 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2111 | "AOFFI is not implemented"); | ||
| 2112 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 2113 | "DC is not implemented"); | ||
| 2114 | |||
| 1753 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 2115 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 1754 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | 2116 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); |
| 1755 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. | 2117 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. |
| 1756 | const std::string sampler = GetSampler(instr.sampler); | 2118 | const std::string sampler = |
| 2119 | GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); | ||
| 1757 | const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | 2120 | const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; |
| 1758 | const std::string texture = "textureGather(" + sampler + ", coords, " + | 2121 | const std::string texture = "textureGather(" + sampler + ", coords, " + |
| 1759 | std::to_string(instr.tld4s.component) + ')'; | 2122 | std::to_string(instr.tld4s.component) + ')'; |
| 1760 | WriteTexsInstruction(instr, coord, texture); | 2123 | WriteTexsInstruction(instr, coord, texture); |
| 1761 | break; | 2124 | break; |
| 1762 | } | 2125 | } |
| 2126 | case OpCode::Id::TXQ: { | ||
| 2127 | ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2128 | "NODEP is not implemented"); | ||
| 2129 | |||
| 2130 | // TODO: the new commits on the texture refactor, change the way samplers work. | ||
| 2131 | // Sadly, not all texture instructions specify the type of texture their sampler | ||
| 2132 | // uses. This must be fixed at a later instance. | ||
| 2133 | const std::string sampler = | ||
| 2134 | GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); | ||
| 2135 | switch (instr.txq.query_type) { | ||
| 2136 | case Tegra::Shader::TextureQueryType::Dimension: { | ||
| 2137 | const std::string texture = "textureQueryLevels(" + sampler + ')'; | ||
| 2138 | regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); | ||
| 2139 | break; | ||
| 2140 | } | ||
| 2141 | default: { | ||
| 2142 | LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}", | ||
| 2143 | static_cast<u32>(instr.txq.query_type.Value())); | ||
| 2144 | UNREACHABLE(); | ||
| 2145 | } | ||
| 2146 | } | ||
| 2147 | break; | ||
| 2148 | } | ||
| 2149 | case OpCode::Id::TMML: { | ||
| 2150 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2151 | "NODEP is not implemented"); | ||
| 2152 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | ||
| 2153 | "NDV is not implemented"); | ||
| 2154 | |||
| 2155 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2156 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2157 | const bool is_array = instr.tmml.array != 0; | ||
| 2158 | auto texture_type = instr.tmml.texture_type.Value(); | ||
| 2159 | const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); | ||
| 2160 | |||
| 2161 | // TODO: add coordinates for different samplers once other texture types are | ||
| 2162 | // implemented. | ||
| 2163 | std::string coord; | ||
| 2164 | switch (texture_type) { | ||
| 2165 | case Tegra::Shader::TextureType::Texture1D: { | ||
| 2166 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2167 | coord = "float coords = " + x + ';'; | ||
| 2168 | break; | ||
| 2169 | } | ||
| 2170 | case Tegra::Shader::TextureType::Texture2D: { | ||
| 2171 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2172 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2173 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||
| 2174 | break; | ||
| 2175 | } | ||
| 2176 | default: | ||
| 2177 | LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||
| 2178 | static_cast<u32>(texture_type)); | ||
| 2179 | UNREACHABLE(); | ||
| 2180 | |||
| 2181 | // Fallback to interpreting as a 2D texture for now | ||
| 2182 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2183 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2184 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||
| 2185 | texture_type = Tegra::Shader::TextureType::Texture2D; | ||
| 2186 | } | ||
| 2187 | // Add an extra scope and declare the texture coords inside to prevent | ||
| 2188 | // overwriting them in case they are used as outputs of the texs instruction. | ||
| 2189 | shader.AddLine('{'); | ||
| 2190 | ++shader.scope; | ||
| 2191 | shader.AddLine(coord); | ||
| 2192 | const std::string texture = "textureQueryLod(" + sampler + ", coords)"; | ||
| 2193 | const std::string tmp = "vec2 tmp = " + texture + "*vec2(256.0, 256.0);"; | ||
| 2194 | shader.AddLine(tmp); | ||
| 2195 | |||
| 2196 | regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(tmp.y)", 1, 1); | ||
| 2197 | regs.SetRegisterToInteger(instr.gpr0.Value() + 1, false, 0, "uint(tmp.x)", 1, 1); | ||
| 2198 | --shader.scope; | ||
| 2199 | shader.AddLine('}'); | ||
| 2200 | break; | ||
| 2201 | } | ||
| 1763 | default: { | 2202 | default: { |
| 1764 | LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); | 2203 | LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); |
| 1765 | UNREACHABLE(); | 2204 | UNREACHABLE(); |
| @@ -1799,12 +2238,12 @@ private: | |||
| 1799 | // We can't use the constant predicate as destination. | 2238 | // We can't use the constant predicate as destination. |
| 1800 | ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); | 2239 | ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); |
| 1801 | 2240 | ||
| 1802 | std::string second_pred = | 2241 | const std::string second_pred = |
| 1803 | GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0); | 2242 | GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0); |
| 1804 | 2243 | ||
| 1805 | std::string combiner = GetPredicateCombiner(instr.fsetp.op); | 2244 | const std::string combiner = GetPredicateCombiner(instr.fsetp.op); |
| 1806 | 2245 | ||
| 1807 | std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b); | 2246 | const std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b); |
| 1808 | // Set the primary predicate to the result of Predicate OP SecondPredicate | 2247 | // Set the primary predicate to the result of Predicate OP SecondPredicate |
| 1809 | SetPredicate(instr.fsetp.pred3, | 2248 | SetPredicate(instr.fsetp.pred3, |
| 1810 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); | 2249 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); |
| @@ -1818,7 +2257,8 @@ private: | |||
| 1818 | break; | 2257 | break; |
| 1819 | } | 2258 | } |
| 1820 | case OpCode::Type::IntegerSetPredicate: { | 2259 | case OpCode::Type::IntegerSetPredicate: { |
| 1821 | std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed); | 2260 | const std::string op_a = |
| 2261 | regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed); | ||
| 1822 | std::string op_b; | 2262 | std::string op_b; |
| 1823 | 2263 | ||
| 1824 | if (instr.is_b_imm) { | 2264 | if (instr.is_b_imm) { |
| @@ -1835,12 +2275,12 @@ private: | |||
| 1835 | // We can't use the constant predicate as destination. | 2275 | // We can't use the constant predicate as destination. |
| 1836 | ASSERT(instr.isetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); | 2276 | ASSERT(instr.isetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); |
| 1837 | 2277 | ||
| 1838 | std::string second_pred = | 2278 | const std::string second_pred = |
| 1839 | GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0); | 2279 | GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0); |
| 1840 | 2280 | ||
| 1841 | std::string combiner = GetPredicateCombiner(instr.isetp.op); | 2281 | const std::string combiner = GetPredicateCombiner(instr.isetp.op); |
| 1842 | 2282 | ||
| 1843 | std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b); | 2283 | const std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b); |
| 1844 | // Set the primary predicate to the result of Predicate OP SecondPredicate | 2284 | // Set the primary predicate to the result of Predicate OP SecondPredicate |
| 1845 | SetPredicate(instr.isetp.pred3, | 2285 | SetPredicate(instr.isetp.pred3, |
| 1846 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); | 2286 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); |
| @@ -1853,32 +2293,80 @@ private: | |||
| 1853 | } | 2293 | } |
| 1854 | break; | 2294 | break; |
| 1855 | } | 2295 | } |
| 2296 | case OpCode::Type::PredicateSetRegister: { | ||
| 2297 | const std::string op_a = | ||
| 2298 | GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); | ||
| 2299 | const std::string op_b = | ||
| 2300 | GetPredicateCondition(instr.pset.pred29, instr.pset.neg_pred29 != 0); | ||
| 2301 | |||
| 2302 | const std::string second_pred = | ||
| 2303 | GetPredicateCondition(instr.pset.pred39, instr.pset.neg_pred39 != 0); | ||
| 2304 | |||
| 2305 | const std::string combiner = GetPredicateCombiner(instr.pset.op); | ||
| 2306 | |||
| 2307 | const std::string predicate = | ||
| 2308 | '(' + op_a + ") " + GetPredicateCombiner(instr.pset.cond) + " (" + op_b + ')'; | ||
| 2309 | const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')'; | ||
| 2310 | if (instr.pset.bf == 0) { | ||
| 2311 | const std::string value = '(' + result + ") ? 0xFFFFFFFF : 0"; | ||
| 2312 | regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1); | ||
| 2313 | } else { | ||
| 2314 | const std::string value = '(' + result + ") ? 1.0 : 0.0"; | ||
| 2315 | regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); | ||
| 2316 | } | ||
| 2317 | |||
| 2318 | break; | ||
| 2319 | } | ||
| 1856 | case OpCode::Type::PredicateSetPredicate: { | 2320 | case OpCode::Type::PredicateSetPredicate: { |
| 1857 | std::string op_a = | 2321 | switch (opcode->GetId()) { |
| 1858 | GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); | 2322 | case OpCode::Id::PSETP: { |
| 1859 | std::string op_b = | 2323 | const std::string op_a = |
| 1860 | GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); | 2324 | GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); |
| 2325 | const std::string op_b = | ||
| 2326 | GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); | ||
| 1861 | 2327 | ||
| 1862 | // We can't use the constant predicate as destination. | 2328 | // We can't use the constant predicate as destination. |
| 1863 | ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); | 2329 | ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); |
| 1864 | 2330 | ||
| 1865 | std::string second_pred = | 2331 | const std::string second_pred = |
| 1866 | GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); | 2332 | GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); |
| 1867 | 2333 | ||
| 1868 | std::string combiner = GetPredicateCombiner(instr.psetp.op); | 2334 | const std::string combiner = GetPredicateCombiner(instr.psetp.op); |
| 1869 | 2335 | ||
| 1870 | std::string predicate = | 2336 | const std::string predicate = |
| 1871 | '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; | 2337 | '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; |
| 1872 | 2338 | ||
| 1873 | // Set the primary predicate to the result of Predicate OP SecondPredicate | 2339 | // Set the primary predicate to the result of Predicate OP SecondPredicate |
| 1874 | SetPredicate(instr.psetp.pred3, | 2340 | SetPredicate(instr.psetp.pred3, |
| 1875 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); | 2341 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); |
| 1876 | 2342 | ||
| 1877 | if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | 2343 | if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { |
| 1878 | // Set the secondary predicate to the result of !Predicate OP SecondPredicate, | 2344 | // Set the secondary predicate to the result of !Predicate OP SecondPredicate, |
| 1879 | // if enabled | 2345 | // if enabled |
| 1880 | SetPredicate(instr.psetp.pred0, | 2346 | SetPredicate(instr.psetp.pred0, |
| 1881 | "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); | 2347 | "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); |
| 2348 | } | ||
| 2349 | break; | ||
| 2350 | } | ||
| 2351 | case OpCode::Id::CSETP: { | ||
| 2352 | const std::string pred = | ||
| 2353 | GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); | ||
| 2354 | const std::string combiner = GetPredicateCombiner(instr.csetp.op); | ||
| 2355 | const std::string controlCode = regs.GetControlCode(instr.csetp.cc); | ||
| 2356 | if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 2357 | SetPredicate(instr.csetp.pred3, | ||
| 2358 | '(' + controlCode + ") " + combiner + " (" + pred + ')'); | ||
| 2359 | } | ||
| 2360 | if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 2361 | SetPredicate(instr.csetp.pred0, | ||
| 2362 | "!(" + controlCode + ") " + combiner + " (" + pred + ')'); | ||
| 2363 | } | ||
| 2364 | break; | ||
| 2365 | } | ||
| 2366 | default: { | ||
| 2367 | LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName()); | ||
| 2368 | UNREACHABLE(); | ||
| 2369 | } | ||
| 1882 | } | 2370 | } |
| 1883 | break; | 2371 | break; |
| 1884 | } | 2372 | } |
| @@ -1893,7 +2381,7 @@ private: | |||
| 1893 | std::string op_b = instr.fset.neg_b ? "-" : ""; | 2381 | std::string op_b = instr.fset.neg_b ? "-" : ""; |
| 1894 | 2382 | ||
| 1895 | if (instr.is_b_imm) { | 2383 | if (instr.is_b_imm) { |
| 1896 | std::string imm = GetImmediate19(instr); | 2384 | const std::string imm = GetImmediate19(instr); |
| 1897 | if (instr.fset.neg_imm) | 2385 | if (instr.fset.neg_imm) |
| 1898 | op_b += "(-" + imm + ')'; | 2386 | op_b += "(-" + imm + ')'; |
| 1899 | else | 2387 | else |
| @@ -1913,13 +2401,14 @@ private: | |||
| 1913 | 2401 | ||
| 1914 | // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the | 2402 | // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the |
| 1915 | // condition is true, and to 0 otherwise. | 2403 | // condition is true, and to 0 otherwise. |
| 1916 | std::string second_pred = | 2404 | const std::string second_pred = |
| 1917 | GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0); | 2405 | GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0); |
| 1918 | 2406 | ||
| 1919 | std::string combiner = GetPredicateCombiner(instr.fset.op); | 2407 | const std::string combiner = GetPredicateCombiner(instr.fset.op); |
| 1920 | 2408 | ||
| 1921 | std::string predicate = "((" + GetPredicateComparison(instr.fset.cond, op_a, op_b) + | 2409 | const std::string predicate = "((" + |
| 1922 | ") " + combiner + " (" + second_pred + "))"; | 2410 | GetPredicateComparison(instr.fset.cond, op_a, op_b) + |
| 2411 | ") " + combiner + " (" + second_pred + "))"; | ||
| 1923 | 2412 | ||
| 1924 | if (instr.fset.bf) { | 2413 | if (instr.fset.bf) { |
| 1925 | regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); | 2414 | regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); |
| @@ -1930,7 +2419,7 @@ private: | |||
| 1930 | break; | 2419 | break; |
| 1931 | } | 2420 | } |
| 1932 | case OpCode::Type::IntegerSet: { | 2421 | case OpCode::Type::IntegerSet: { |
| 1933 | std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed); | 2422 | const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed); |
| 1934 | 2423 | ||
| 1935 | std::string op_b; | 2424 | std::string op_b; |
| 1936 | 2425 | ||
| @@ -1947,13 +2436,14 @@ private: | |||
| 1947 | 2436 | ||
| 1948 | // The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the | 2437 | // The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the |
| 1949 | // condition is true, and to 0 otherwise. | 2438 | // condition is true, and to 0 otherwise. |
| 1950 | std::string second_pred = | 2439 | const std::string second_pred = |
| 1951 | GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0); | 2440 | GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0); |
| 1952 | 2441 | ||
| 1953 | std::string combiner = GetPredicateCombiner(instr.iset.op); | 2442 | const std::string combiner = GetPredicateCombiner(instr.iset.op); |
| 1954 | 2443 | ||
| 1955 | std::string predicate = "((" + GetPredicateComparison(instr.iset.cond, op_a, op_b) + | 2444 | const std::string predicate = "((" + |
| 1956 | ") " + combiner + " (" + second_pred + "))"; | 2445 | GetPredicateComparison(instr.iset.cond, op_a, op_b) + |
| 2446 | ") " + combiner + " (" + second_pred + "))"; | ||
| 1957 | 2447 | ||
| 1958 | if (instr.iset.bf) { | 2448 | if (instr.iset.bf) { |
| 1959 | regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); | 2449 | regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); |
| @@ -2103,45 +2593,22 @@ private: | |||
| 2103 | case OpCode::Id::BRA: { | 2593 | case OpCode::Id::BRA: { |
| 2104 | ASSERT_MSG(instr.bra.constant_buffer == 0, | 2594 | ASSERT_MSG(instr.bra.constant_buffer == 0, |
| 2105 | "BRA with constant buffers are not implemented"); | 2595 | "BRA with constant buffers are not implemented"); |
| 2106 | u32 target = offset + instr.bra.GetBranchTarget(); | 2596 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 2107 | shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); | 2597 | shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); |
| 2108 | break; | 2598 | break; |
| 2109 | } | 2599 | } |
| 2110 | case OpCode::Id::IPA: { | 2600 | case OpCode::Id::IPA: { |
| 2111 | const auto& attribute = instr.attribute.fmt28; | 2601 | const auto& attribute = instr.attribute.fmt28; |
| 2112 | const auto& reg = instr.gpr0; | 2602 | const auto& reg = instr.gpr0; |
| 2113 | switch (instr.ipa.mode) { | ||
| 2114 | case Tegra::Shader::IpaMode::Pass: | ||
| 2115 | if (stage == Maxwell3D::Regs::ShaderStage::Fragment && | ||
| 2116 | attribute.index == Attribute::Index::Position) { | ||
| 2117 | switch (attribute.element) { | ||
| 2118 | case 0: | ||
| 2119 | shader.AddLine(regs.GetRegisterAsFloat(reg) + " = gl_FragCoord.x;"); | ||
| 2120 | break; | ||
| 2121 | case 1: | ||
| 2122 | shader.AddLine(regs.GetRegisterAsFloat(reg) + " = gl_FragCoord.y;"); | ||
| 2123 | break; | ||
| 2124 | case 2: | ||
| 2125 | shader.AddLine(regs.GetRegisterAsFloat(reg) + " = gl_FragCoord.z;"); | ||
| 2126 | break; | ||
| 2127 | case 3: | ||
| 2128 | shader.AddLine(regs.GetRegisterAsFloat(reg) + " = 1.0;"); | ||
| 2129 | break; | ||
| 2130 | } | ||
| 2131 | } else { | ||
| 2132 | regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index); | ||
| 2133 | } | ||
| 2134 | break; | ||
| 2135 | case Tegra::Shader::IpaMode::None: | ||
| 2136 | regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index); | ||
| 2137 | break; | ||
| 2138 | default: | ||
| 2139 | LOG_CRITICAL(HW_GPU, "Unhandled IPA mode: {}", | ||
| 2140 | static_cast<u32>(instr.ipa.mode.Value())); | ||
| 2141 | UNREACHABLE(); | ||
| 2142 | regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index); | ||
| 2143 | } | ||
| 2144 | 2603 | ||
| 2604 | Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), | ||
| 2605 | instr.ipa.sample_mode.Value()}; | ||
| 2606 | regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index, | ||
| 2607 | input_mode); | ||
| 2608 | |||
| 2609 | if (instr.ipa.saturate) { | ||
| 2610 | regs.SetRegisterToFloat(reg, 0, regs.GetRegisterAsFloat(reg), 1, 1, true); | ||
| 2611 | } | ||
| 2145 | break; | 2612 | break; |
| 2146 | } | 2613 | } |
| 2147 | case OpCode::Id::SSY: { | 2614 | case OpCode::Id::SSY: { |
| @@ -2150,7 +2617,7 @@ private: | |||
| 2150 | // has a similar structure to the BRA opcode. | 2617 | // has a similar structure to the BRA opcode. |
| 2151 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); | 2618 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); |
| 2152 | 2619 | ||
| 2153 | u32 target = offset + instr.bra.GetBranchTarget(); | 2620 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 2154 | EmitPushToSSYStack(target); | 2621 | EmitPushToSSYStack(target); |
| 2155 | break; | 2622 | break; |
| 2156 | } | 2623 | } |
| @@ -2244,10 +2711,10 @@ private: | |||
| 2244 | shader.AddLine("case " + std::to_string(label) + "u: {"); | 2711 | shader.AddLine("case " + std::to_string(label) + "u: {"); |
| 2245 | ++shader.scope; | 2712 | ++shader.scope; |
| 2246 | 2713 | ||
| 2247 | auto next_it = labels.lower_bound(label + 1); | 2714 | const auto next_it = labels.lower_bound(label + 1); |
| 2248 | u32 next_label = next_it == labels.end() ? subroutine.end : *next_it; | 2715 | const u32 next_label = next_it == labels.end() ? subroutine.end : *next_it; |
| 2249 | 2716 | ||
| 2250 | u32 compile_end = CompileRange(label, next_label); | 2717 | const u32 compile_end = CompileRange(label, next_label); |
| 2251 | if (compile_end > next_label && compile_end != PROGRAM_END) { | 2718 | if (compile_end > next_label && compile_end != PROGRAM_END) { |
| 2252 | // This happens only when there is a label inside a IF/LOOP block | 2719 | // This happens only when there is a label inside a IF/LOOP block |
| 2253 | shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }"); | 2720 | shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }"); |
| @@ -2289,6 +2756,7 @@ private: | |||
| 2289 | private: | 2756 | private: |
| 2290 | const std::set<Subroutine>& subroutines; | 2757 | const std::set<Subroutine>& subroutines; |
| 2291 | const ProgramCode& program_code; | 2758 | const ProgramCode& program_code; |
| 2759 | Tegra::Shader::Header header; | ||
| 2292 | const u32 main_offset; | 2760 | const u32 main_offset; |
| 2293 | Maxwell3D::Regs::ShaderStage stage; | 2761 | Maxwell3D::Regs::ShaderStage stage; |
| 2294 | const std::string& suffix; | 2762 | const std::string& suffix; |
| @@ -2310,7 +2778,8 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, | |||
| 2310 | Maxwell3D::Regs::ShaderStage stage, | 2778 | Maxwell3D::Regs::ShaderStage stage, |
| 2311 | const std::string& suffix) { | 2779 | const std::string& suffix) { |
| 2312 | try { | 2780 | try { |
| 2313 | auto subroutines = ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); | 2781 | const auto subroutines = |
| 2782 | ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); | ||
| 2314 | GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); | 2783 | GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); |
| 2315 | return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; | 2784 | return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; |
| 2316 | } catch (const DecompileFail& exception) { | 2785 | } catch (const DecompileFail& exception) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 6ca05945e..b0466c18f 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -42,6 +42,7 @@ layout (std140) uniform vs_config { | |||
| 42 | }; | 42 | }; |
| 43 | 43 | ||
| 44 | void main() { | 44 | void main() { |
| 45 | position = vec4(0.0, 0.0, 0.0, 0.0); | ||
| 45 | exec_vertex(); | 46 | exec_vertex(); |
| 46 | )"; | 47 | )"; |
| 47 | 48 | ||
| @@ -87,7 +88,14 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { | |||
| 87 | .get_value_or({}); | 88 | .get_value_or({}); |
| 88 | out += R"( | 89 | out += R"( |
| 89 | in vec4 position; | 90 | in vec4 position; |
| 90 | layout(location = 0) out vec4 color[8]; | 91 | layout(location = 0) out vec4 FragColor0; |
| 92 | layout(location = 1) out vec4 FragColor1; | ||
| 93 | layout(location = 2) out vec4 FragColor2; | ||
| 94 | layout(location = 3) out vec4 FragColor3; | ||
| 95 | layout(location = 4) out vec4 FragColor4; | ||
| 96 | layout(location = 5) out vec4 FragColor5; | ||
| 97 | layout(location = 6) out vec4 FragColor6; | ||
| 98 | layout(location = 7) out vec4 FragColor7; | ||
| 91 | 99 | ||
| 92 | layout (std140) uniform fs_config { | 100 | layout (std140) uniform fs_config { |
| 93 | vec4 viewport_flip; | 101 | vec4 viewport_flip; |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index c788099d4..d53b93ad5 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -9,10 +9,11 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/engines/shader_bytecode.h" | ||
| 12 | 13 | ||
| 13 | namespace OpenGL::GLShader { | 14 | namespace OpenGL::GLShader { |
| 14 | 15 | ||
| 15 | constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; | 16 | constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; |
| 16 | using ProgramCode = std::vector<u64>; | 17 | using ProgramCode = std::vector<u64>; |
| 17 | 18 | ||
| 18 | class ConstBufferEntry { | 19 | class ConstBufferEntry { |
| @@ -50,7 +51,11 @@ public: | |||
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | std::string GetName() const { | 53 | std::string GetName() const { |
| 53 | return BufferBaseNames[static_cast<size_t>(stage)] + std::to_string(index); | 54 | return BufferBaseNames[static_cast<std::size_t>(stage)] + std::to_string(index); |
| 55 | } | ||
| 56 | |||
| 57 | u32 GetHash() const { | ||
| 58 | return (static_cast<u32>(stage) << 16) | index; | ||
| 54 | } | 59 | } |
| 55 | 60 | ||
| 56 | private: | 61 | private: |
| @@ -69,14 +74,15 @@ class SamplerEntry { | |||
| 69 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 74 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 70 | 75 | ||
| 71 | public: | 76 | public: |
| 72 | SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) | 77 | SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index, |
| 73 | : offset(offset), stage(stage), sampler_index(index) {} | 78 | Tegra::Shader::TextureType type, bool is_array) |
| 79 | : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {} | ||
| 74 | 80 | ||
| 75 | size_t GetOffset() const { | 81 | std::size_t GetOffset() const { |
| 76 | return offset; | 82 | return offset; |
| 77 | } | 83 | } |
| 78 | 84 | ||
| 79 | size_t GetIndex() const { | 85 | std::size_t GetIndex() const { |
| 80 | return sampler_index; | 86 | return sampler_index; |
| 81 | } | 87 | } |
| 82 | 88 | ||
| @@ -85,23 +91,63 @@ public: | |||
| 85 | } | 91 | } |
| 86 | 92 | ||
| 87 | std::string GetName() const { | 93 | std::string GetName() const { |
| 88 | return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + | 94 | return std::string(TextureSamplerNames[static_cast<std::size_t>(stage)]) + '_' + |
| 89 | std::to_string(sampler_index) + ']'; | 95 | std::to_string(sampler_index); |
| 96 | } | ||
| 97 | |||
| 98 | std::string GetTypeString() const { | ||
| 99 | using Tegra::Shader::TextureType; | ||
| 100 | std::string glsl_type; | ||
| 101 | |||
| 102 | switch (type) { | ||
| 103 | case TextureType::Texture1D: | ||
| 104 | glsl_type = "sampler1D"; | ||
| 105 | break; | ||
| 106 | case TextureType::Texture2D: | ||
| 107 | glsl_type = "sampler2D"; | ||
| 108 | break; | ||
| 109 | case TextureType::Texture3D: | ||
| 110 | glsl_type = "sampler3D"; | ||
| 111 | break; | ||
| 112 | case TextureType::TextureCube: | ||
| 113 | glsl_type = "samplerCube"; | ||
| 114 | break; | ||
| 115 | default: | ||
| 116 | UNIMPLEMENTED(); | ||
| 117 | } | ||
| 118 | if (is_array) | ||
| 119 | glsl_type += "Array"; | ||
| 120 | return glsl_type; | ||
| 121 | } | ||
| 122 | |||
| 123 | Tegra::Shader::TextureType GetType() const { | ||
| 124 | return type; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool IsArray() const { | ||
| 128 | return is_array; | ||
| 129 | } | ||
| 130 | |||
| 131 | u32 GetHash() const { | ||
| 132 | return (static_cast<u32>(stage) << 16) | static_cast<u32>(sampler_index); | ||
| 90 | } | 133 | } |
| 91 | 134 | ||
| 92 | static std::string GetArrayName(Maxwell::ShaderStage stage) { | 135 | static std::string GetArrayName(Maxwell::ShaderStage stage) { |
| 93 | return TextureSamplerNames[static_cast<size_t>(stage)]; | 136 | return TextureSamplerNames[static_cast<std::size_t>(stage)]; |
| 94 | } | 137 | } |
| 95 | 138 | ||
| 96 | private: | 139 | private: |
| 97 | static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { | 140 | static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { |
| 98 | "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", | 141 | "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", |
| 99 | }; | 142 | }; |
| 143 | |||
| 100 | /// Offset in TSC memory from which to read the sampler object, as specified by the sampling | 144 | /// Offset in TSC memory from which to read the sampler object, as specified by the sampling |
| 101 | /// instruction. | 145 | /// instruction. |
| 102 | size_t offset; | 146 | std::size_t offset; |
| 103 | Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. | 147 | Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. |
| 104 | size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. | 148 | std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. |
| 149 | Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc) | ||
| 150 | bool is_array; ///< Whether the texture is being sampled as an array texture or not. | ||
| 105 | }; | 151 | }; |
| 106 | 152 | ||
| 107 | struct ShaderEntries { | 153 | struct ShaderEntries { |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 533e42caa..b86cd96e8 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | namespace OpenGL::GLShader { | 12 | namespace OpenGL::GLShader { |
| 13 | 13 | ||
| 14 | /// Number of OpenGL texture samplers that can be used in the fragment shader | 14 | /// Number of OpenGL texture samplers that can be used in the fragment shader |
| 15 | static constexpr size_t NumTextureSamplers = 32; | 15 | static constexpr std::size_t NumTextureSamplers = 32; |
| 16 | 16 | ||
| 17 | using Tegra::Engines::Maxwell3D; | 17 | using Tegra::Engines::Maxwell3D; |
| 18 | 18 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 5781d9d16..5f3fe067e 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp | |||
| @@ -25,7 +25,7 @@ GLuint LoadShader(const char* source, GLenum type) { | |||
| 25 | default: | 25 | default: |
| 26 | UNREACHABLE(); | 26 | UNREACHABLE(); |
| 27 | } | 27 | } |
| 28 | GLuint shader_id = glCreateShader(type); | 28 | const GLuint shader_id = glCreateShader(type); |
| 29 | glShaderSource(shader_id, 1, &source, nullptr); | 29 | glShaderSource(shader_id, 1, &source, nullptr); |
| 30 | LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); | 30 | LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); |
| 31 | glCompileShader(shader_id); | 31 | glCompileShader(shader_id); |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 60a4defd1..af99132ba 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -200,9 +200,9 @@ void OpenGLState::Apply() const { | |||
| 200 | const auto& texture_unit = texture_units[i]; | 200 | const auto& texture_unit = texture_units[i]; |
| 201 | const auto& cur_state_texture_unit = cur_state.texture_units[i]; | 201 | const auto& cur_state_texture_unit = cur_state.texture_units[i]; |
| 202 | 202 | ||
| 203 | if (texture_unit.texture_2d != cur_state_texture_unit.texture_2d) { | 203 | if (texture_unit.texture != cur_state_texture_unit.texture) { |
| 204 | glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); | 204 | glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); |
| 205 | glBindTexture(GL_TEXTURE_2D, texture_unit.texture_2d); | 205 | glBindTexture(texture_unit.target, texture_unit.texture); |
| 206 | } | 206 | } |
| 207 | if (texture_unit.sampler != cur_state_texture_unit.sampler) { | 207 | if (texture_unit.sampler != cur_state_texture_unit.sampler) { |
| 208 | glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); | 208 | glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); |
| @@ -214,7 +214,7 @@ void OpenGLState::Apply() const { | |||
| 214 | texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) { | 214 | texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) { |
| 215 | std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g, | 215 | std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g, |
| 216 | texture_unit.swizzle.b, texture_unit.swizzle.a}; | 216 | texture_unit.swizzle.b, texture_unit.swizzle.a}; |
| 217 | glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); | 217 | glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); |
| 218 | } | 218 | } |
| 219 | } | 219 | } |
| 220 | 220 | ||
| @@ -272,7 +272,7 @@ void OpenGLState::Apply() const { | |||
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | // Clip distance | 274 | // Clip distance |
| 275 | for (size_t i = 0; i < clip_distance.size(); ++i) { | 275 | for (std::size_t i = 0; i < clip_distance.size(); ++i) { |
| 276 | if (clip_distance[i] != cur_state.clip_distance[i]) { | 276 | if (clip_distance[i] != cur_state.clip_distance[i]) { |
| 277 | if (clip_distance[i]) { | 277 | if (clip_distance[i]) { |
| 278 | glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); | 278 | glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); |
| @@ -287,7 +287,7 @@ void OpenGLState::Apply() const { | |||
| 287 | 287 | ||
| 288 | OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { | 288 | OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { |
| 289 | for (auto& unit : texture_units) { | 289 | for (auto& unit : texture_units) { |
| 290 | if (unit.texture_2d == handle) { | 290 | if (unit.texture == handle) { |
| 291 | unit.Unbind(); | 291 | unit.Unbind(); |
| 292 | } | 292 | } |
| 293 | } | 293 | } |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 46e96a97d..e3e24b9e7 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -94,8 +94,9 @@ public: | |||
| 94 | 94 | ||
| 95 | // 3 texture units - one for each that is used in PICA fragment shader emulation | 95 | // 3 texture units - one for each that is used in PICA fragment shader emulation |
| 96 | struct TextureUnit { | 96 | struct TextureUnit { |
| 97 | GLuint texture_2d; // GL_TEXTURE_BINDING_2D | 97 | GLuint texture; // GL_TEXTURE_BINDING_2D |
| 98 | GLuint sampler; // GL_SAMPLER_BINDING | 98 | GLuint sampler; // GL_SAMPLER_BINDING |
| 99 | GLenum target; | ||
| 99 | struct { | 100 | struct { |
| 100 | GLint r; // GL_TEXTURE_SWIZZLE_R | 101 | GLint r; // GL_TEXTURE_SWIZZLE_R |
| 101 | GLint g; // GL_TEXTURE_SWIZZLE_G | 102 | GLint g; // GL_TEXTURE_SWIZZLE_G |
| @@ -104,7 +105,7 @@ public: | |||
| 104 | } swizzle; | 105 | } swizzle; |
| 105 | 106 | ||
| 106 | void Unbind() { | 107 | void Unbind() { |
| 107 | texture_2d = 0; | 108 | texture = 0; |
| 108 | swizzle.r = GL_RED; | 109 | swizzle.r = GL_RED; |
| 109 | swizzle.g = GL_GREEN; | 110 | swizzle.g = GL_GREEN; |
| 110 | swizzle.b = GL_BLUE; | 111 | swizzle.b = GL_BLUE; |
| @@ -114,6 +115,7 @@ public: | |||
| 114 | void Reset() { | 115 | void Reset() { |
| 115 | Unbind(); | 116 | Unbind(); |
| 116 | sampler = 0; | 117 | sampler = 0; |
| 118 | target = GL_TEXTURE_2D; | ||
| 117 | } | 119 | } |
| 118 | }; | 120 | }; |
| 119 | std::array<TextureUnit, 32> texture_units; | 121 | std::array<TextureUnit, 32> texture_units; |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index e565afcee..664f3ca20 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp | |||
| @@ -29,7 +29,7 @@ OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coh | |||
| 29 | if (GLAD_GL_ARB_buffer_storage) { | 29 | if (GLAD_GL_ARB_buffer_storage) { |
| 30 | persistent = true; | 30 | persistent = true; |
| 31 | coherent = prefer_coherent; | 31 | coherent = prefer_coherent; |
| 32 | GLbitfield flags = | 32 | const GLbitfield flags = |
| 33 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); | 33 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); |
| 34 | glBufferStorage(gl_target, allocate_size, nullptr, flags); | 34 | glBufferStorage(gl_target, allocate_size, nullptr, flags); |
| 35 | mapped_ptr = static_cast<u8*>(glMapBufferRange( | 35 | mapped_ptr = static_cast<u8*>(glMapBufferRange( |
| @@ -61,7 +61,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a | |||
| 61 | mapped_size = size; | 61 | mapped_size = size; |
| 62 | 62 | ||
| 63 | if (alignment > 0) { | 63 | if (alignment > 0) { |
| 64 | buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment); | 64 | buffer_pos = Common::AlignUp<std::size_t>(buffer_pos, alignment); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | bool invalidate = false; | 67 | bool invalidate = false; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 411a73d50..96d916b07 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -177,7 +177,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | |||
| 177 | Memory::GetPointer(framebuffer_addr), | 177 | Memory::GetPointer(framebuffer_addr), |
| 178 | gl_framebuffer_data.data(), true); | 178 | gl_framebuffer_data.data(), true); |
| 179 | 179 | ||
| 180 | state.texture_units[0].texture_2d = screen_info.texture.resource.handle; | 180 | state.texture_units[0].texture = screen_info.texture.resource.handle; |
| 181 | state.Apply(); | 181 | state.Apply(); |
| 182 | 182 | ||
| 183 | glActiveTexture(GL_TEXTURE0); | 183 | glActiveTexture(GL_TEXTURE0); |
| @@ -194,7 +194,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | |||
| 194 | 194 | ||
| 195 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | 195 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| 196 | 196 | ||
| 197 | state.texture_units[0].texture_2d = 0; | 197 | state.texture_units[0].texture = 0; |
| 198 | state.Apply(); | 198 | state.Apply(); |
| 199 | } | 199 | } |
| 200 | } | 200 | } |
| @@ -205,7 +205,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | |||
| 205 | */ | 205 | */ |
| 206 | void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | 206 | void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, |
| 207 | const TextureInfo& texture) { | 207 | const TextureInfo& texture) { |
| 208 | state.texture_units[0].texture_2d = texture.resource.handle; | 208 | state.texture_units[0].texture = texture.resource.handle; |
| 209 | state.Apply(); | 209 | state.Apply(); |
| 210 | 210 | ||
| 211 | glActiveTexture(GL_TEXTURE0); | 211 | glActiveTexture(GL_TEXTURE0); |
| @@ -214,7 +214,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 214 | // Update existing texture | 214 | // Update existing texture |
| 215 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); | 215 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); |
| 216 | 216 | ||
| 217 | state.texture_units[0].texture_2d = 0; | 217 | state.texture_units[0].texture = 0; |
| 218 | state.Apply(); | 218 | state.Apply(); |
| 219 | } | 219 | } |
| 220 | 220 | ||
| @@ -260,7 +260,7 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 260 | // Allocation of storage is deferred until the first frame, when we | 260 | // Allocation of storage is deferred until the first frame, when we |
| 261 | // know the framebuffer size. | 261 | // know the framebuffer size. |
| 262 | 262 | ||
| 263 | state.texture_units[0].texture_2d = screen_info.texture.resource.handle; | 263 | state.texture_units[0].texture = screen_info.texture.resource.handle; |
| 264 | state.Apply(); | 264 | state.Apply(); |
| 265 | 265 | ||
| 266 | glActiveTexture(GL_TEXTURE0); | 266 | glActiveTexture(GL_TEXTURE0); |
| @@ -272,7 +272,7 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 272 | 272 | ||
| 273 | screen_info.display_texture = screen_info.texture.resource.handle; | 273 | screen_info.display_texture = screen_info.texture.resource.handle; |
| 274 | 274 | ||
| 275 | state.texture_units[0].texture_2d = 0; | 275 | state.texture_units[0].texture = 0; |
| 276 | state.Apply(); | 276 | state.Apply(); |
| 277 | 277 | ||
| 278 | // Clear screen to black | 278 | // Clear screen to black |
| @@ -305,14 +305,14 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | |||
| 305 | UNREACHABLE(); | 305 | UNREACHABLE(); |
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | state.texture_units[0].texture_2d = texture.resource.handle; | 308 | state.texture_units[0].texture = texture.resource.handle; |
| 309 | state.Apply(); | 309 | state.Apply(); |
| 310 | 310 | ||
| 311 | glActiveTexture(GL_TEXTURE0); | 311 | glActiveTexture(GL_TEXTURE0); |
| 312 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | 312 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, |
| 313 | texture.gl_format, texture.gl_type, nullptr); | 313 | texture.gl_format, texture.gl_type, nullptr); |
| 314 | 314 | ||
| 315 | state.texture_units[0].texture_2d = 0; | 315 | state.texture_units[0].texture = 0; |
| 316 | state.Apply(); | 316 | state.Apply(); |
| 317 | } | 317 | } |
| 318 | 318 | ||
| @@ -354,14 +354,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, | |||
| 354 | ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v), | 354 | ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v), |
| 355 | }}; | 355 | }}; |
| 356 | 356 | ||
| 357 | state.texture_units[0].texture_2d = screen_info.display_texture; | 357 | state.texture_units[0].texture = screen_info.display_texture; |
| 358 | state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; | 358 | state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; |
| 359 | state.Apply(); | 359 | state.Apply(); |
| 360 | 360 | ||
| 361 | glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); | 361 | glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); |
| 362 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 362 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 363 | 363 | ||
| 364 | state.texture_units[0].texture_2d = 0; | 364 | state.texture_units[0].texture = 0; |
| 365 | state.Apply(); | 365 | state.Apply(); |
| 366 | } | 366 | } |
| 367 | 367 | ||
| @@ -369,6 +369,12 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, | |||
| 369 | * Draws the emulated screens to the emulator window. | 369 | * Draws the emulated screens to the emulator window. |
| 370 | */ | 370 | */ |
| 371 | void RendererOpenGL::DrawScreen() { | 371 | void RendererOpenGL::DrawScreen() { |
| 372 | if (renderer_settings.set_background_color) { | ||
| 373 | // Update background color before drawing | ||
| 374 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, | ||
| 375 | 0.0f); | ||
| 376 | } | ||
| 377 | |||
| 372 | const auto& layout = render_window.GetFramebufferLayout(); | 378 | const auto& layout = render_window.GetFramebufferLayout(); |
| 373 | const auto& screen = layout.screen; | 379 | const auto& screen = layout.screen; |
| 374 | 380 | ||
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 272294c62..20ba6d4f6 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -46,6 +46,48 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_ | |||
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | template <std::size_t N, std::size_t M> | ||
| 50 | struct alignas(64) SwizzleTable { | ||
| 51 | constexpr SwizzleTable() { | ||
| 52 | for (u32 y = 0; y < N; ++y) { | ||
| 53 | for (u32 x = 0; x < M; ++x) { | ||
| 54 | const u32 x2 = x * 16; | ||
| 55 | values[y][x] = static_cast<u16>(((x2 % 64) / 32) * 256 + ((y % 8) / 2) * 64 + | ||
| 56 | ((x2 % 32) / 16) * 32 + (y % 2) * 16); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | const std::array<u16, M>& operator[](std::size_t index) const { | ||
| 61 | return values[index]; | ||
| 62 | } | ||
| 63 | std::array<std::array<u16, M>, N> values{}; | ||
| 64 | }; | ||
| 65 | |||
| 66 | constexpr auto swizzle_table = SwizzleTable<8, 4>(); | ||
| 67 | |||
| 68 | void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_data, | ||
| 69 | u8* unswizzled_data, bool unswizzle, u32 block_height) { | ||
| 70 | std::array<u8*, 2> data_ptrs; | ||
| 71 | const std::size_t stride{width * bytes_per_pixel}; | ||
| 72 | const std::size_t image_width_in_gobs{(stride + 63) / 64}; | ||
| 73 | const std::size_t copy_size{16}; | ||
| 74 | for (std::size_t y = 0; y < height; ++y) { | ||
| 75 | const std::size_t initial_gob = | ||
| 76 | (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + | ||
| 77 | (y % (8 * block_height) / 8) * 512; | ||
| 78 | const std::size_t pixel_base{y * width * bytes_per_pixel}; | ||
| 79 | const auto& table = swizzle_table[y % 8]; | ||
| 80 | for (std::size_t xb = 0; xb < stride; xb += copy_size) { | ||
| 81 | const std::size_t gob_address{initial_gob + (xb / 64) * 512 * block_height}; | ||
| 82 | const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; | ||
| 83 | const std::size_t pixel_index{xb + pixel_base}; | ||
| 84 | data_ptrs[unswizzle] = swizzled_data + swizzle_offset; | ||
| 85 | data_ptrs[!unswizzle] = unswizzled_data + pixel_index; | ||
| 86 | std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 49 | u32 BytesPerPixel(TextureFormat format) { | 91 | u32 BytesPerPixel(TextureFormat format) { |
| 50 | switch (format) { | 92 | switch (format) { |
| 51 | case TextureFormat::DXT1: | 93 | case TextureFormat::DXT1: |
| @@ -63,6 +105,7 @@ u32 BytesPerPixel(TextureFormat format) { | |||
| 63 | case TextureFormat::R32_G32_B32: | 105 | case TextureFormat::R32_G32_B32: |
| 64 | return 12; | 106 | return 12; |
| 65 | case TextureFormat::ASTC_2D_4X4: | 107 | case TextureFormat::ASTC_2D_4X4: |
| 108 | case TextureFormat::ASTC_2D_8X8: | ||
| 66 | case TextureFormat::A8R8G8B8: | 109 | case TextureFormat::A8R8G8B8: |
| 67 | case TextureFormat::A2B10G10R10: | 110 | case TextureFormat::A2B10G10R10: |
| 68 | case TextureFormat::BF10GF11RF11: | 111 | case TextureFormat::BF10GF11RF11: |
| @@ -91,8 +134,13 @@ u32 BytesPerPixel(TextureFormat format) { | |||
| 91 | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, | 134 | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, |
| 92 | u32 height, u32 block_height) { | 135 | u32 height, u32 block_height) { |
| 93 | std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); | 136 | std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); |
| 94 | CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, | 137 | if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { |
| 95 | Memory::GetPointer(address), unswizzled_data.data(), true, block_height); | 138 | FastSwizzleData(width / tile_size, height / tile_size, bytes_per_pixel, |
| 139 | Memory::GetPointer(address), unswizzled_data.data(), true, block_height); | ||
| 140 | } else { | ||
| 141 | CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, | ||
| 142 | Memory::GetPointer(address), unswizzled_data.data(), true, block_height); | ||
| 143 | } | ||
| 96 | return unswizzled_data; | 144 | return unswizzled_data; |
| 97 | } | 145 | } |
| 98 | 146 | ||
| @@ -111,6 +159,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 111 | case TextureFormat::BC6H_UF16: | 159 | case TextureFormat::BC6H_UF16: |
| 112 | case TextureFormat::BC6H_SF16: | 160 | case TextureFormat::BC6H_SF16: |
| 113 | case TextureFormat::ASTC_2D_4X4: | 161 | case TextureFormat::ASTC_2D_4X4: |
| 162 | case TextureFormat::ASTC_2D_8X8: | ||
| 114 | case TextureFormat::A8R8G8B8: | 163 | case TextureFormat::A8R8G8B8: |
| 115 | case TextureFormat::A2B10G10R10: | 164 | case TextureFormat::A2B10G10R10: |
| 116 | case TextureFormat::A1B5G5R5: | 165 | case TextureFormat::A1B5G5R5: |
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index c6bd2f4b9..c2fb824b2 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h | |||
| @@ -170,8 +170,12 @@ struct TICEntry { | |||
| 170 | BitField<0, 16, u32> width_minus_1; | 170 | BitField<0, 16, u32> width_minus_1; |
| 171 | BitField<23, 4, TextureType> texture_type; | 171 | BitField<23, 4, TextureType> texture_type; |
| 172 | }; | 172 | }; |
| 173 | u16 height_minus_1; | 173 | union { |
| 174 | INSERT_PADDING_BYTES(10); | 174 | BitField<0, 16, u32> height_minus_1; |
| 175 | BitField<16, 15, u32> depth_minus_1; | ||
| 176 | }; | ||
| 177 | |||
| 178 | INSERT_PADDING_BYTES(8); | ||
| 175 | 179 | ||
| 176 | GPUVAddr Address() const { | 180 | GPUVAddr Address() const { |
| 177 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); | 181 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); |
| @@ -192,6 +196,10 @@ struct TICEntry { | |||
| 192 | return height_minus_1 + 1; | 196 | return height_minus_1 + 1; |
| 193 | } | 197 | } |
| 194 | 198 | ||
| 199 | u32 Depth() const { | ||
| 200 | return depth_minus_1 + 1; | ||
| 201 | } | ||
| 202 | |||
| 195 | u32 BlockHeight() const { | 203 | u32 BlockHeight() const { |
| 196 | ASSERT(header_version == TICHeaderVersion::BlockLinear || | 204 | ASSERT(header_version == TICHeaderVersion::BlockLinear || |
| 197 | header_version == TICHeaderVersion::BlockLinearColorKey); | 205 | header_version == TICHeaderVersion::BlockLinearColorKey); |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index ea9ea69e4..f48b69809 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -9,6 +9,8 @@ add_executable(yuzu | |||
| 9 | about_dialog.h | 9 | about_dialog.h |
| 10 | bootmanager.cpp | 10 | bootmanager.cpp |
| 11 | bootmanager.h | 11 | bootmanager.h |
| 12 | compatibility_list.cpp | ||
| 13 | compatibility_list.h | ||
| 12 | configuration/config.cpp | 14 | configuration/config.cpp |
| 13 | configuration/config.h | 15 | configuration/config.h |
| 14 | configuration/configure_audio.cpp | 16 | configuration/configure_audio.cpp |
| @@ -43,6 +45,8 @@ add_executable(yuzu | |||
| 43 | game_list.cpp | 45 | game_list.cpp |
| 44 | game_list.h | 46 | game_list.h |
| 45 | game_list_p.h | 47 | game_list_p.h |
| 48 | game_list_worker.cpp | ||
| 49 | game_list_worker.h | ||
| 46 | hotkeys.cpp | 50 | hotkeys.cpp |
| 47 | hotkeys.h | 51 | hotkeys.h |
| 48 | main.cpp | 52 | main.cpp |
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index a81ad2888..3efa65a38 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp | |||
| @@ -11,7 +11,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia | |||
| 11 | ui->setupUi(this); | 11 | ui->setupUi(this); |
| 12 | ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200)); | 12 | ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200)); |
| 13 | ui->labelBuildInfo->setText( | 13 | ui->labelBuildInfo->setText( |
| 14 | ui->labelBuildInfo->text().arg(Common::g_build_name, Common::g_scm_branch, | 14 | ui->labelBuildInfo->text().arg(Common::g_build_fullname, Common::g_scm_branch, |
| 15 | Common::g_scm_desc, QString(Common::g_build_date).left(10))); | 15 | Common::g_scm_desc, QString(Common::g_build_date).left(10))); |
| 16 | } | 16 | } |
| 17 | 17 | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 159b2c32b..4e4c108ab 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -112,6 +112,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | |||
| 112 | setWindowTitle(QString::fromStdString(window_title)); | 112 | setWindowTitle(QString::fromStdString(window_title)); |
| 113 | 113 | ||
| 114 | InputCommon::Init(); | 114 | InputCommon::Init(); |
| 115 | InputCommon::StartJoystickEventHandler(); | ||
| 115 | } | 116 | } |
| 116 | 117 | ||
| 117 | GRenderWindow::~GRenderWindow() { | 118 | GRenderWindow::~GRenderWindow() { |
| @@ -256,6 +257,7 @@ void GRenderWindow::InitRenderTarget() { | |||
| 256 | QGLFormat fmt; | 257 | QGLFormat fmt; |
| 257 | fmt.setVersion(3, 3); | 258 | fmt.setVersion(3, 3); |
| 258 | fmt.setProfile(QGLFormat::CoreProfile); | 259 | fmt.setProfile(QGLFormat::CoreProfile); |
| 260 | fmt.setSwapInterval(false); | ||
| 259 | 261 | ||
| 260 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X | 262 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X |
| 261 | fmt.setOption(QGL::NoDeprecatedFunctions); | 263 | fmt.setOption(QGL::NoDeprecatedFunctions); |
diff --git a/src/yuzu/compatibility_list.cpp b/src/yuzu/compatibility_list.cpp new file mode 100644 index 000000000..2d2cfd03c --- /dev/null +++ b/src/yuzu/compatibility_list.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 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 <algorithm> | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "yuzu/compatibility_list.h" | ||
| 10 | |||
| 11 | CompatibilityList::const_iterator FindMatchingCompatibilityEntry( | ||
| 12 | const CompatibilityList& compatibility_list, u64 program_id) { | ||
| 13 | return std::find_if(compatibility_list.begin(), compatibility_list.end(), | ||
| 14 | [program_id](const auto& element) { | ||
| 15 | std::string pid = fmt::format("{:016X}", program_id); | ||
| 16 | return element.first == pid; | ||
| 17 | }); | ||
| 18 | } | ||
diff --git a/src/yuzu/compatibility_list.h b/src/yuzu/compatibility_list.h new file mode 100644 index 000000000..bc0175bd3 --- /dev/null +++ b/src/yuzu/compatibility_list.h | |||
| @@ -0,0 +1,17 @@ | |||
| 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 <string> | ||
| 8 | #include <unordered_map> | ||
| 9 | |||
| 10 | #include <QString> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | using CompatibilityList = std::unordered_map<std::string, std::pair<QString, QString>>; | ||
| 15 | |||
| 16 | CompatibilityList::const_iterator FindMatchingCompatibilityEntry( | ||
| 17 | const CompatibilityList& compatibility_list, u64 program_id); | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 60b6d6d44..d229225b4 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -95,6 +95,8 @@ void Config::ReadValues() { | |||
| 95 | 95 | ||
| 96 | qt_config->beginGroup("Audio"); | 96 | qt_config->beginGroup("Audio"); |
| 97 | Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); | 97 | Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); |
| 98 | Settings::values.enable_audio_stretching = | ||
| 99 | qt_config->value("enable_audio_stretching", true).toBool(); | ||
| 98 | Settings::values.audio_device_id = | 100 | Settings::values.audio_device_id = |
| 99 | qt_config->value("output_device", "auto").toString().toStdString(); | 101 | qt_config->value("output_device", "auto").toString().toStdString(); |
| 100 | Settings::values.volume = qt_config->value("volume", 1).toFloat(); | 102 | Settings::values.volume = qt_config->value("volume", 1).toFloat(); |
| @@ -102,6 +104,20 @@ void Config::ReadValues() { | |||
| 102 | 104 | ||
| 103 | qt_config->beginGroup("Data Storage"); | 105 | qt_config->beginGroup("Data Storage"); |
| 104 | Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); | 106 | Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); |
| 107 | FileUtil::GetUserPath( | ||
| 108 | FileUtil::UserPath::NANDDir, | ||
| 109 | qt_config | ||
| 110 | ->value("nand_directory", | ||
| 111 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) | ||
| 112 | .toString() | ||
| 113 | .toStdString()); | ||
| 114 | FileUtil::GetUserPath( | ||
| 115 | FileUtil::UserPath::SDMCDir, | ||
| 116 | qt_config | ||
| 117 | ->value("sdmc_directory", | ||
| 118 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) | ||
| 119 | .toString() | ||
| 120 | .toStdString()); | ||
| 105 | qt_config->endGroup(); | 121 | qt_config->endGroup(); |
| 106 | 122 | ||
| 107 | qt_config->beginGroup("System"); | 123 | qt_config->beginGroup("System"); |
| @@ -216,12 +232,17 @@ void Config::SaveValues() { | |||
| 216 | 232 | ||
| 217 | qt_config->beginGroup("Audio"); | 233 | qt_config->beginGroup("Audio"); |
| 218 | qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); | 234 | qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); |
| 235 | qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); | ||
| 219 | qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); | 236 | qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); |
| 220 | qt_config->setValue("volume", Settings::values.volume); | 237 | qt_config->setValue("volume", Settings::values.volume); |
| 221 | qt_config->endGroup(); | 238 | qt_config->endGroup(); |
| 222 | 239 | ||
| 223 | qt_config->beginGroup("Data Storage"); | 240 | qt_config->beginGroup("Data Storage"); |
| 224 | qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); | 241 | qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); |
| 242 | qt_config->setValue("nand_directory", | ||
| 243 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | ||
| 244 | qt_config->setValue("sdmc_directory", | ||
| 245 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||
| 225 | qt_config->endGroup(); | 246 | qt_config->endGroup(); |
| 226 | 247 | ||
| 227 | qt_config->beginGroup("System"); | 248 | qt_config->beginGroup("System"); |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index fbb813f6c..6ea59f2a3 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -46,6 +46,8 @@ void ConfigureAudio::setConfiguration() { | |||
| 46 | } | 46 | } |
| 47 | ui->output_sink_combo_box->setCurrentIndex(new_sink_index); | 47 | ui->output_sink_combo_box->setCurrentIndex(new_sink_index); |
| 48 | 48 | ||
| 49 | ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); | ||
| 50 | |||
| 49 | // The device list cannot be pre-populated (nor listed) until the output sink is known. | 51 | // The device list cannot be pre-populated (nor listed) until the output sink is known. |
| 50 | updateAudioDevices(new_sink_index); | 52 | updateAudioDevices(new_sink_index); |
| 51 | 53 | ||
| @@ -67,6 +69,7 @@ void ConfigureAudio::applyConfiguration() { | |||
| 67 | Settings::values.sink_id = | 69 | Settings::values.sink_id = |
| 68 | ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) | 70 | ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) |
| 69 | .toStdString(); | 71 | .toStdString(); |
| 72 | Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); | ||
| 70 | Settings::values.audio_device_id = | 73 | Settings::values.audio_device_id = |
| 71 | ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) | 74 | ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) |
| 72 | .toStdString(); | 75 | .toStdString(); |
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui index ef67890dc..a29a0e265 100644 --- a/src/yuzu/configuration/configure_audio.ui +++ b/src/yuzu/configuration/configure_audio.ui | |||
| @@ -31,6 +31,16 @@ | |||
| 31 | </item> | 31 | </item> |
| 32 | </layout> | 32 | </layout> |
| 33 | </item> | 33 | </item> |
| 34 | <item> | ||
| 35 | <widget class="QCheckBox" name="toggle_audio_stretching"> | ||
| 36 | <property name="toolTip"> | ||
| 37 | <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> | ||
| 38 | </property> | ||
| 39 | <property name="text"> | ||
| 40 | <string>Enable audio stretching</string> | ||
| 41 | </property> | ||
| 42 | </widget> | ||
| 43 | </item> | ||
| 34 | <item> | 44 | <item> |
| 35 | <layout class="QHBoxLayout"> | 45 | <layout class="QHBoxLayout"> |
| 36 | <item> | 46 | <item> |
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp index 1ae3423cf..8743ce982 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_gamelist.cpp | |||
| @@ -2,47 +2,51 @@ | |||
| 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 "core/core.h" | 5 | #include <array> |
| 6 | #include <utility> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 6 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 7 | #include "ui_configure_gamelist.h" | 10 | #include "ui_configure_gamelist.h" |
| 8 | #include "ui_settings.h" | ||
| 9 | #include "yuzu/configuration/configure_gamelist.h" | 11 | #include "yuzu/configuration/configure_gamelist.h" |
| 12 | #include "yuzu/ui_settings.h" | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | constexpr std::array<std::pair<u32, const char*>, 5> default_icon_sizes{{ | ||
| 16 | std::make_pair(0, QT_TR_NOOP("None")), | ||
| 17 | std::make_pair(32, QT_TR_NOOP("Small (32x32)")), | ||
| 18 | std::make_pair(64, QT_TR_NOOP("Standard (64x64)")), | ||
| 19 | std::make_pair(128, QT_TR_NOOP("Large (128x128)")), | ||
| 20 | std::make_pair(256, QT_TR_NOOP("Full Size (256x256)")), | ||
| 21 | }}; | ||
| 22 | |||
| 23 | constexpr std::array<const char*, 4> row_text_names{{ | ||
| 24 | QT_TR_NOOP("Filename"), | ||
| 25 | QT_TR_NOOP("Filetype"), | ||
| 26 | QT_TR_NOOP("Title ID"), | ||
| 27 | QT_TR_NOOP("Title Name"), | ||
| 28 | }}; | ||
| 29 | } // Anonymous namespace | ||
| 10 | 30 | ||
| 11 | ConfigureGameList::ConfigureGameList(QWidget* parent) | 31 | ConfigureGameList::ConfigureGameList(QWidget* parent) |
| 12 | : QWidget(parent), ui(new Ui::ConfigureGameList) { | 32 | : QWidget(parent), ui(new Ui::ConfigureGameList) { |
| 13 | ui->setupUi(this); | 33 | ui->setupUi(this); |
| 14 | 34 | ||
| 15 | static const std::vector<std::pair<u32, std::string>> default_icon_sizes{ | 35 | InitializeIconSizeComboBox(); |
| 16 | std::make_pair(0, "None"), std::make_pair(32, "Small"), | 36 | InitializeRowComboBoxes(); |
| 17 | std::make_pair(64, "Standard"), std::make_pair(128, "Large"), | ||
| 18 | std::make_pair(256, "Full Size"), | ||
| 19 | }; | ||
| 20 | |||
| 21 | for (const auto& size : default_icon_sizes) { | ||
| 22 | ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" + | ||
| 23 | std::to_string(size.first) + "x" + | ||
| 24 | std::to_string(size.first) + ")"), | ||
| 25 | size.first); | ||
| 26 | } | ||
| 27 | |||
| 28 | static const std::vector<std::string> row_text_names{ | ||
| 29 | "Filename", | ||
| 30 | "Filetype", | ||
| 31 | "Title ID", | ||
| 32 | "Title Name", | ||
| 33 | }; | ||
| 34 | |||
| 35 | for (size_t i = 0; i < row_text_names.size(); ++i) { | ||
| 36 | ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]), | ||
| 37 | QVariant::fromValue(i)); | ||
| 38 | ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]), | ||
| 39 | QVariant::fromValue(i)); | ||
| 40 | } | ||
| 41 | 37 | ||
| 42 | this->setConfiguration(); | 38 | this->setConfiguration(); |
| 43 | } | 39 | } |
| 44 | 40 | ||
| 45 | ConfigureGameList::~ConfigureGameList() {} | 41 | ConfigureGameList::~ConfigureGameList() = default; |
| 42 | |||
| 43 | void ConfigureGameList::applyConfiguration() { | ||
| 44 | UISettings::values.show_unknown = ui->show_unknown->isChecked(); | ||
| 45 | UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); | ||
| 46 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | ||
| 47 | UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); | ||
| 48 | Settings::Apply(); | ||
| 49 | } | ||
| 46 | 50 | ||
| 47 | void ConfigureGameList::setConfiguration() { | 51 | void ConfigureGameList::setConfiguration() { |
| 48 | ui->show_unknown->setChecked(UISettings::values.show_unknown); | 52 | ui->show_unknown->setChecked(UISettings::values.show_unknown); |
| @@ -54,10 +58,39 @@ void ConfigureGameList::setConfiguration() { | |||
| 54 | ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id)); | 58 | ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id)); |
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | void ConfigureGameList::applyConfiguration() { | 61 | void ConfigureGameList::changeEvent(QEvent* event) { |
| 58 | UISettings::values.show_unknown = ui->show_unknown->isChecked(); | 62 | if (event->type() == QEvent::LanguageChange) { |
| 59 | UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); | 63 | RetranslateUI(); |
| 60 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | 64 | return; |
| 61 | UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); | 65 | } |
| 62 | Settings::Apply(); | 66 | |
| 67 | QWidget::changeEvent(event); | ||
| 68 | } | ||
| 69 | |||
| 70 | void ConfigureGameList::RetranslateUI() { | ||
| 71 | ui->retranslateUi(this); | ||
| 72 | |||
| 73 | for (int i = 0; i < ui->icon_size_combobox->count(); i++) { | ||
| 74 | ui->icon_size_combobox->setItemText(i, tr(default_icon_sizes[i].second)); | ||
| 75 | } | ||
| 76 | |||
| 77 | for (int i = 0; i < ui->row_1_text_combobox->count(); i++) { | ||
| 78 | const QString name = tr(row_text_names[i]); | ||
| 79 | |||
| 80 | ui->row_1_text_combobox->setItemText(i, name); | ||
| 81 | ui->row_2_text_combobox->setItemText(i, name); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | void ConfigureGameList::InitializeIconSizeComboBox() { | ||
| 86 | for (const auto& size : default_icon_sizes) { | ||
| 87 | ui->icon_size_combobox->addItem(size.second, size.first); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void ConfigureGameList::InitializeRowComboBoxes() { | ||
| 92 | for (std::size_t i = 0; i < row_text_names.size(); ++i) { | ||
| 93 | ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); | ||
| 94 | ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); | ||
| 95 | } | ||
| 63 | } | 96 | } |
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h index 94fba6373..ff7406c60 100644 --- a/src/yuzu/configuration/configure_gamelist.h +++ b/src/yuzu/configuration/configure_gamelist.h | |||
| @@ -23,6 +23,11 @@ public: | |||
| 23 | private: | 23 | private: |
| 24 | void setConfiguration(); | 24 | void setConfiguration(); |
| 25 | 25 | ||
| 26 | private: | 26 | void changeEvent(QEvent*) override; |
| 27 | void RetranslateUI(); | ||
| 28 | |||
| 29 | void InitializeIconSizeComboBox(); | ||
| 30 | void InitializeRowComboBoxes(); | ||
| 31 | |||
| 27 | std::unique_ptr<Ui::ConfigureGameList> ui; | 32 | std::unique_ptr<Ui::ConfigureGameList> ui; |
| 28 | }; | 33 | }; |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index d8caee1e8..9292d9a42 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -20,7 +20,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 20 | this->setConfiguration(); | 20 | this->setConfiguration(); |
| 21 | 21 | ||
| 22 | ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 22 | ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 23 | ui->use_multi_core->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 24 | ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 23 | ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 25 | } | 24 | } |
| 26 | 25 | ||
| @@ -31,7 +30,6 @@ void ConfigureGeneral::setConfiguration() { | |||
| 31 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 30 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 32 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 31 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 33 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); | 32 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); |
| 34 | ui->use_multi_core->setChecked(Settings::values.use_multi_core); | ||
| 35 | ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); | 33 | ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); |
| 36 | } | 34 | } |
| 37 | 35 | ||
| @@ -46,6 +44,5 @@ void ConfigureGeneral::applyConfiguration() { | |||
| 46 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | 44 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); |
| 47 | 45 | ||
| 48 | Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); | 46 | Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); |
| 49 | Settings::values.use_multi_core = ui->use_multi_core->isChecked(); | ||
| 50 | Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); | 47 | Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); |
| 51 | } | 48 | } |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 233adbe27..1775c4d40 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -58,13 +58,6 @@ | |||
| 58 | </property> | 58 | </property> |
| 59 | </widget> | 59 | </widget> |
| 60 | </item> | 60 | </item> |
| 61 | <item> | ||
| 62 | <widget class="QCheckBox" name="use_multi_core"> | ||
| 63 | <property name="text"> | ||
| 64 | <string>Enable multi-core</string> | ||
| 65 | </property> | ||
| 66 | </widget> | ||
| 67 | </item> | ||
| 68 | </layout> | 61 | </layout> |
| 69 | </item> | 62 | </item> |
| 70 | </layout> | 63 | </layout> |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index ee1287028..839d58f59 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <QColorDialog> | ||
| 5 | #include "core/core.h" | 6 | #include "core/core.h" |
| 6 | #include "core/settings.h" | 7 | #include "core/settings.h" |
| 7 | #include "ui_configure_graphics.h" | 8 | #include "ui_configure_graphics.h" |
| @@ -16,6 +17,14 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 16 | ui->frame_limit->setEnabled(Settings::values.use_frame_limit); | 17 | ui->frame_limit->setEnabled(Settings::values.use_frame_limit); |
| 17 | connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, | 18 | connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, |
| 18 | &QSpinBox::setEnabled); | 19 | &QSpinBox::setEnabled); |
| 20 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | ||
| 21 | const QColor new_bg_color = QColorDialog::getColor(bg_color); | ||
| 22 | if (!new_bg_color.isValid()) | ||
| 23 | return; | ||
| 24 | bg_color = new_bg_color; | ||
| 25 | ui->bg_button->setStyleSheet( | ||
| 26 | QString("QPushButton { background-color: %1 }").arg(bg_color.name())); | ||
| 27 | }); | ||
| 19 | } | 28 | } |
| 20 | 29 | ||
| 21 | ConfigureGraphics::~ConfigureGraphics() = default; | 30 | ConfigureGraphics::~ConfigureGraphics() = default; |
| @@ -65,6 +74,10 @@ void ConfigureGraphics::setConfiguration() { | |||
| 65 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 74 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); |
| 66 | ui->frame_limit->setValue(Settings::values.frame_limit); | 75 | ui->frame_limit->setValue(Settings::values.frame_limit); |
| 67 | ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); | 76 | ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); |
| 77 | bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | ||
| 78 | Settings::values.bg_blue); | ||
| 79 | ui->bg_button->setStyleSheet( | ||
| 80 | QString("QPushButton { background-color: %1 }").arg(bg_color.name())); | ||
| 68 | } | 81 | } |
| 69 | 82 | ||
| 70 | void ConfigureGraphics::applyConfiguration() { | 83 | void ConfigureGraphics::applyConfiguration() { |
| @@ -73,4 +86,7 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 73 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 86 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); |
| 74 | Settings::values.frame_limit = ui->frame_limit->value(); | 87 | Settings::values.frame_limit = ui->frame_limit->value(); |
| 75 | Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); | 88 | Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); |
| 89 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | ||
| 90 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | ||
| 91 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); | ||
| 76 | } | 92 | } |
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 5497a55f7..9bda26fd6 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -25,4 +25,5 @@ private: | |||
| 25 | 25 | ||
| 26 | private: | 26 | private: |
| 27 | std::unique_ptr<Ui::ConfigureGraphics> ui; | 27 | std::unique_ptr<Ui::ConfigureGraphics> ui; |
| 28 | QColor bg_color; | ||
| 28 | }; | 29 | }; |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 3bc18c26e..8fc00af1b 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -96,6 +96,27 @@ | |||
| 96 | </item> | 96 | </item> |
| 97 | </layout> | 97 | </layout> |
| 98 | </item> | 98 | </item> |
| 99 | <item> | ||
| 100 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | ||
| 101 | <item> | ||
| 102 | <widget class="QLabel" name="bg_label"> | ||
| 103 | <property name="text"> | ||
| 104 | <string>Background Color:</string> | ||
| 105 | </property> | ||
| 106 | </widget> | ||
| 107 | </item> | ||
| 108 | <item> | ||
| 109 | <widget class="QPushButton" name="bg_button"> | ||
| 110 | <property name="maximumSize"> | ||
| 111 | <size> | ||
| 112 | <width>40</width> | ||
| 113 | <height>16777215</height> | ||
| 114 | </size> | ||
| 115 | </property> | ||
| 116 | </widget> | ||
| 117 | </item> | ||
| 118 | </layout> | ||
| 119 | </item> | ||
| 99 | </layout> | 120 | </layout> |
| 100 | </widget> | 121 | </widget> |
| 101 | </item> | 122 | </item> |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 5e7badedf..d29abb74b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | 1 | // Copyright 2016 Citra Emulator Project |
| 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 | ||
| @@ -53,19 +53,18 @@ static QString ButtonToText(const Common::ParamPackage& param) { | |||
| 53 | } else if (param.Get("engine", "") == "keyboard") { | 53 | } else if (param.Get("engine", "") == "keyboard") { |
| 54 | return getKeyName(param.Get("code", 0)); | 54 | return getKeyName(param.Get("code", 0)); |
| 55 | } else if (param.Get("engine", "") == "sdl") { | 55 | } else if (param.Get("engine", "") == "sdl") { |
| 56 | QString text = QString(QObject::tr("Joystick %1")).arg(param.Get("joystick", "").c_str()); | ||
| 57 | if (param.Has("hat")) { | 56 | if (param.Has("hat")) { |
| 58 | text += QString(QObject::tr(" Hat %1 %2")) | 57 | return QString(QObject::tr("Hat %1 %2")) |
| 59 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); | 58 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); |
| 60 | } | 59 | } |
| 61 | if (param.Has("axis")) { | 60 | if (param.Has("axis")) { |
| 62 | text += QString(QObject::tr(" Axis %1%2")) | 61 | return QString(QObject::tr("Axis %1%2")) |
| 63 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); | 62 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); |
| 64 | } | 63 | } |
| 65 | if (param.Has("button")) { | 64 | if (param.Has("button")) { |
| 66 | text += QString(QObject::tr(" Button %1")).arg(param.Get("button", "").c_str()); | 65 | return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); |
| 67 | } | 66 | } |
| 68 | return text; | 67 | return QString(); |
| 69 | } else { | 68 | } else { |
| 70 | return QObject::tr("[unknown]"); | 69 | return QObject::tr("[unknown]"); |
| 71 | } | 70 | } |
| @@ -81,13 +80,12 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string | |||
| 81 | return QString(QObject::tr("[unused]")); | 80 | return QString(QObject::tr("[unused]")); |
| 82 | } | 81 | } |
| 83 | 82 | ||
| 84 | QString text = QString(QObject::tr("Joystick %1")).arg(param.Get("joystick", "").c_str()); | ||
| 85 | if (dir == "left" || dir == "right") { | 83 | if (dir == "left" || dir == "right") { |
| 86 | text += QString(QObject::tr(" Axis %1")).arg(param.Get("axis_x", "").c_str()); | 84 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); |
| 87 | } else if (dir == "up" || dir == "down") { | 85 | } else if (dir == "up" || dir == "down") { |
| 88 | text += QString(QObject::tr(" Axis %1")).arg(param.Get("axis_y", "").c_str()); | 86 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str()); |
| 89 | } | 87 | } |
| 90 | return text; | 88 | return QString(); |
| 91 | } else { | 89 | } else { |
| 92 | return QObject::tr("[unknown]"); | 90 | return QObject::tr("[unknown]"); |
| 93 | } | 91 | } |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index fe682b3b8..b5c88f944 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp | |||
| @@ -42,7 +42,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { | |||
| 42 | tr("Finished primitive batch")}, | 42 | tr("Finished primitive batch")}, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | DEBUG_ASSERT(map.size() == static_cast<size_t>(Tegra::DebugContext::Event::NumEvents)); | 45 | DEBUG_ASSERT(map.size() == |
| 46 | static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents)); | ||
| 46 | return (map.find(event) != map.end()) ? map.at(event) : QString(); | 47 | return (map.find(event) != map.end()) ? map.at(event) : QString(); |
| 47 | } | 48 | } |
| 48 | 49 | ||
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 7e37962d5..cbcd5dd5f 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp | |||
| @@ -341,8 +341,8 @@ void GraphicsSurfaceWidget::OnUpdate() { | |||
| 341 | // directly... | 341 | // directly... |
| 342 | 342 | ||
| 343 | const auto& registers = gpu.Maxwell3D().regs; | 343 | const auto& registers = gpu.Maxwell3D().regs; |
| 344 | const auto& rt = registers.rt[static_cast<size_t>(surface_source) - | 344 | const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - |
| 345 | static_cast<size_t>(Source::RenderTarget0)]; | 345 | static_cast<std::size_t>(Source::RenderTarget0)]; |
| 346 | 346 | ||
| 347 | surface_address = rt.Address(); | 347 | surface_address = rt.Address(); |
| 348 | surface_width = rt.width; | 348 | surface_width = rt.width; |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 6c2cd967e..a3b1fd357 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/kernel/thread.h" | 15 | #include "core/hle/kernel/thread.h" |
| 16 | #include "core/hle/kernel/timer.h" | 16 | #include "core/hle/kernel/timer.h" |
| 17 | #include "core/hle/kernel/wait_object.h" | 17 | #include "core/hle/kernel/wait_object.h" |
| 18 | #include "core/memory.h" | ||
| 18 | 19 | ||
| 19 | WaitTreeItem::WaitTreeItem() = default; | 20 | WaitTreeItem::WaitTreeItem() = default; |
| 20 | WaitTreeItem::~WaitTreeItem() = default; | 21 | WaitTreeItem::~WaitTreeItem() = default; |
| @@ -117,7 +118,7 @@ QString WaitTreeCallstack::GetText() const { | |||
| 117 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { | 118 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { |
| 118 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 119 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 119 | 120 | ||
| 120 | constexpr size_t BaseRegister = 29; | 121 | constexpr std::size_t BaseRegister = 29; |
| 121 | u64 base_pointer = thread.context.cpu_registers[BaseRegister]; | 122 | u64 base_pointer = thread.context.cpu_registers[BaseRegister]; |
| 122 | 123 | ||
| 123 | while (base_pointer != 0) { | 124 | while (base_pointer != 0) { |
| @@ -213,35 +214,35 @@ QString WaitTreeThread::GetText() const { | |||
| 213 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 214 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 214 | QString status; | 215 | QString status; |
| 215 | switch (thread.status) { | 216 | switch (thread.status) { |
| 216 | case ThreadStatus::Running: | 217 | case Kernel::ThreadStatus::Running: |
| 217 | status = tr("running"); | 218 | status = tr("running"); |
| 218 | break; | 219 | break; |
| 219 | case ThreadStatus::Ready: | 220 | case Kernel::ThreadStatus::Ready: |
| 220 | status = tr("ready"); | 221 | status = tr("ready"); |
| 221 | break; | 222 | break; |
| 222 | case ThreadStatus::WaitHLEEvent: | 223 | case Kernel::ThreadStatus::WaitHLEEvent: |
| 223 | status = tr("waiting for HLE return"); | 224 | status = tr("waiting for HLE return"); |
| 224 | break; | 225 | break; |
| 225 | case ThreadStatus::WaitSleep: | 226 | case Kernel::ThreadStatus::WaitSleep: |
| 226 | status = tr("sleeping"); | 227 | status = tr("sleeping"); |
| 227 | break; | 228 | break; |
| 228 | case ThreadStatus::WaitIPC: | 229 | case Kernel::ThreadStatus::WaitIPC: |
| 229 | status = tr("waiting for IPC reply"); | 230 | status = tr("waiting for IPC reply"); |
| 230 | break; | 231 | break; |
| 231 | case ThreadStatus::WaitSynchAll: | 232 | case Kernel::ThreadStatus::WaitSynchAll: |
| 232 | case ThreadStatus::WaitSynchAny: | 233 | case Kernel::ThreadStatus::WaitSynchAny: |
| 233 | status = tr("waiting for objects"); | 234 | status = tr("waiting for objects"); |
| 234 | break; | 235 | break; |
| 235 | case ThreadStatus::WaitMutex: | 236 | case Kernel::ThreadStatus::WaitMutex: |
| 236 | status = tr("waiting for mutex"); | 237 | status = tr("waiting for mutex"); |
| 237 | break; | 238 | break; |
| 238 | case ThreadStatus::WaitArb: | 239 | case Kernel::ThreadStatus::WaitArb: |
| 239 | status = tr("waiting for address arbiter"); | 240 | status = tr("waiting for address arbiter"); |
| 240 | break; | 241 | break; |
| 241 | case ThreadStatus::Dormant: | 242 | case Kernel::ThreadStatus::Dormant: |
| 242 | status = tr("dormant"); | 243 | status = tr("dormant"); |
| 243 | break; | 244 | break; |
| 244 | case ThreadStatus::Dead: | 245 | case Kernel::ThreadStatus::Dead: |
| 245 | status = tr("dead"); | 246 | status = tr("dead"); |
| 246 | break; | 247 | break; |
| 247 | } | 248 | } |
| @@ -254,23 +255,23 @@ QString WaitTreeThread::GetText() const { | |||
| 254 | QColor WaitTreeThread::GetColor() const { | 255 | QColor WaitTreeThread::GetColor() const { |
| 255 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 256 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 256 | switch (thread.status) { | 257 | switch (thread.status) { |
| 257 | case ThreadStatus::Running: | 258 | case Kernel::ThreadStatus::Running: |
| 258 | return QColor(Qt::GlobalColor::darkGreen); | 259 | return QColor(Qt::GlobalColor::darkGreen); |
| 259 | case ThreadStatus::Ready: | 260 | case Kernel::ThreadStatus::Ready: |
| 260 | return QColor(Qt::GlobalColor::darkBlue); | 261 | return QColor(Qt::GlobalColor::darkBlue); |
| 261 | case ThreadStatus::WaitHLEEvent: | 262 | case Kernel::ThreadStatus::WaitHLEEvent: |
| 262 | case ThreadStatus::WaitIPC: | 263 | case Kernel::ThreadStatus::WaitIPC: |
| 263 | return QColor(Qt::GlobalColor::darkRed); | 264 | return QColor(Qt::GlobalColor::darkRed); |
| 264 | case ThreadStatus::WaitSleep: | 265 | case Kernel::ThreadStatus::WaitSleep: |
| 265 | return QColor(Qt::GlobalColor::darkYellow); | 266 | return QColor(Qt::GlobalColor::darkYellow); |
| 266 | case ThreadStatus::WaitSynchAll: | 267 | case Kernel::ThreadStatus::WaitSynchAll: |
| 267 | case ThreadStatus::WaitSynchAny: | 268 | case Kernel::ThreadStatus::WaitSynchAny: |
| 268 | case ThreadStatus::WaitMutex: | 269 | case Kernel::ThreadStatus::WaitMutex: |
| 269 | case ThreadStatus::WaitArb: | 270 | case Kernel::ThreadStatus::WaitArb: |
| 270 | return QColor(Qt::GlobalColor::red); | 271 | return QColor(Qt::GlobalColor::red); |
| 271 | case ThreadStatus::Dormant: | 272 | case Kernel::ThreadStatus::Dormant: |
| 272 | return QColor(Qt::GlobalColor::darkCyan); | 273 | return QColor(Qt::GlobalColor::darkCyan); |
| 273 | case ThreadStatus::Dead: | 274 | case Kernel::ThreadStatus::Dead: |
| 274 | return QColor(Qt::GlobalColor::gray); | 275 | return QColor(Qt::GlobalColor::gray); |
| 275 | default: | 276 | default: |
| 276 | return WaitTreeItem::GetColor(); | 277 | return WaitTreeItem::GetColor(); |
| @@ -284,13 +285,13 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 284 | 285 | ||
| 285 | QString processor; | 286 | QString processor; |
| 286 | switch (thread.processor_id) { | 287 | switch (thread.processor_id) { |
| 287 | case ThreadProcessorId::THREADPROCESSORID_DEFAULT: | 288 | case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT: |
| 288 | processor = tr("default"); | 289 | processor = tr("default"); |
| 289 | break; | 290 | break; |
| 290 | case ThreadProcessorId::THREADPROCESSORID_0: | 291 | case Kernel::ThreadProcessorId::THREADPROCESSORID_0: |
| 291 | case ThreadProcessorId::THREADPROCESSORID_1: | 292 | case Kernel::ThreadProcessorId::THREADPROCESSORID_1: |
| 292 | case ThreadProcessorId::THREADPROCESSORID_2: | 293 | case Kernel::ThreadProcessorId::THREADPROCESSORID_2: |
| 293 | case ThreadProcessorId::THREADPROCESSORID_3: | 294 | case Kernel::ThreadProcessorId::THREADPROCESSORID_3: |
| 294 | processor = tr("core %1").arg(thread.processor_id); | 295 | processor = tr("core %1").arg(thread.processor_id); |
| 295 | break; | 296 | break; |
| 296 | default: | 297 | default: |
| @@ -314,8 +315,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 314 | else | 315 | else |
| 315 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); | 316 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); |
| 316 | 317 | ||
| 317 | if (thread.status == ThreadStatus::WaitSynchAny || | 318 | if (thread.status == Kernel::ThreadStatus::WaitSynchAny || |
| 318 | thread.status == ThreadStatus::WaitSynchAll) { | 319 | thread.status == Kernel::ThreadStatus::WaitSynchAll) { |
| 319 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, | 320 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, |
| 320 | thread.IsSleepingOnWaitAll())); | 321 | thread.IsSleepingOnWaitAll())); |
| 321 | } | 322 | } |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index d15242d59..e8b2f720a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -13,20 +13,16 @@ | |||
| 13 | #include <QKeyEvent> | 13 | #include <QKeyEvent> |
| 14 | #include <QMenu> | 14 | #include <QMenu> |
| 15 | #include <QThreadPool> | 15 | #include <QThreadPool> |
| 16 | #include <boost/container/flat_map.hpp> | ||
| 17 | #include <fmt/format.h> | 16 | #include <fmt/format.h> |
| 18 | #include "common/common_paths.h" | 17 | #include "common/common_paths.h" |
| 18 | #include "common/common_types.h" | ||
| 19 | #include "common/file_util.h" | ||
| 19 | #include "common/logging/log.h" | 20 | #include "common/logging/log.h" |
| 20 | #include "common/string_util.h" | 21 | #include "core/file_sys/patch_manager.h" |
| 21 | #include "core/file_sys/content_archive.h" | 22 | #include "yuzu/compatibility_list.h" |
| 22 | #include "core/file_sys/control_metadata.h" | ||
| 23 | #include "core/file_sys/registered_cache.h" | ||
| 24 | #include "core/file_sys/romfs.h" | ||
| 25 | #include "core/file_sys/vfs_real.h" | ||
| 26 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 27 | #include "core/loader/loader.h" | ||
| 28 | #include "yuzu/game_list.h" | 23 | #include "yuzu/game_list.h" |
| 29 | #include "yuzu/game_list_p.h" | 24 | #include "yuzu/game_list_p.h" |
| 25 | #include "yuzu/game_list_worker.h" | ||
| 30 | #include "yuzu/main.h" | 26 | #include "yuzu/main.h" |
| 31 | #include "yuzu/ui_settings.h" | 27 | #include "yuzu/ui_settings.h" |
| 32 | 28 | ||
| @@ -93,15 +89,7 @@ bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* e | |||
| 93 | } | 89 | } |
| 94 | 90 | ||
| 95 | void GameList::SearchField::setFilterResult(int visible, int total) { | 91 | void GameList::SearchField::setFilterResult(int visible, int total) { |
| 96 | QString result_of_text = tr("of"); | 92 | label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); |
| 97 | QString result_text; | ||
| 98 | if (total == 1) { | ||
| 99 | result_text = tr("result"); | ||
| 100 | } else { | ||
| 101 | result_text = tr("results"); | ||
| 102 | } | ||
| 103 | label_filter_result->setText( | ||
| 104 | QString("%1 %2 %3 %4").arg(visible).arg(result_of_text).arg(total).arg(result_text)); | ||
| 105 | } | 93 | } |
| 106 | 94 | ||
| 107 | void GameList::SearchField::clear() { | 95 | void GameList::SearchField::clear() { |
| @@ -231,6 +219,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | |||
| 231 | item_model->insertColumns(0, COLUMN_COUNT); | 219 | item_model->insertColumns(0, COLUMN_COUNT); |
| 232 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | 220 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); |
| 233 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); | 221 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); |
| 222 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); | ||
| 234 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | 223 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); |
| 235 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | 224 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); |
| 236 | 225 | ||
| @@ -369,7 +358,7 @@ void GameList::LoadCompatibilityList() { | |||
| 369 | QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); | 358 | QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); |
| 370 | QJsonArray arr = json.array(); | 359 | QJsonArray arr = json.array(); |
| 371 | 360 | ||
| 372 | for (const QJsonValue& value : arr) { | 361 | for (const QJsonValueRef& value : arr) { |
| 373 | QJsonObject game = value.toObject(); | 362 | QJsonObject game = value.toObject(); |
| 374 | 363 | ||
| 375 | if (game.contains("compatibility") && game["compatibility"].isDouble()) { | 364 | if (game.contains("compatibility") && game["compatibility"].isDouble()) { |
| @@ -377,9 +366,9 @@ void GameList::LoadCompatibilityList() { | |||
| 377 | QString directory = game["directory"].toString(); | 366 | QString directory = game["directory"].toString(); |
| 378 | QJsonArray ids = game["releases"].toArray(); | 367 | QJsonArray ids = game["releases"].toArray(); |
| 379 | 368 | ||
| 380 | for (const QJsonValue& value : ids) { | 369 | for (const QJsonValueRef& id_ref : ids) { |
| 381 | QJsonObject object = value.toObject(); | 370 | QJsonObject id_object = id_ref.toObject(); |
| 382 | QString id = object["id"].toString(); | 371 | QString id = id_object["id"].toString(); |
| 383 | compatibility_list.emplace( | 372 | compatibility_list.emplace( |
| 384 | id.toUpper().toStdString(), | 373 | id.toUpper().toStdString(), |
| 385 | std::make_pair(QString::number(compatibility), directory)); | 374 | std::make_pair(QString::number(compatibility), directory)); |
| @@ -431,27 +420,7 @@ void GameList::LoadInterfaceLayout() { | |||
| 431 | item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); | 420 | item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); |
| 432 | } | 421 | } |
| 433 | 422 | ||
| 434 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"}; | 423 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; |
| 435 | |||
| 436 | static bool HasSupportedFileExtension(const std::string& file_name) { | ||
| 437 | const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); | ||
| 438 | return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); | ||
| 439 | } | ||
| 440 | |||
| 441 | static bool IsExtractedNCAMain(const std::string& file_name) { | ||
| 442 | return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; | ||
| 443 | } | ||
| 444 | |||
| 445 | static QString FormatGameName(const std::string& physical_name) { | ||
| 446 | const QString physical_name_as_qstring = QString::fromStdString(physical_name); | ||
| 447 | const QFileInfo file_info(physical_name_as_qstring); | ||
| 448 | |||
| 449 | if (IsExtractedNCAMain(physical_name)) { | ||
| 450 | return file_info.dir().path(); | ||
| 451 | } | ||
| 452 | |||
| 453 | return physical_name_as_qstring; | ||
| 454 | } | ||
| 455 | 424 | ||
| 456 | void GameList::RefreshGameDirectory() { | 425 | void GameList::RefreshGameDirectory() { |
| 457 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { | 426 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { |
| @@ -460,175 +429,3 @@ void GameList::RefreshGameDirectory() { | |||
| 460 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 429 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); |
| 461 | } | 430 | } |
| 462 | } | 431 | } |
| 463 | |||
| 464 | static void GetMetadataFromControlNCA(const std::shared_ptr<FileSys::NCA>& nca, | ||
| 465 | std::vector<u8>& icon, std::string& name) { | ||
| 466 | const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS()); | ||
| 467 | if (control_dir == nullptr) | ||
| 468 | return; | ||
| 469 | |||
| 470 | const auto nacp_file = control_dir->GetFile("control.nacp"); | ||
| 471 | if (nacp_file == nullptr) | ||
| 472 | return; | ||
| 473 | FileSys::NACP nacp(nacp_file); | ||
| 474 | name = nacp.GetApplicationName(); | ||
| 475 | |||
| 476 | FileSys::VirtualFile icon_file = nullptr; | ||
| 477 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||
| 478 | icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat"); | ||
| 479 | if (icon_file != nullptr) { | ||
| 480 | icon = icon_file->ReadAllBytes(); | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | GameListWorker::GameListWorker( | ||
| 487 | FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, | ||
| 488 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) | ||
| 489 | : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), | ||
| 490 | compatibility_list(compatibility_list) {} | ||
| 491 | |||
| 492 | GameListWorker::~GameListWorker() = default; | ||
| 493 | |||
| 494 | void GameListWorker::AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache) { | ||
| 495 | const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application, | ||
| 496 | FileSys::ContentRecordType::Program); | ||
| 497 | |||
| 498 | for (const auto& game : installed_games) { | ||
| 499 | const auto& file = cache->GetEntryUnparsed(game); | ||
| 500 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); | ||
| 501 | if (!loader) | ||
| 502 | continue; | ||
| 503 | |||
| 504 | std::vector<u8> icon; | ||
| 505 | std::string name; | ||
| 506 | u64 program_id = 0; | ||
| 507 | loader->ReadProgramId(program_id); | ||
| 508 | |||
| 509 | const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); | ||
| 510 | if (control != nullptr) | ||
| 511 | GetMetadataFromControlNCA(control, icon, name); | ||
| 512 | emit EntryReady({ | ||
| 513 | new GameListItemPath( | ||
| 514 | FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), | ||
| 515 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), | ||
| 516 | program_id), | ||
| 517 | new GameListItem( | ||
| 518 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||
| 519 | new GameListItemSize(file->GetSize()), | ||
| 520 | }); | ||
| 521 | } | ||
| 522 | |||
| 523 | const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, | ||
| 524 | FileSys::ContentRecordType::Control); | ||
| 525 | |||
| 526 | for (const auto& entry : control_data) { | ||
| 527 | const auto nca = cache->GetEntry(entry); | ||
| 528 | if (nca != nullptr) | ||
| 529 | nca_control_map.insert_or_assign(entry.title_id, nca); | ||
| 530 | } | ||
| 531 | } | ||
| 532 | |||
| 533 | void GameListWorker::FillControlMap(const std::string& dir_path) { | ||
| 534 | const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, | ||
| 535 | const std::string& virtual_name) -> bool { | ||
| 536 | std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 537 | |||
| 538 | if (stop_processing) | ||
| 539 | return false; // Breaks the callback loop. | ||
| 540 | |||
| 541 | bool is_dir = FileUtil::IsDirectory(physical_name); | ||
| 542 | QFileInfo file_info(physical_name.c_str()); | ||
| 543 | if (!is_dir && file_info.suffix().toStdString() == "nca") { | ||
| 544 | auto nca = | ||
| 545 | std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||
| 546 | if (nca->GetType() == FileSys::NCAContentType::Control) | ||
| 547 | nca_control_map.insert_or_assign(nca->GetTitleId(), nca); | ||
| 548 | } | ||
| 549 | return true; | ||
| 550 | }; | ||
| 551 | |||
| 552 | FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); | ||
| 553 | } | ||
| 554 | |||
| 555 | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | ||
| 556 | const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, | ||
| 557 | const std::string& virtual_name) -> bool { | ||
| 558 | std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 559 | |||
| 560 | if (stop_processing) | ||
| 561 | return false; // Breaks the callback loop. | ||
| 562 | |||
| 563 | bool is_dir = FileUtil::IsDirectory(physical_name); | ||
| 564 | if (!is_dir && | ||
| 565 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | ||
| 566 | std::unique_ptr<Loader::AppLoader> loader = | ||
| 567 | Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||
| 568 | if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || | ||
| 569 | loader->GetFileType() == Loader::FileType::Error) && | ||
| 570 | !UISettings::values.show_unknown)) | ||
| 571 | return true; | ||
| 572 | |||
| 573 | std::vector<u8> icon; | ||
| 574 | const auto res1 = loader->ReadIcon(icon); | ||
| 575 | |||
| 576 | u64 program_id = 0; | ||
| 577 | const auto res2 = loader->ReadProgramId(program_id); | ||
| 578 | |||
| 579 | std::string name = " "; | ||
| 580 | const auto res3 = loader->ReadTitle(name); | ||
| 581 | |||
| 582 | if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && | ||
| 583 | res2 == Loader::ResultStatus::Success) { | ||
| 584 | // Use from metadata pool. | ||
| 585 | if (nca_control_map.find(program_id) != nca_control_map.end()) { | ||
| 586 | const auto nca = nca_control_map[program_id]; | ||
| 587 | GetMetadataFromControlNCA(nca, icon, name); | ||
| 588 | } | ||
| 589 | } | ||
| 590 | |||
| 591 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 592 | |||
| 593 | // The game list uses this as compatibility number for untested games | ||
| 594 | QString compatibility("99"); | ||
| 595 | if (it != compatibility_list.end()) | ||
| 596 | compatibility = it->second.first; | ||
| 597 | |||
| 598 | emit EntryReady({ | ||
| 599 | new GameListItemPath( | ||
| 600 | FormatGameName(physical_name), icon, QString::fromStdString(name), | ||
| 601 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), | ||
| 602 | program_id), | ||
| 603 | new GameListItemCompat(compatibility), | ||
| 604 | new GameListItem( | ||
| 605 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||
| 606 | new GameListItemSize(FileUtil::GetSize(physical_name)), | ||
| 607 | }); | ||
| 608 | } else if (is_dir && recursion > 0) { | ||
| 609 | watch_list.append(QString::fromStdString(physical_name)); | ||
| 610 | AddFstEntriesToGameList(physical_name, recursion - 1); | ||
| 611 | } | ||
| 612 | |||
| 613 | return true; | ||
| 614 | }; | ||
| 615 | |||
| 616 | FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); | ||
| 617 | } | ||
| 618 | |||
| 619 | void GameListWorker::run() { | ||
| 620 | stop_processing = false; | ||
| 621 | watch_list.append(dir_path); | ||
| 622 | FillControlMap(dir_path.toStdString()); | ||
| 623 | AddInstalledTitlesToGameList(Service::FileSystem::GetUserNANDContents()); | ||
| 624 | AddInstalledTitlesToGameList(Service::FileSystem::GetSystemNANDContents()); | ||
| 625 | AddInstalledTitlesToGameList(Service::FileSystem::GetSDMCContents()); | ||
| 626 | AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); | ||
| 627 | nca_control_map.clear(); | ||
| 628 | emit Finished(watch_list); | ||
| 629 | } | ||
| 630 | |||
| 631 | void GameListWorker::Cancel() { | ||
| 632 | this->disconnect(); | ||
| 633 | stop_processing = true; | ||
| 634 | } | ||
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 6a5c2f5f8..2713e7b54 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -4,8 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <unordered_map> | ||
| 8 | |||
| 9 | #include <QFileSystemWatcher> | 7 | #include <QFileSystemWatcher> |
| 10 | #include <QHBoxLayout> | 8 | #include <QHBoxLayout> |
| 11 | #include <QLabel> | 9 | #include <QLabel> |
| @@ -20,6 +18,9 @@ | |||
| 20 | #include <QVBoxLayout> | 18 | #include <QVBoxLayout> |
| 21 | #include <QWidget> | 19 | #include <QWidget> |
| 22 | 20 | ||
| 21 | #include "common/common_types.h" | ||
| 22 | #include "yuzu/compatibility_list.h" | ||
| 23 | |||
| 23 | class GameListWorker; | 24 | class GameListWorker; |
| 24 | class GMainWindow; | 25 | class GMainWindow; |
| 25 | 26 | ||
| @@ -36,6 +37,7 @@ public: | |||
| 36 | enum { | 37 | enum { |
| 37 | COLUMN_NAME, | 38 | COLUMN_NAME, |
| 38 | COLUMN_COMPATIBILITY, | 39 | COLUMN_COMPATIBILITY, |
| 40 | COLUMN_ADD_ONS, | ||
| 39 | COLUMN_FILE_TYPE, | 41 | COLUMN_FILE_TYPE, |
| 40 | COLUMN_SIZE, | 42 | COLUMN_SIZE, |
| 41 | COLUMN_COUNT, // Number of columns | 43 | COLUMN_COUNT, // Number of columns |
| @@ -87,9 +89,8 @@ signals: | |||
| 87 | void GameChosen(QString game_path); | 89 | void GameChosen(QString game_path); |
| 88 | void ShouldCancelWorker(); | 90 | void ShouldCancelWorker(); |
| 89 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); | 91 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); |
| 90 | void NavigateToGamedbEntryRequested( | 92 | void NavigateToGamedbEntryRequested(u64 program_id, |
| 91 | u64 program_id, | 93 | const CompatibilityList& compatibility_list); |
| 92 | std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); | ||
| 93 | 94 | ||
| 94 | private slots: | 95 | private slots: |
| 95 | void onTextChanged(const QString& newText); | 96 | void onTextChanged(const QString& newText); |
| @@ -111,7 +112,7 @@ private: | |||
| 111 | QStandardItemModel* item_model = nullptr; | 112 | QStandardItemModel* item_model = nullptr; |
| 112 | GameListWorker* current_worker = nullptr; | 113 | GameListWorker* current_worker = nullptr; |
| 113 | QFileSystemWatcher* watcher = nullptr; | 114 | QFileSystemWatcher* watcher = nullptr; |
| 114 | std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list; | 115 | CompatibilityList compatibility_list; |
| 115 | }; | 116 | }; |
| 116 | 117 | ||
| 117 | Q_DECLARE_METATYPE(GameListOpenTarget); | 118 | Q_DECLARE_METATYPE(GameListOpenTarget); |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 3624cb21a..b6272d536 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -4,29 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 7 | #include <array> | 8 | #include <array> |
| 8 | #include <atomic> | ||
| 9 | #include <map> | 9 | #include <map> |
| 10 | #include <memory> | 10 | #include <string> |
| 11 | #include <unordered_map> | 11 | #include <unordered_map> |
| 12 | #include <utility> | 12 | #include <utility> |
| 13 | |||
| 13 | #include <QCoreApplication> | 14 | #include <QCoreApplication> |
| 14 | #include <QImage> | 15 | #include <QImage> |
| 15 | #include <QObject> | 16 | #include <QObject> |
| 16 | #include <QRunnable> | ||
| 17 | #include <QStandardItem> | 17 | #include <QStandardItem> |
| 18 | #include <QString> | 18 | #include <QString> |
| 19 | |||
| 20 | #include "common/common_types.h" | ||
| 19 | #include "common/logging/log.h" | 21 | #include "common/logging/log.h" |
| 20 | #include "common/string_util.h" | 22 | #include "common/string_util.h" |
| 21 | #include "yuzu/ui_settings.h" | 23 | #include "yuzu/ui_settings.h" |
| 22 | #include "yuzu/util/util.h" | 24 | #include "yuzu/util/util.h" |
| 23 | 25 | ||
| 24 | namespace FileSys { | ||
| 25 | class NCA; | ||
| 26 | class RegisteredCache; | ||
| 27 | class VfsFilesystem; | ||
| 28 | } // namespace FileSys | ||
| 29 | |||
| 30 | /** | 26 | /** |
| 31 | * Gets the default icon (for games without valid SMDH) | 27 | * Gets the default icon (for games without valid SMDH) |
| 32 | * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | 28 | * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) |
| @@ -38,17 +34,6 @@ static QPixmap GetDefaultIcon(u32 size) { | |||
| 38 | return icon; | 34 | return icon; |
| 39 | } | 35 | } |
| 40 | 36 | ||
| 41 | static auto FindMatchingCompatibilityEntry( | ||
| 42 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list, | ||
| 43 | u64 program_id) { | ||
| 44 | return std::find_if( | ||
| 45 | compatibility_list.begin(), compatibility_list.end(), | ||
| 46 | [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) { | ||
| 47 | std::string pid = fmt::format("{:016X}", program_id); | ||
| 48 | return element.first == pid; | ||
| 49 | }); | ||
| 50 | } | ||
| 51 | |||
| 52 | class GameListItem : public QStandardItem { | 37 | class GameListItem : public QStandardItem { |
| 53 | 38 | ||
| 54 | public: | 39 | public: |
| @@ -121,7 +106,7 @@ class GameListItemCompat : public GameListItem { | |||
| 121 | public: | 106 | public: |
| 122 | static const int CompatNumberRole = Qt::UserRole + 1; | 107 | static const int CompatNumberRole = Qt::UserRole + 1; |
| 123 | GameListItemCompat() = default; | 108 | GameListItemCompat() = default; |
| 124 | explicit GameListItemCompat(const QString& compatiblity) { | 109 | explicit GameListItemCompat(const QString& compatibility) { |
| 125 | struct CompatStatus { | 110 | struct CompatStatus { |
| 126 | QString color; | 111 | QString color; |
| 127 | const char* text; | 112 | const char* text; |
| @@ -138,13 +123,13 @@ public: | |||
| 138 | {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; | 123 | {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; |
| 139 | // clang-format on | 124 | // clang-format on |
| 140 | 125 | ||
| 141 | auto iterator = status_data.find(compatiblity); | 126 | auto iterator = status_data.find(compatibility); |
| 142 | if (iterator == status_data.end()) { | 127 | if (iterator == status_data.end()) { |
| 143 | LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); | 128 | LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString()); |
| 144 | return; | 129 | return; |
| 145 | } | 130 | } |
| 146 | CompatStatus status = iterator->second; | 131 | const CompatStatus& status = iterator->second; |
| 147 | setData(compatiblity, CompatNumberRole); | 132 | setData(compatibility, CompatNumberRole); |
| 148 | setText(QObject::tr(status.text)); | 133 | setText(QObject::tr(status.text)); |
| 149 | setToolTip(QObject::tr(status.tooltip)); | 134 | setToolTip(QObject::tr(status.tooltip)); |
| 150 | setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); | 135 | setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); |
| @@ -191,50 +176,3 @@ public: | |||
| 191 | return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); | 176 | return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); |
| 192 | } | 177 | } |
| 193 | }; | 178 | }; |
| 194 | |||
| 195 | /** | ||
| 196 | * Asynchronous worker object for populating the game list. | ||
| 197 | * Communicates with other threads through Qt's signal/slot system. | ||
| 198 | */ | ||
| 199 | class GameListWorker : public QObject, public QRunnable { | ||
| 200 | Q_OBJECT | ||
| 201 | |||
| 202 | public: | ||
| 203 | GameListWorker( | ||
| 204 | std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, | ||
| 205 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); | ||
| 206 | ~GameListWorker() override; | ||
| 207 | |||
| 208 | public slots: | ||
| 209 | /// Starts the processing of directory tree information. | ||
| 210 | void run() override; | ||
| 211 | /// Tells the worker that it should no longer continue processing. Thread-safe. | ||
| 212 | void Cancel(); | ||
| 213 | |||
| 214 | signals: | ||
| 215 | /** | ||
| 216 | * The `EntryReady` signal is emitted once an entry has been prepared and is ready | ||
| 217 | * to be added to the game list. | ||
| 218 | * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. | ||
| 219 | */ | ||
| 220 | void EntryReady(QList<QStandardItem*> entry_items); | ||
| 221 | |||
| 222 | /** | ||
| 223 | * After the worker has traversed the game directory looking for entries, this signal is emmited | ||
| 224 | * with a list of folders that should be watched for changes as well. | ||
| 225 | */ | ||
| 226 | void Finished(QStringList watch_list); | ||
| 227 | |||
| 228 | private: | ||
| 229 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | ||
| 230 | std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; | ||
| 231 | QStringList watch_list; | ||
| 232 | QString dir_path; | ||
| 233 | bool deep_scan; | ||
| 234 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list; | ||
| 235 | std::atomic_bool stop_processing; | ||
| 236 | |||
| 237 | void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache); | ||
| 238 | void FillControlMap(const std::string& dir_path); | ||
| 239 | void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); | ||
| 240 | }; | ||
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp new file mode 100644 index 000000000..e228d61bd --- /dev/null +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -0,0 +1,239 @@ | |||
| 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 <memory> | ||
| 6 | #include <string> | ||
| 7 | #include <utility> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include <QDir> | ||
| 11 | #include <QFileInfo> | ||
| 12 | |||
| 13 | #include "common/common_paths.h" | ||
| 14 | #include "common/file_util.h" | ||
| 15 | #include "core/file_sys/content_archive.h" | ||
| 16 | #include "core/file_sys/control_metadata.h" | ||
| 17 | #include "core/file_sys/mode.h" | ||
| 18 | #include "core/file_sys/nca_metadata.h" | ||
| 19 | #include "core/file_sys/patch_manager.h" | ||
| 20 | #include "core/file_sys/registered_cache.h" | ||
| 21 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 22 | #include "core/loader/loader.h" | ||
| 23 | #include "yuzu/compatibility_list.h" | ||
| 24 | #include "yuzu/game_list.h" | ||
| 25 | #include "yuzu/game_list_p.h" | ||
| 26 | #include "yuzu/game_list_worker.h" | ||
| 27 | #include "yuzu/ui_settings.h" | ||
| 28 | |||
| 29 | namespace { | ||
| 30 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, | ||
| 31 | const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, | ||
| 32 | std::string& name) { | ||
| 33 | auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); | ||
| 34 | if (icon_file != nullptr) | ||
| 35 | icon = icon_file->ReadAllBytes(); | ||
| 36 | if (nacp != nullptr) | ||
| 37 | name = nacp->GetApplicationName(); | ||
| 38 | } | ||
| 39 | |||
| 40 | bool HasSupportedFileExtension(const std::string& file_name) { | ||
| 41 | const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); | ||
| 42 | return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); | ||
| 43 | } | ||
| 44 | |||
| 45 | bool IsExtractedNCAMain(const std::string& file_name) { | ||
| 46 | return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; | ||
| 47 | } | ||
| 48 | |||
| 49 | QString FormatGameName(const std::string& physical_name) { | ||
| 50 | const QString physical_name_as_qstring = QString::fromStdString(physical_name); | ||
| 51 | const QFileInfo file_info(physical_name_as_qstring); | ||
| 52 | |||
| 53 | if (IsExtractedNCAMain(physical_name)) { | ||
| 54 | return file_info.dir().path(); | ||
| 55 | } | ||
| 56 | |||
| 57 | return physical_name_as_qstring; | ||
| 58 | } | ||
| 59 | |||
| 60 | QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { | ||
| 61 | QString out; | ||
| 62 | for (const auto& kv : patch_manager.GetPatchVersionNames()) { | ||
| 63 | if (!updatable && kv.first == FileSys::PatchType::Update) | ||
| 64 | continue; | ||
| 65 | |||
| 66 | if (kv.second.empty()) { | ||
| 67 | out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); | ||
| 68 | } else { | ||
| 69 | out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second) | ||
| 70 | .c_str()); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | out.chop(1); | ||
| 75 | return out; | ||
| 76 | } | ||
| 77 | } // Anonymous namespace | ||
| 78 | |||
| 79 | GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, | ||
| 80 | const CompatibilityList& compatibility_list) | ||
| 81 | : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), | ||
| 82 | compatibility_list(compatibility_list) {} | ||
| 83 | |||
| 84 | GameListWorker::~GameListWorker() = default; | ||
| 85 | |||
| 86 | void GameListWorker::AddInstalledTitlesToGameList() { | ||
| 87 | const auto cache = Service::FileSystem::GetUnionContents(); | ||
| 88 | const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application, | ||
| 89 | FileSys::ContentRecordType::Program); | ||
| 90 | |||
| 91 | for (const auto& game : installed_games) { | ||
| 92 | const auto& file = cache->GetEntryUnparsed(game); | ||
| 93 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); | ||
| 94 | if (!loader) | ||
| 95 | continue; | ||
| 96 | |||
| 97 | std::vector<u8> icon; | ||
| 98 | std::string name; | ||
| 99 | u64 program_id = 0; | ||
| 100 | loader->ReadProgramId(program_id); | ||
| 101 | |||
| 102 | const FileSys::PatchManager patch{program_id}; | ||
| 103 | const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); | ||
| 104 | if (control != nullptr) | ||
| 105 | GetMetadataFromControlNCA(patch, control, icon, name); | ||
| 106 | |||
| 107 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 108 | |||
| 109 | // The game list uses this as compatibility number for untested games | ||
| 110 | QString compatibility("99"); | ||
| 111 | if (it != compatibility_list.end()) | ||
| 112 | compatibility = it->second.first; | ||
| 113 | |||
| 114 | emit EntryReady({ | ||
| 115 | new GameListItemPath( | ||
| 116 | FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), | ||
| 117 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), | ||
| 118 | program_id), | ||
| 119 | new GameListItemCompat(compatibility), | ||
| 120 | new GameListItem(FormatPatchNameVersions(patch)), | ||
| 121 | new GameListItem( | ||
| 122 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||
| 123 | new GameListItemSize(file->GetSize()), | ||
| 124 | }); | ||
| 125 | } | ||
| 126 | |||
| 127 | const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, | ||
| 128 | FileSys::ContentRecordType::Control); | ||
| 129 | |||
| 130 | for (const auto& entry : control_data) { | ||
| 131 | const auto nca = cache->GetEntry(entry); | ||
| 132 | if (nca != nullptr) | ||
| 133 | nca_control_map.insert_or_assign(entry.title_id, nca); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | void GameListWorker::FillControlMap(const std::string& dir_path) { | ||
| 138 | const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, | ||
| 139 | const std::string& virtual_name) -> bool { | ||
| 140 | std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 141 | |||
| 142 | if (stop_processing) | ||
| 143 | return false; // Breaks the callback loop. | ||
| 144 | |||
| 145 | bool is_dir = FileUtil::IsDirectory(physical_name); | ||
| 146 | QFileInfo file_info(physical_name.c_str()); | ||
| 147 | if (!is_dir && file_info.suffix().toStdString() == "nca") { | ||
| 148 | auto nca = | ||
| 149 | std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||
| 150 | if (nca->GetType() == FileSys::NCAContentType::Control) | ||
| 151 | nca_control_map.insert_or_assign(nca->GetTitleId(), nca); | ||
| 152 | } | ||
| 153 | return true; | ||
| 154 | }; | ||
| 155 | |||
| 156 | FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); | ||
| 157 | } | ||
| 158 | |||
| 159 | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | ||
| 160 | const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, | ||
| 161 | const std::string& virtual_name) -> bool { | ||
| 162 | std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 163 | |||
| 164 | if (stop_processing) | ||
| 165 | return false; // Breaks the callback loop. | ||
| 166 | |||
| 167 | bool is_dir = FileUtil::IsDirectory(physical_name); | ||
| 168 | if (!is_dir && | ||
| 169 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | ||
| 170 | std::unique_ptr<Loader::AppLoader> loader = | ||
| 171 | Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | ||
| 172 | if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || | ||
| 173 | loader->GetFileType() == Loader::FileType::Error) && | ||
| 174 | !UISettings::values.show_unknown)) | ||
| 175 | return true; | ||
| 176 | |||
| 177 | std::vector<u8> icon; | ||
| 178 | const auto res1 = loader->ReadIcon(icon); | ||
| 179 | |||
| 180 | u64 program_id = 0; | ||
| 181 | const auto res2 = loader->ReadProgramId(program_id); | ||
| 182 | |||
| 183 | std::string name = " "; | ||
| 184 | const auto res3 = loader->ReadTitle(name); | ||
| 185 | |||
| 186 | const FileSys::PatchManager patch{program_id}; | ||
| 187 | |||
| 188 | if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && | ||
| 189 | res2 == Loader::ResultStatus::Success) { | ||
| 190 | // Use from metadata pool. | ||
| 191 | if (nca_control_map.find(program_id) != nca_control_map.end()) { | ||
| 192 | const auto nca = nca_control_map[program_id]; | ||
| 193 | GetMetadataFromControlNCA(patch, nca, icon, name); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 198 | |||
| 199 | // The game list uses this as compatibility number for untested games | ||
| 200 | QString compatibility("99"); | ||
| 201 | if (it != compatibility_list.end()) | ||
| 202 | compatibility = it->second.first; | ||
| 203 | |||
| 204 | emit EntryReady({ | ||
| 205 | new GameListItemPath( | ||
| 206 | FormatGameName(physical_name), icon, QString::fromStdString(name), | ||
| 207 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), | ||
| 208 | program_id), | ||
| 209 | new GameListItemCompat(compatibility), | ||
| 210 | new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())), | ||
| 211 | new GameListItem( | ||
| 212 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||
| 213 | new GameListItemSize(FileUtil::GetSize(physical_name)), | ||
| 214 | }); | ||
| 215 | } else if (is_dir && recursion > 0) { | ||
| 216 | watch_list.append(QString::fromStdString(physical_name)); | ||
| 217 | AddFstEntriesToGameList(physical_name, recursion - 1); | ||
| 218 | } | ||
| 219 | |||
| 220 | return true; | ||
| 221 | }; | ||
| 222 | |||
| 223 | FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); | ||
| 224 | } | ||
| 225 | |||
| 226 | void GameListWorker::run() { | ||
| 227 | stop_processing = false; | ||
| 228 | watch_list.append(dir_path); | ||
| 229 | FillControlMap(dir_path.toStdString()); | ||
| 230 | AddInstalledTitlesToGameList(); | ||
| 231 | AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); | ||
| 232 | nca_control_map.clear(); | ||
| 233 | emit Finished(watch_list); | ||
| 234 | } | ||
| 235 | |||
| 236 | void GameListWorker::Cancel() { | ||
| 237 | this->disconnect(); | ||
| 238 | stop_processing = true; | ||
| 239 | } | ||
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h new file mode 100644 index 000000000..09d20c42f --- /dev/null +++ b/src/yuzu/game_list_worker.h | |||
| @@ -0,0 +1,72 @@ | |||
| 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 <atomic> | ||
| 8 | #include <map> | ||
| 9 | #include <memory> | ||
| 10 | #include <string> | ||
| 11 | #include <unordered_map> | ||
| 12 | |||
| 13 | #include <QList> | ||
| 14 | #include <QObject> | ||
| 15 | #include <QRunnable> | ||
| 16 | #include <QString> | ||
| 17 | |||
| 18 | #include "common/common_types.h" | ||
| 19 | #include "yuzu/compatibility_list.h" | ||
| 20 | |||
| 21 | class QStandardItem; | ||
| 22 | |||
| 23 | namespace FileSys { | ||
| 24 | class NCA; | ||
| 25 | class VfsFilesystem; | ||
| 26 | } // namespace FileSys | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Asynchronous worker object for populating the game list. | ||
| 30 | * Communicates with other threads through Qt's signal/slot system. | ||
| 31 | */ | ||
| 32 | class GameListWorker : public QObject, public QRunnable { | ||
| 33 | Q_OBJECT | ||
| 34 | |||
| 35 | public: | ||
| 36 | GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, | ||
| 37 | const CompatibilityList& compatibility_list); | ||
| 38 | ~GameListWorker() override; | ||
| 39 | |||
| 40 | /// Starts the processing of directory tree information. | ||
| 41 | void run() override; | ||
| 42 | |||
| 43 | /// Tells the worker that it should no longer continue processing. Thread-safe. | ||
| 44 | void Cancel(); | ||
| 45 | |||
| 46 | signals: | ||
| 47 | /** | ||
| 48 | * The `EntryReady` signal is emitted once an entry has been prepared and is ready | ||
| 49 | * to be added to the game list. | ||
| 50 | * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. | ||
| 51 | */ | ||
| 52 | void EntryReady(QList<QStandardItem*> entry_items); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * After the worker has traversed the game directory looking for entries, this signal is emitted | ||
| 56 | * with a list of folders that should be watched for changes as well. | ||
| 57 | */ | ||
| 58 | void Finished(QStringList watch_list); | ||
| 59 | |||
| 60 | private: | ||
| 61 | void AddInstalledTitlesToGameList(); | ||
| 62 | void FillControlMap(const std::string& dir_path); | ||
| 63 | void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); | ||
| 64 | |||
| 65 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | ||
| 66 | std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; | ||
| 67 | QStringList watch_list; | ||
| 68 | QString dir_path; | ||
| 69 | bool deep_scan; | ||
| 70 | const CompatibilityList& compatibility_list; | ||
| 71 | std::atomic_bool stop_processing; | ||
| 72 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e11ba7854..45bb1d1d1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -12,12 +12,14 @@ | |||
| 12 | 12 | ||
| 13 | #define QT_NO_OPENGL | 13 | #define QT_NO_OPENGL |
| 14 | #include <QDesktopWidget> | 14 | #include <QDesktopWidget> |
| 15 | #include <QDialogButtonBox> | ||
| 15 | #include <QFileDialog> | 16 | #include <QFileDialog> |
| 16 | #include <QMessageBox> | 17 | #include <QMessageBox> |
| 17 | #include <QtGui> | 18 | #include <QtGui> |
| 18 | #include <QtWidgets> | 19 | #include <QtWidgets> |
| 19 | #include <fmt/format.h> | 20 | #include <fmt/format.h> |
| 20 | #include "common/common_paths.h" | 21 | #include "common/common_paths.h" |
| 22 | #include "common/file_util.h" | ||
| 21 | #include "common/logging/backend.h" | 23 | #include "common/logging/backend.h" |
| 22 | #include "common/logging/filter.h" | 24 | #include "common/logging/filter.h" |
| 23 | #include "common/logging/log.h" | 25 | #include "common/logging/log.h" |
| @@ -29,9 +31,14 @@ | |||
| 29 | #include "core/core.h" | 31 | #include "core/core.h" |
| 30 | #include "core/crypto/key_manager.h" | 32 | #include "core/crypto/key_manager.h" |
| 31 | #include "core/file_sys/card_image.h" | 33 | #include "core/file_sys/card_image.h" |
| 34 | #include "core/file_sys/content_archive.h" | ||
| 35 | #include "core/file_sys/control_metadata.h" | ||
| 36 | #include "core/file_sys/patch_manager.h" | ||
| 32 | #include "core/file_sys/registered_cache.h" | 37 | #include "core/file_sys/registered_cache.h" |
| 33 | #include "core/file_sys/savedata_factory.h" | 38 | #include "core/file_sys/savedata_factory.h" |
| 39 | #include "core/file_sys/submission_package.h" | ||
| 34 | #include "core/file_sys/vfs_real.h" | 40 | #include "core/file_sys/vfs_real.h" |
| 41 | #include "core/hle/kernel/process.h" | ||
| 35 | #include "core/hle/service/filesystem/filesystem.h" | 42 | #include "core/hle/service/filesystem/filesystem.h" |
| 36 | #include "core/loader/loader.h" | 43 | #include "core/loader/loader.h" |
| 37 | #include "core/perf_stats.h" | 44 | #include "core/perf_stats.h" |
| @@ -40,6 +47,7 @@ | |||
| 40 | #include "video_core/debug_utils/debug_utils.h" | 47 | #include "video_core/debug_utils/debug_utils.h" |
| 41 | #include "yuzu/about_dialog.h" | 48 | #include "yuzu/about_dialog.h" |
| 42 | #include "yuzu/bootmanager.h" | 49 | #include "yuzu/bootmanager.h" |
| 50 | #include "yuzu/compatibility_list.h" | ||
| 43 | #include "yuzu/configuration/config.h" | 51 | #include "yuzu/configuration/config.h" |
| 44 | #include "yuzu/configuration/configure_dialog.h" | 52 | #include "yuzu/configuration/configure_dialog.h" |
| 45 | #include "yuzu/debugger/console.h" | 53 | #include "yuzu/debugger/console.h" |
| @@ -73,6 +81,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 73 | */ | 81 | */ |
| 74 | enum class CalloutFlag : uint32_t { | 82 | enum class CalloutFlag : uint32_t { |
| 75 | Telemetry = 0x1, | 83 | Telemetry = 0x1, |
| 84 | DRDDeprecation = 0x2, | ||
| 76 | }; | 85 | }; |
| 77 | 86 | ||
| 78 | static void ShowCalloutMessage(const QString& message, CalloutFlag flag) { | 87 | static void ShowCalloutMessage(const QString& message, CalloutFlag flag) { |
| @@ -128,11 +137,11 @@ GMainWindow::GMainWindow() | |||
| 128 | 137 | ||
| 129 | ConnectMenuEvents(); | 138 | ConnectMenuEvents(); |
| 130 | ConnectWidgetEvents(); | 139 | ConnectWidgetEvents(); |
| 131 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_name, Common::g_scm_branch, | 140 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, |
| 132 | Common::g_scm_desc); | 141 | Common::g_scm_desc); |
| 133 | 142 | ||
| 134 | setWindowTitle(QString("yuzu %1| %2-%3") | 143 | setWindowTitle(QString("yuzu %1| %2-%3") |
| 135 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | 144 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); |
| 136 | show(); | 145 | show(); |
| 137 | 146 | ||
| 138 | // Necessary to load titles from nand in gamelist. | 147 | // Necessary to load titles from nand in gamelist. |
| @@ -372,6 +381,10 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 372 | &GMainWindow::OnMenuInstallToNAND); | 381 | &GMainWindow::OnMenuInstallToNAND); |
| 373 | connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, | 382 | connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, |
| 374 | &GMainWindow::OnMenuSelectGameListRoot); | 383 | &GMainWindow::OnMenuSelectGameListRoot); |
| 384 | connect(ui.action_Select_NAND_Directory, &QAction::triggered, this, | ||
| 385 | [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::NAND); }); | ||
| 386 | connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, | ||
| 387 | [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); | ||
| 375 | connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); | 388 | connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); |
| 376 | 389 | ||
| 377 | // Emulation | 390 | // Emulation |
| @@ -419,7 +432,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) { | |||
| 419 | } | 432 | } |
| 420 | } | 433 | } |
| 421 | 434 | ||
| 422 | bool GMainWindow::SupportsRequiredGLExtensions() { | 435 | QStringList GMainWindow::GetUnsupportedGLExtensions() { |
| 423 | QStringList unsupported_ext; | 436 | QStringList unsupported_ext; |
| 424 | 437 | ||
| 425 | if (!GLAD_GL_ARB_program_interface_query) | 438 | if (!GLAD_GL_ARB_program_interface_query) |
| @@ -432,6 +445,12 @@ bool GMainWindow::SupportsRequiredGLExtensions() { | |||
| 432 | unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); | 445 | unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); |
| 433 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | 446 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) |
| 434 | unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); | 447 | unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); |
| 448 | if (!GLAD_GL_ARB_base_instance) | ||
| 449 | unsupported_ext.append("ARB_base_instance"); | ||
| 450 | if (!GLAD_GL_ARB_texture_storage) | ||
| 451 | unsupported_ext.append("ARB_texture_storage"); | ||
| 452 | if (!GLAD_GL_ARB_multi_bind) | ||
| 453 | unsupported_ext.append("ARB_multi_bind"); | ||
| 435 | 454 | ||
| 436 | // Extensions required to support some texture formats. | 455 | // Extensions required to support some texture formats. |
| 437 | if (!GLAD_GL_EXT_texture_compression_s3tc) | 456 | if (!GLAD_GL_EXT_texture_compression_s3tc) |
| @@ -446,7 +465,7 @@ bool GMainWindow::SupportsRequiredGLExtensions() { | |||
| 446 | for (const QString& ext : unsupported_ext) | 465 | for (const QString& ext : unsupported_ext) |
| 447 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | 466 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); |
| 448 | 467 | ||
| 449 | return unsupported_ext.empty(); | 468 | return unsupported_ext; |
| 450 | } | 469 | } |
| 451 | 470 | ||
| 452 | bool GMainWindow::LoadROM(const QString& filename) { | 471 | bool GMainWindow::LoadROM(const QString& filename) { |
| @@ -464,11 +483,13 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 464 | return false; | 483 | return false; |
| 465 | } | 484 | } |
| 466 | 485 | ||
| 467 | if (!SupportsRequiredGLExtensions()) { | 486 | QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); |
| 468 | QMessageBox::critical( | 487 | if (!unsupported_gl_extensions.empty()) { |
| 469 | this, tr("Error while initializing OpenGL Core!"), | 488 | QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), |
| 470 | tr("Your GPU may not support one or more required OpenGL extensions. Please " | 489 | tr("Your GPU may not support one or more required OpenGL" |
| 471 | "ensure you have the latest graphics driver. See the log for more details.")); | 490 | "extensions. Please ensure you have the latest graphics " |
| 491 | "driver.<br><br>Unsupported extensions:<br>") + | ||
| 492 | unsupported_gl_extensions.join("<br>")); | ||
| 472 | return false; | 493 | return false; |
| 473 | } | 494 | } |
| 474 | 495 | ||
| @@ -479,6 +500,23 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 479 | 500 | ||
| 480 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | 501 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; |
| 481 | 502 | ||
| 503 | const auto drd_callout = | ||
| 504 | (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; | ||
| 505 | |||
| 506 | if (result == Core::System::ResultStatus::Success && | ||
| 507 | system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory && | ||
| 508 | drd_callout) { | ||
| 509 | UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation); | ||
| 510 | QMessageBox::warning( | ||
| 511 | this, tr("Warning Outdated Game Format"), | ||
| 512 | tr("You are using the deconstructed ROM directory format for this game, which is an " | ||
| 513 | "outdated format that has been superseded by others such as NCA, NAX, XCI, or " | ||
| 514 | "NSP. Deconstructed ROM directories lack icons, metadata, and update " | ||
| 515 | "support.<br><br>For an explanation of the various Switch formats yuzu supports, <a " | ||
| 516 | "href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our " | ||
| 517 | "wiki</a>. This message will not be shown again.")); | ||
| 518 | } | ||
| 519 | |||
| 482 | render_window->DoneCurrent(); | 520 | render_window->DoneCurrent(); |
| 483 | 521 | ||
| 484 | if (result != Core::System::ResultStatus::Success) { | 522 | if (result != Core::System::ResultStatus::Success) { |
| @@ -563,11 +601,19 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 563 | 601 | ||
| 564 | std::string title_name; | 602 | std::string title_name; |
| 565 | const auto res = Core::System::GetInstance().GetGameName(title_name); | 603 | const auto res = Core::System::GetInstance().GetGameName(title_name); |
| 566 | if (res != Loader::ResultStatus::Success) | 604 | if (res != Loader::ResultStatus::Success) { |
| 567 | title_name = FileUtil::GetFilename(filename.toStdString()); | 605 | const u64 program_id = Core::System::GetInstance().CurrentProcess()->program_id; |
| 606 | |||
| 607 | const auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); | ||
| 608 | if (nacp != nullptr) | ||
| 609 | title_name = nacp->GetApplicationName(); | ||
| 610 | |||
| 611 | if (title_name.empty()) | ||
| 612 | title_name = FileUtil::GetFilename(filename.toStdString()); | ||
| 613 | } | ||
| 568 | 614 | ||
| 569 | setWindowTitle(QString("yuzu %1| %4 | %2-%3") | 615 | setWindowTitle(QString("yuzu %1| %4 | %2-%3") |
| 570 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc, | 616 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, |
| 571 | QString::fromStdString(title_name))); | 617 | QString::fromStdString(title_name))); |
| 572 | 618 | ||
| 573 | render_window->show(); | 619 | render_window->show(); |
| @@ -602,7 +648,7 @@ void GMainWindow::ShutdownGame() { | |||
| 602 | game_list->show(); | 648 | game_list->show(); |
| 603 | game_list->setFilterFocus(); | 649 | game_list->setFilterFocus(); |
| 604 | setWindowTitle(QString("yuzu %1| %2-%3") | 650 | setWindowTitle(QString("yuzu %1| %2-%3") |
| 605 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | 651 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); |
| 606 | 652 | ||
| 607 | // Disable status bar updates | 653 | // Disable status bar updates |
| 608 | status_bar_update_timer.stop(); | 654 | status_bar_update_timer.stop(); |
| @@ -684,14 +730,11 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 684 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | 730 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); |
| 685 | } | 731 | } |
| 686 | 732 | ||
| 687 | void GMainWindow::OnGameListNavigateToGamedbEntry( | 733 | void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, |
| 688 | u64 program_id, | 734 | const CompatibilityList& compatibility_list) { |
| 689 | std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) { | 735 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 690 | |||
| 691 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 692 | 736 | ||
| 693 | QString directory; | 737 | QString directory; |
| 694 | |||
| 695 | if (it != compatibility_list.end()) | 738 | if (it != compatibility_list.end()) |
| 696 | directory = it->second.second; | 739 | directory = it->second.second; |
| 697 | 740 | ||
| @@ -737,7 +780,8 @@ void GMainWindow::OnMenuLoadFolder() { | |||
| 737 | 780 | ||
| 738 | void GMainWindow::OnMenuInstallToNAND() { | 781 | void GMainWindow::OnMenuInstallToNAND() { |
| 739 | const QString file_filter = | 782 | const QString file_filter = |
| 740 | tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge " | 783 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " |
| 784 | "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge " | ||
| 741 | "Image (*.xci)"); | 785 | "Image (*.xci)"); |
| 742 | QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), | 786 | QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), |
| 743 | UISettings::values.roms_path, file_filter); | 787 | UISettings::values.roms_path, file_filter); |
| @@ -760,7 +804,7 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 760 | tr("Cancel"), 0, progress_maximum, this); | 804 | tr("Cancel"), 0, progress_maximum, this); |
| 761 | progress.setWindowModality(Qt::WindowModal); | 805 | progress.setWindowModality(Qt::WindowModal); |
| 762 | 806 | ||
| 763 | for (size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 807 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 764 | if (progress.wasCanceled()) { | 808 | if (progress.wasCanceled()) { |
| 765 | dest->Resize(0); | 809 | dest->Resize(0); |
| 766 | return false; | 810 | return false; |
| @@ -797,22 +841,34 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 797 | QMessageBox::Yes; | 841 | QMessageBox::Yes; |
| 798 | }; | 842 | }; |
| 799 | 843 | ||
| 800 | if (filename.endsWith("xci", Qt::CaseInsensitive)) { | 844 | if (filename.endsWith("xci", Qt::CaseInsensitive) || |
| 801 | const auto xci = std::make_shared<FileSys::XCI>( | 845 | filename.endsWith("nsp", Qt::CaseInsensitive)) { |
| 802 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 846 | |
| 803 | if (xci->GetStatus() != Loader::ResultStatus::Success) { | 847 | std::shared_ptr<FileSys::NSP> nsp; |
| 848 | if (filename.endsWith("nsp", Qt::CaseInsensitive)) { | ||
| 849 | nsp = std::make_shared<FileSys::NSP>( | ||
| 850 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 851 | if (nsp->IsExtractedType()) | ||
| 852 | failed(); | ||
| 853 | } else { | ||
| 854 | const auto xci = std::make_shared<FileSys::XCI>( | ||
| 855 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 856 | nsp = xci->GetSecurePartitionNSP(); | ||
| 857 | } | ||
| 858 | |||
| 859 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { | ||
| 804 | failed(); | 860 | failed(); |
| 805 | return; | 861 | return; |
| 806 | } | 862 | } |
| 807 | const auto res = | 863 | const auto res = |
| 808 | Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy); | 864 | Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy); |
| 809 | if (res == FileSys::InstallResult::Success) { | 865 | if (res == FileSys::InstallResult::Success) { |
| 810 | success(); | 866 | success(); |
| 811 | } else { | 867 | } else { |
| 812 | if (res == FileSys::InstallResult::ErrorAlreadyExists) { | 868 | if (res == FileSys::InstallResult::ErrorAlreadyExists) { |
| 813 | if (overwrite()) { | 869 | if (overwrite()) { |
| 814 | const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( | 870 | const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( |
| 815 | xci, true, qt_raw_copy); | 871 | nsp, true, qt_raw_copy); |
| 816 | if (res2 == FileSys::InstallResult::Success) { | 872 | if (res2 == FileSys::InstallResult::Success) { |
| 817 | success(); | 873 | success(); |
| 818 | } else { | 874 | } else { |
| @@ -826,7 +882,11 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 826 | } else { | 882 | } else { |
| 827 | const auto nca = std::make_shared<FileSys::NCA>( | 883 | const auto nca = std::make_shared<FileSys::NCA>( |
| 828 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 884 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 829 | if (nca->GetStatus() != Loader::ResultStatus::Success) { | 885 | const auto id = nca->GetStatus(); |
| 886 | |||
| 887 | // Game updates necessary are missing base RomFS | ||
| 888 | if (id != Loader::ResultStatus::Success && | ||
| 889 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 830 | failed(); | 890 | failed(); |
| 831 | return; | 891 | return; |
| 832 | } | 892 | } |
| @@ -885,6 +945,28 @@ void GMainWindow::OnMenuSelectGameListRoot() { | |||
| 885 | } | 945 | } |
| 886 | } | 946 | } |
| 887 | 947 | ||
| 948 | void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) { | ||
| 949 | const auto res = QMessageBox::information( | ||
| 950 | this, tr("Changing Emulated Directory"), | ||
| 951 | tr("You are about to change the emulated %1 directory of the system. Please note " | ||
| 952 | "that this does not also move the contents of the previous directory to the " | ||
| 953 | "new one and you will have to do that yourself.") | ||
| 954 | .arg(target == EmulatedDirectoryTarget::SDMC ? tr("SD card") : tr("NAND")), | ||
| 955 | QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel}); | ||
| 956 | |||
| 957 | if (res == QMessageBox::Cancel) | ||
| 958 | return; | ||
| 959 | |||
| 960 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); | ||
| 961 | if (!dir_path.isEmpty()) { | ||
| 962 | FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir | ||
| 963 | : FileUtil::UserPath::NANDDir, | ||
| 964 | dir_path.toStdString()); | ||
| 965 | Service::FileSystem::CreateFactories(vfs); | ||
| 966 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | ||
| 967 | } | ||
| 968 | } | ||
| 969 | |||
| 888 | void GMainWindow::OnMenuRecentFile() { | 970 | void GMainWindow::OnMenuRecentFile() { |
| 889 | QAction* action = qobject_cast<QAction*>(sender()); | 971 | QAction* action = qobject_cast<QAction*>(sender()); |
| 890 | assert(action); | 972 | assert(action); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 0b97e8220..552e3e61c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -6,10 +6,14 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <unordered_map> | 8 | #include <unordered_map> |
| 9 | |||
| 9 | #include <QMainWindow> | 10 | #include <QMainWindow> |
| 10 | #include <QTimer> | 11 | #include <QTimer> |
| 12 | |||
| 13 | #include "common/common_types.h" | ||
| 11 | #include "core/core.h" | 14 | #include "core/core.h" |
| 12 | #include "ui_main.h" | 15 | #include "ui_main.h" |
| 16 | #include "yuzu/compatibility_list.h" | ||
| 13 | #include "yuzu/hotkeys.h" | 17 | #include "yuzu/hotkeys.h" |
| 14 | 18 | ||
| 15 | class Config; | 19 | class Config; |
| @@ -32,6 +36,11 @@ namespace Tegra { | |||
| 32 | class DebugContext; | 36 | class DebugContext; |
| 33 | } | 37 | } |
| 34 | 38 | ||
| 39 | enum class EmulatedDirectoryTarget { | ||
| 40 | NAND, | ||
| 41 | SDMC, | ||
| 42 | }; | ||
| 43 | |||
| 35 | class GMainWindow : public QMainWindow { | 44 | class GMainWindow : public QMainWindow { |
| 36 | Q_OBJECT | 45 | Q_OBJECT |
| 37 | 46 | ||
| @@ -85,7 +94,7 @@ private: | |||
| 85 | void ConnectWidgetEvents(); | 94 | void ConnectWidgetEvents(); |
| 86 | void ConnectMenuEvents(); | 95 | void ConnectMenuEvents(); |
| 87 | 96 | ||
| 88 | bool SupportsRequiredGLExtensions(); | 97 | QStringList GetUnsupportedGLExtensions(); |
| 89 | bool LoadROM(const QString& filename); | 98 | bool LoadROM(const QString& filename); |
| 90 | void BootGame(const QString& filename); | 99 | void BootGame(const QString& filename); |
| 91 | void ShutdownGame(); | 100 | void ShutdownGame(); |
| @@ -129,14 +138,15 @@ private slots: | |||
| 129 | /// Called whenever a user selects a game in the game list widget. | 138 | /// Called whenever a user selects a game in the game list widget. |
| 130 | void OnGameListLoadFile(QString game_path); | 139 | void OnGameListLoadFile(QString game_path); |
| 131 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); | 140 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); |
| 132 | void OnGameListNavigateToGamedbEntry( | 141 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 133 | u64 program_id, | 142 | const CompatibilityList& compatibility_list); |
| 134 | std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); | ||
| 135 | void OnMenuLoadFile(); | 143 | void OnMenuLoadFile(); |
| 136 | void OnMenuLoadFolder(); | 144 | void OnMenuLoadFolder(); |
| 137 | void OnMenuInstallToNAND(); | 145 | void OnMenuInstallToNAND(); |
| 138 | /// Called whenever a user selects the "File->Select Game List Root" menu item | 146 | /// Called whenever a user selects the "File->Select Game List Root" menu item |
| 139 | void OnMenuSelectGameListRoot(); | 147 | void OnMenuSelectGameListRoot(); |
| 148 | /// Called whenever a user select the "File->Select -- Directory" where -- is NAND or SD Card | ||
| 149 | void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); | ||
| 140 | void OnMenuRecentFile(); | 150 | void OnMenuRecentFile(); |
| 141 | void OnConfigure(); | 151 | void OnConfigure(); |
| 142 | void OnAbout(); | 152 | void OnAbout(); |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index faa0c626a..3879d4813 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -65,6 +65,9 @@ | |||
| 65 | <addaction name="action_Select_Game_List_Root"/> | 65 | <addaction name="action_Select_Game_List_Root"/> |
| 66 | <addaction name="menu_recent_files"/> | 66 | <addaction name="menu_recent_files"/> |
| 67 | <addaction name="separator"/> | 67 | <addaction name="separator"/> |
| 68 | <addaction name="action_Select_NAND_Directory"/> | ||
| 69 | <addaction name="action_Select_SDMC_Directory"/> | ||
| 70 | <addaction name="separator"/> | ||
| 68 | <addaction name="action_Exit"/> | 71 | <addaction name="action_Exit"/> |
| 69 | </widget> | 72 | </widget> |
| 70 | <widget class="QMenu" name="menu_Emulation"> | 73 | <widget class="QMenu" name="menu_Emulation"> |
| @@ -204,6 +207,22 @@ | |||
| 204 | <string>Selects a folder to display in the game list</string> | 207 | <string>Selects a folder to display in the game list</string> |
| 205 | </property> | 208 | </property> |
| 206 | </action> | 209 | </action> |
| 210 | <action name="action_Select_NAND_Directory"> | ||
| 211 | <property name="text"> | ||
| 212 | <string>Select NAND Directory...</string> | ||
| 213 | </property> | ||
| 214 | <property name="toolTip"> | ||
| 215 | <string>Selects a folder to use as the root of the emulated NAND</string> | ||
| 216 | </property> | ||
| 217 | </action> | ||
| 218 | <action name="action_Select_SDMC_Directory"> | ||
| 219 | <property name="text"> | ||
| 220 | <string>Select SD Card Directory...</string> | ||
| 221 | </property> | ||
| 222 | <property name="toolTip"> | ||
| 223 | <string>Selects a folder to use as the root of the emulated SD card</string> | ||
| 224 | </property> | ||
| 225 | </action> | ||
| 207 | <action name="action_Fullscreen"> | 226 | <action name="action_Fullscreen"> |
| 208 | <property name="checkable"> | 227 | <property name="checkable"> |
| 209 | <bool>true</bool> | 228 | <bool>true</bool> |
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index e99042a23..62c080aff 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -30,8 +30,9 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { | |||
| 30 | QPixmap circle_pixmap(16, 16); | 30 | QPixmap circle_pixmap(16, 16); |
| 31 | circle_pixmap.fill(Qt::transparent); | 31 | circle_pixmap.fill(Qt::transparent); |
| 32 | QPainter painter(&circle_pixmap); | 32 | QPainter painter(&circle_pixmap); |
| 33 | painter.setRenderHint(QPainter::Antialiasing); | ||
| 33 | painter.setPen(color); | 34 | painter.setPen(color); |
| 34 | painter.setBrush(color); | 35 | painter.setBrush(color); |
| 35 | painter.drawEllipse(0, 0, 15, 15); | 36 | painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); |
| 36 | return circle_pixmap; | 37 | return circle_pixmap; |
| 37 | } | 38 | } |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index a95580152..7ec1f5110 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -108,15 +108,27 @@ void Config::ReadValues() { | |||
| 108 | 108 | ||
| 109 | // Audio | 109 | // Audio |
| 110 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); | 110 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); |
| 111 | Settings::values.enable_audio_stretching = | ||
| 112 | sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); | ||
| 111 | Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); | 113 | Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); |
| 112 | Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); | 114 | Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); |
| 113 | 115 | ||
| 114 | // Data Storage | 116 | // Data Storage |
| 115 | Settings::values.use_virtual_sd = | 117 | Settings::values.use_virtual_sd = |
| 116 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); | 118 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); |
| 119 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, | ||
| 120 | sdl2_config->Get("Data Storage", "nand_directory", | ||
| 121 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | ||
| 122 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | ||
| 123 | sdl2_config->Get("Data Storage", "nand_directory", | ||
| 124 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||
| 117 | 125 | ||
| 118 | // System | 126 | // System |
| 119 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 127 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |
| 128 | Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); | ||
| 129 | if (Settings::values.username.empty()) { | ||
| 130 | Settings::values.username = "yuzu"; | ||
| 131 | } | ||
| 120 | 132 | ||
| 121 | // Miscellaneous | 133 | // Miscellaneous |
| 122 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 134 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 6ed9e7962..d35c441e9 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -150,6 +150,12 @@ swap_screen = | |||
| 150 | # auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available) | 150 | # auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available) |
| 151 | output_engine = | 151 | output_engine = |
| 152 | 152 | ||
| 153 | # Whether or not to enable the audio-stretching post-processing effect. | ||
| 154 | # This effect adjusts audio speed to match emulation speed and helps prevent audio stutter, | ||
| 155 | # at the cost of increasing audio latency. | ||
| 156 | # 0: No, 1 (default): Yes | ||
| 157 | enable_audio_stretching = | ||
| 158 | |||
| 153 | # Which audio device to use. | 159 | # Which audio device to use. |
| 154 | # auto (default): Auto-select | 160 | # auto (default): Auto-select |
| 155 | output_device = | 161 | output_device = |
| @@ -170,7 +176,7 @@ use_docked_mode = | |||
| 170 | 176 | ||
| 171 | # Sets the account username, max length is 32 characters | 177 | # Sets the account username, max length is 32 characters |
| 172 | # yuzu (default) | 178 | # yuzu (default) |
| 173 | username = | 179 | username = yuzu |
| 174 | 180 | ||
| 175 | # Sets the systems language index | 181 | # Sets the systems language index |
| 176 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, | 182 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 351dd9225..0733301b2 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "input_common/keyboard.h" | 16 | #include "input_common/keyboard.h" |
| 17 | #include "input_common/main.h" | 17 | #include "input_common/main.h" |
| 18 | #include "input_common/motion_emu.h" | 18 | #include "input_common/motion_emu.h" |
| 19 | #include "input_common/sdl/sdl.h" | ||
| 19 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 20 | 21 | ||
| 21 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 22 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| @@ -91,6 +92,12 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { | |||
| 91 | unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); | 92 | unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); |
| 92 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | 93 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) |
| 93 | unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); | 94 | unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); |
| 95 | if (!GLAD_GL_ARB_base_instance) | ||
| 96 | unsupported_ext.push_back("ARB_base_instance"); | ||
| 97 | if (!GLAD_GL_ARB_texture_storage) | ||
| 98 | unsupported_ext.push_back("ARB_texture_storage"); | ||
| 99 | if (!GLAD_GL_ARB_multi_bind) | ||
| 100 | unsupported_ext.push_back("ARB_multi_bind"); | ||
| 94 | 101 | ||
| 95 | // Extensions required to support some texture formats. | 102 | // Extensions required to support some texture formats. |
| 96 | if (!GLAD_GL_EXT_texture_compression_s3tc) | 103 | if (!GLAD_GL_EXT_texture_compression_s3tc) |
| @@ -114,7 +121,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 114 | SDL_SetMainReady(); | 121 | SDL_SetMainReady(); |
| 115 | 122 | ||
| 116 | // Initialize the window | 123 | // Initialize the window |
| 117 | if (SDL_Init(SDL_INIT_VIDEO) < 0) { | 124 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { |
| 118 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | 125 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); |
| 119 | exit(1); | 126 | exit(1); |
| 120 | } | 127 | } |
| @@ -128,7 +135,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 128 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 135 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 129 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 136 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 130 | 137 | ||
| 131 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | 138 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| 132 | Common::g_scm_branch, Common::g_scm_desc); | 139 | Common::g_scm_branch, Common::g_scm_desc); |
| 133 | render_window = | 140 | render_window = |
| 134 | SDL_CreateWindow(window_title.c_str(), | 141 | SDL_CreateWindow(window_title.c_str(), |
| @@ -166,13 +173,15 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 166 | OnResize(); | 173 | OnResize(); |
| 167 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 174 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 168 | SDL_PumpEvents(); | 175 | SDL_PumpEvents(); |
| 169 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_name, Common::g_scm_branch, | 176 | SDL_GL_SetSwapInterval(false); |
| 177 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | ||
| 170 | Common::g_scm_desc); | 178 | Common::g_scm_desc); |
| 171 | 179 | ||
| 172 | DoneCurrent(); | 180 | DoneCurrent(); |
| 173 | } | 181 | } |
| 174 | 182 | ||
| 175 | EmuWindow_SDL2::~EmuWindow_SDL2() { | 183 | EmuWindow_SDL2::~EmuWindow_SDL2() { |
| 184 | InputCommon::SDL::CloseSDLJoysticks(); | ||
| 176 | SDL_GL_DeleteContext(gl_context); | 185 | SDL_GL_DeleteContext(gl_context); |
| 177 | SDL_Quit(); | 186 | SDL_Quit(); |
| 178 | 187 | ||
| @@ -217,6 +226,9 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 217 | case SDL_QUIT: | 226 | case SDL_QUIT: |
| 218 | is_open = false; | 227 | is_open = false; |
| 219 | break; | 228 | break; |
| 229 | default: | ||
| 230 | InputCommon::SDL::HandleGameControllerEvent(event); | ||
| 231 | break; | ||
| 220 | } | 232 | } |
| 221 | } | 233 | } |
| 222 | } | 234 | } |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 41e7da897..b2559b717 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <fmt/ostream.h> | 10 | #include <fmt/ostream.h> |
| 11 | 11 | ||
| 12 | #include "common/common_paths.h" | 12 | #include "common/common_paths.h" |
| 13 | #include "common/file_util.h" | ||
| 13 | #include "common/logging/backend.h" | 14 | #include "common/logging/backend.h" |
| 14 | #include "common/logging/filter.h" | 15 | #include "common/logging/filter.h" |
| 15 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| @@ -19,8 +20,10 @@ | |||
| 19 | #include "common/string_util.h" | 20 | #include "common/string_util.h" |
| 20 | #include "common/telemetry.h" | 21 | #include "common/telemetry.h" |
| 21 | #include "core/core.h" | 22 | #include "core/core.h" |
| 23 | #include "core/crypto/key_manager.h" | ||
| 22 | #include "core/file_sys/vfs_real.h" | 24 | #include "core/file_sys/vfs_real.h" |
| 23 | #include "core/gdbstub/gdbstub.h" | 25 | #include "core/gdbstub/gdbstub.h" |
| 26 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 24 | #include "core/loader/loader.h" | 27 | #include "core/loader/loader.h" |
| 25 | #include "core/settings.h" | 28 | #include "core/settings.h" |
| 26 | #include "core/telemetry_session.h" | 29 | #include "core/telemetry_session.h" |
| @@ -28,7 +31,6 @@ | |||
| 28 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 31 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 29 | 32 | ||
| 30 | #include <getopt.h> | 33 | #include <getopt.h> |
| 31 | #include "core/crypto/key_manager.h" | ||
| 32 | #ifndef _MSC_VER | 34 | #ifndef _MSC_VER |
| 33 | #include <unistd.h> | 35 | #include <unistd.h> |
| 34 | #endif | 36 | #endif |
| @@ -81,6 +83,9 @@ int main(int argc, char** argv) { | |||
| 81 | int option_index = 0; | 83 | int option_index = 0; |
| 82 | bool use_gdbstub = Settings::values.use_gdbstub; | 84 | bool use_gdbstub = Settings::values.use_gdbstub; |
| 83 | u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); | 85 | u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); |
| 86 | |||
| 87 | InitializeLogging(); | ||
| 88 | |||
| 84 | char* endarg; | 89 | char* endarg; |
| 85 | #ifdef _WIN32 | 90 | #ifdef _WIN32 |
| 86 | int argc_w; | 91 | int argc_w; |
| @@ -143,8 +148,6 @@ int main(int argc, char** argv) { | |||
| 143 | LocalFree(argv_w); | 148 | LocalFree(argv_w); |
| 144 | #endif | 149 | #endif |
| 145 | 150 | ||
| 146 | InitializeLogging(); | ||
| 147 | |||
| 148 | MicroProfileOnThreadCreate("EmuThread"); | 151 | MicroProfileOnThreadCreate("EmuThread"); |
| 149 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 152 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| 150 | 153 | ||
| @@ -167,6 +170,7 @@ int main(int argc, char** argv) { | |||
| 167 | 170 | ||
| 168 | Core::System& system{Core::System::GetInstance()}; | 171 | Core::System& system{Core::System::GetInstance()}; |
| 169 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 172 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 173 | Service::FileSystem::CreateFactories(system.GetFilesystem()); | ||
| 170 | 174 | ||
| 171 | SCOPE_EXIT({ system.Shutdown(); }); | 175 | SCOPE_EXIT({ system.Shutdown(); }); |
| 172 | 176 | ||