diff options
Diffstat (limited to 'src')
290 files changed, 8073 insertions, 2424 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 85354f43e7..a88551fbc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ include_directories(.) add_subdirectory(common) add_subdirectory(core) +add_subdirectory(audio_core) add_subdirectory(video_core) add_subdirectory(input_common) add_subdirectory(tests) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt new file mode 100644 index 0000000000..ec71524a3b --- /dev/null +++ b/src/audio_core/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library(audio_core STATIC + audio_out.cpp + audio_out.h + audio_renderer.cpp + audio_renderer.h + buffer.h + codec.cpp + codec.h + null_sink.h + stream.cpp + stream.h + sink.h + sink_details.cpp + sink_details.h + sink_stream.h + + $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> +) + +create_target_directory_groups(audio_core) + +target_link_libraries(audio_core PUBLIC common core) + +if(ENABLE_CUBEB) + target_link_libraries(audio_core PRIVATE cubeb) + target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) +endif() diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp new file mode 100644 index 0000000000..12632a95c5 --- /dev/null +++ b/src/audio_core/audio_out.cpp @@ -0,0 +1,58 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "audio_core/audio_out.h" +#include "audio_core/sink.h" +#include "audio_core/sink_details.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/settings.h" + +namespace AudioCore { + +/// Returns the stream format from the specified number of channels +static Stream::Format ChannelsToStreamFormat(u32 num_channels) { + switch (num_channels) { + case 1: + return Stream::Format::Mono16; + case 2: + return Stream::Format::Stereo16; + case 6: + return Stream::Format::Multi51Channel16; + } + + LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels); + UNREACHABLE(); + return {}; +} + +StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, + Stream::ReleaseCallback&& release_callback) { + if (!sink) { + const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); + sink = sink_details.factory(Settings::values.audio_device_id); + } + + return std::make_shared<Stream>( + sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback), + sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); +} + +std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { + return stream->GetTagsAndReleaseBuffers(max_count); +} + +void AudioOut::StartStream(StreamPtr stream) { + stream->Play(); +} + +void AudioOut::StopStream(StreamPtr stream) { + stream->Stop(); +} + +bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) { + return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data))); +} + +} // namespace AudioCore diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h new file mode 100644 index 0000000000..39b7e656b2 --- /dev/null +++ b/src/audio_core/audio_out.h @@ -0,0 +1,43 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> +#include <vector> + +#include "audio_core/buffer.h" +#include "audio_core/sink.h" +#include "audio_core/stream.h" +#include "common/common_types.h" + +namespace AudioCore { + +/** + * Represents an audio playback interface, used to open and play audio streams + */ +class AudioOut { +public: + /// Opens a new audio stream + StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, + Stream::ReleaseCallback&& release_callback); + + /// Returns a vector of recently released buffers specified by tag for the specified stream + std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count); + + /// Starts an audio stream for playback + void StartStream(StreamPtr stream); + + /// Stops an audio stream that is currently playing + void StopStream(StreamPtr stream); + + /// Queues a buffer into the specified audio stream, returns true on success + bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data); + +private: + SinkPtr sink; +}; + +} // namespace AudioCore diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp new file mode 100644 index 0000000000..282f345c55 --- /dev/null +++ b/src/audio_core/audio_renderer.cpp @@ -0,0 +1,234 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "audio_core/audio_renderer.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/memory.h" + +namespace AudioCore { + +constexpr u32 STREAM_SAMPLE_RATE{48000}; +constexpr u32 STREAM_NUM_CHANNELS{2}; + +AudioRenderer::AudioRenderer(AudioRendererParameter params, + Kernel::SharedPtr<Kernel::Event> buffer_event) + : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { + + audio_core = std::make_unique<AudioCore::AudioOut>(); + stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", + [=]() { buffer_event->Signal(); }); + audio_core->StartStream(stream); + + QueueMixedBuffer(0); + QueueMixedBuffer(1); + QueueMixedBuffer(2); +} + +std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { + // Copy UpdateDataHeader struct + UpdateDataHeader config{}; + std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader)); + u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); + + // Copy MemoryPoolInfo structs + std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); + std::memcpy(mem_pool_info.data(), + input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, + memory_pool_count * sizeof(MemoryPoolInfo)); + + // Copy VoiceInfo structs + size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + + config.voice_resource_size}; + for (auto& voice : voices) { + std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); + offset += sizeof(VoiceInfo); + } + + // Update voices + for (auto& voice : voices) { + voice.UpdateState(); + if (!voice.GetInfo().is_in_use) { + continue; + } + if (voice.GetInfo().is_new) { + voice.SetWaveIndex(voice.GetInfo().wave_buffer_head); + } + } + + // Update memory pool state + std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); + for (size_t index = 0; index < memory_pool.size(); ++index) { + if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { + memory_pool[index].state = MemoryPoolStates::Attached; + } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { + memory_pool[index].state = MemoryPoolStates::Detached; + } + } + + // Release previous buffers and queue next ones for playback + ReleaseAndQueueBuffers(); + + // Copy output header + UpdateDataHeader response_data{worker_params}; + std::vector<u8> output_params(response_data.total_size); + std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); + + // Copy output memory pool entries + std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(), + response_data.memory_pools_size); + + // Copy output voice status + size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; + for (const auto& voice : voices) { + std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), + sizeof(VoiceOutStatus)); + voice_out_status_offset += sizeof(VoiceOutStatus); + } + + return output_params; +} + +void AudioRenderer::VoiceState::SetWaveIndex(size_t index) { + wave_index = index & 3; + is_refresh_pending = true; +} + +std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) { + if (!IsPlaying()) { + return {}; + } + + if (is_refresh_pending) { + RefreshBuffer(); + } + + const size_t max_size{samples.size() - offset}; + const size_t dequeue_offset{offset}; + size_t size{sample_count * STREAM_NUM_CHANNELS}; + if (size > max_size) { + size = max_size; + } + + out_status.played_sample_count += size / STREAM_NUM_CHANNELS; + offset += size; + + const auto& wave_buffer{info.wave_buffer[wave_index]}; + if (offset == samples.size()) { + offset = 0; + + if (!wave_buffer.is_looping) { + SetWaveIndex(wave_index + 1); + } + + out_status.wave_buffer_consumed++; + + if (wave_buffer.end_of_stream) { + info.play_state = PlayState::Paused; + } + } + + return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size}; +} + +void AudioRenderer::VoiceState::UpdateState() { + if (is_in_use && !info.is_in_use) { + // No longer in use, reset state + is_refresh_pending = true; + wave_index = 0; + offset = 0; + out_status = {}; + } + is_in_use = info.is_in_use; +} + +void AudioRenderer::VoiceState::RefreshBuffer() { + std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16)); + Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(), + info.wave_buffer[wave_index].buffer_sz); + + switch (static_cast<Codec::PcmFormat>(info.sample_format)) { + case Codec::PcmFormat::Int16: { + // PCM16 is played as-is + break; + } + case Codec::PcmFormat::Adpcm: { + // Decode ADPCM to PCM16 + Codec::ADPCM_Coeff coeffs; + Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); + new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()), + new_samples.size() * sizeof(s16), coeffs, adpcm_state); + break; + } + default: + LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format); + UNREACHABLE(); + break; + } + + switch (info.channel_count) { + case 1: + // 1 channel is upsampled to 2 channel + samples.resize(new_samples.size() * 2); + for (size_t index = 0; index < new_samples.size(); ++index) { + samples[index * 2] = new_samples[index]; + samples[index * 2 + 1] = new_samples[index]; + } + break; + case 2: { + // 2 channel is played as is + samples = std::move(new_samples); + break; + } + default: + LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count); + UNREACHABLE(); + break; + } + + is_refresh_pending = false; +} + +static constexpr s16 ClampToS16(s32 value) { + return static_cast<s16>(std::clamp(value, -32768, 32767)); +} + +void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { + constexpr size_t BUFFER_SIZE{512}; + std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); + + for (auto& voice : voices) { + if (!voice.IsPlaying()) { + continue; + } + + size_t offset{}; + s64 samples_remaining{BUFFER_SIZE}; + while (samples_remaining > 0) { + const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; + + if (samples.empty()) { + break; + } + + samples_remaining -= samples.size(); + + for (const auto& sample : samples) { + const s32 buffer_sample{buffer[offset]}; + buffer[offset++] = + ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume)); + } + } + } + audio_core->QueueBuffer(stream, tag, std::move(buffer)); +} + +void AudioRenderer::ReleaseAndQueueBuffers() { + const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)}; + for (const auto& tag : released_buffers) { + QueueMixedBuffer(tag); + } +} + +} // namespace AudioCore diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h new file mode 100644 index 0000000000..6950a4681c --- /dev/null +++ b/src/audio_core/audio_renderer.h @@ -0,0 +1,206 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <vector> + +#include "audio_core/audio_out.h" +#include "audio_core/codec.h" +#include "audio_core/stream.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/kernel/event.h" + +namespace AudioCore { + +enum class PlayState : u8 { + Started = 0, + Stopped = 1, + Paused = 2, +}; + +struct AudioRendererParameter { + u32_le sample_rate; + u32_le sample_count; + u32_le unknown_8; + u32_le unknown_c; + u32_le voice_count; + u32_le sink_count; + u32_le effect_count; + u32_le unknown_1c; + u8 unknown_20; + INSERT_PADDING_BYTES(3); + u32_le splitter_count; + u32_le unknown_2c; + INSERT_PADDING_WORDS(1); + u32_le revision; +}; +static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size"); + +enum class MemoryPoolStates : u32 { // Should be LE + Invalid = 0x0, + Unknown = 0x1, + RequestDetach = 0x2, + Detached = 0x3, + RequestAttach = 0x4, + Attached = 0x5, + Released = 0x6, +}; + +struct MemoryPoolEntry { + MemoryPoolStates state; + u32_le unknown_4; + u32_le unknown_8; + u32_le unknown_c; +}; +static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size"); + +struct MemoryPoolInfo { + u64_le pool_address; + u64_le pool_size; + MemoryPoolStates pool_state; + INSERT_PADDING_WORDS(3); // Unknown +}; +static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size"); +struct BiquadFilter { + u8 enable; + INSERT_PADDING_BYTES(1); + std::array<s16_le, 3> numerator; + std::array<s16_le, 2> denominator; +}; +static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size"); + +struct WaveBuffer { + u64_le buffer_addr; + u64_le buffer_sz; + s32_le start_sample_offset; + s32_le end_sample_offset; + u8 is_looping; + u8 end_of_stream; + u8 sent_to_server; + INSERT_PADDING_BYTES(5); + u64 context_addr; + u64 context_sz; + INSERT_PADDING_BYTES(8); +}; +static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); + +struct VoiceInfo { + u32_le id; + u32_le node_id; + u8 is_new; + u8 is_in_use; + PlayState play_state; + u8 sample_format; + u32_le sample_rate; + u32_le priority; + u32_le sorting_order; + u32_le channel_count; + float_le pitch; + float_le volume; + std::array<BiquadFilter, 2> biquad_filter; + u32_le wave_buffer_count; + u32_le wave_buffer_head; + INSERT_PADDING_WORDS(1); + u64_le additional_params_addr; + u64_le additional_params_sz; + u32_le mix_id; + u32_le splitter_info_id; + std::array<WaveBuffer, 4> wave_buffer; + std::array<u32_le, 6> voice_channel_resource_ids; + INSERT_PADDING_BYTES(24); +}; +static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size"); + +struct VoiceOutStatus { + u64_le played_sample_count; + u32_le wave_buffer_consumed; + u32_le voice_drops_count; +}; +static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); + +struct UpdateDataHeader { + UpdateDataHeader() {} + + explicit UpdateDataHeader(const AudioRendererParameter& config) { + revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision + behavior_size = 0xb0; + memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; + voices_size = config.voice_count * 0x10; + voice_resource_size = 0x0; + effects_size = config.effect_count * 0x10; + mixes_size = 0x0; + sinks_size = config.sink_count * 0x20; + performance_manager_size = 0x10; + total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + + effects_size + sinks_size + performance_manager_size; + } + + u32_le revision; + u32_le behavior_size; + u32_le memory_pools_size; + u32_le voices_size; + u32_le voice_resource_size; + u32_le effects_size; + u32_le mixes_size; + u32_le sinks_size; + u32_le performance_manager_size; + INSERT_PADDING_WORDS(6); + u32_le total_size; +}; +static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); + +class AudioRenderer { +public: + AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); + std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); + void QueueMixedBuffer(Buffer::Tag tag); + void ReleaseAndQueueBuffers(); + +private: + class VoiceState { + public: + bool IsPlaying() const { + return is_in_use && info.play_state == PlayState::Started; + } + + const VoiceOutStatus& GetOutStatus() const { + return out_status; + } + + const VoiceInfo& GetInfo() const { + return info; + } + + VoiceInfo& Info() { + return info; + } + + void SetWaveIndex(size_t index); + std::vector<s16> DequeueSamples(size_t sample_count); + void UpdateState(); + void RefreshBuffer(); + + private: + bool is_in_use{}; + bool is_refresh_pending{}; + size_t wave_index{}; + size_t offset{}; + Codec::ADPCMState adpcm_state{}; + std::vector<s16> samples; + VoiceOutStatus out_status{}; + VoiceInfo info{}; + }; + + AudioRendererParameter worker_params; + Kernel::SharedPtr<Kernel::Event> buffer_event; + std::vector<VoiceState> voices; + std::unique_ptr<AudioCore::AudioOut> audio_core; + AudioCore::StreamPtr stream; +}; + +} // namespace AudioCore diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h new file mode 100644 index 0000000000..a323b23ec9 --- /dev/null +++ b/src/audio_core/buffer.h @@ -0,0 +1,45 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> + +#include "common/common_types.h" + +namespace AudioCore { + +/** + * Represents a buffer of audio samples to be played in an audio stream + */ +class Buffer { +public: + using Tag = u64; + + Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {} + + /// Returns the raw audio data for the buffer + std::vector<s16>& Samples() { + return samples; + } + + /// Returns the raw audio data for the buffer + const std::vector<s16>& GetSamples() const { + return samples; + } + + /// Returns the buffer tag, this is provided by the game to the audout service + Tag GetTag() const { + return tag; + } + +private: + Tag tag; + std::vector<s16> samples; +}; + +using BufferPtr = std::shared_ptr<Buffer>; + +} // namespace AudioCore diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp new file mode 100644 index 0000000000..c3021403f5 --- /dev/null +++ b/src/audio_core/codec.cpp @@ -0,0 +1,77 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "audio_core/codec.h" + +namespace AudioCore::Codec { + +std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, + ADPCMState& state) { + // GC-ADPCM with scale factor and variable coefficients. + // Frames are 8 bytes long containing 14 samples each. + // Samples are 4 bits (one nibble) long. + + constexpr size_t FRAME_LEN = 8; + constexpr size_t SAMPLES_PER_FRAME = 14; + constexpr std::array<int, 16> SIGNED_NIBBLES = { + {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; + + const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; + const size_t ret_size = + sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. + std::vector<s16> ret(ret_size); + + int yn1 = state.yn1, yn2 = state.yn2; + + const size_t NUM_FRAMES = + (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. + for (size_t framei = 0; framei < NUM_FRAMES; framei++) { + const int frame_header = data[framei * FRAME_LEN]; + const int scale = 1 << (frame_header & 0xF); + const int idx = (frame_header >> 4) & 0x7; + + // Coefficients are fixed point with 11 bits fractional part. + const int coef1 = coeff[idx * 2 + 0]; + const int coef2 = coeff[idx * 2 + 1]; + + // Decodes an audio sample. One nibble produces one sample. + const auto decode_sample = [&](const int nibble) -> s16 { + const int xn = nibble * scale; + // We first transform everything into 11 bit fixed point, perform the second order + // digital filter, then transform back. + // 0x400 == 0.5 in 11 bit fixed point. + // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] + int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; + // Clamp to output range. + val = std::clamp<s32>(val, -32768, 32767); + // Advance output feedback. + yn2 = yn1; + yn1 = val; + return static_cast<s16>(val); + }; + + size_t outputi = framei * SAMPLES_PER_FRAME; + size_t datai = framei * FRAME_LEN + 1; + for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { + const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); + ret[outputi] = sample1; + outputi++; + + const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]); + ret[outputi] = sample2; + outputi++; + + datai++; + } + } + + state.yn1 = yn1; + state.yn2 = yn2; + + return ret; +} + +} // namespace AudioCore::Codec diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h new file mode 100644 index 0000000000..3f845c42c3 --- /dev/null +++ b/src/audio_core/codec.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_types.h" + +namespace AudioCore::Codec { + +enum class PcmFormat : u32 { + Invalid = 0, + Int8 = 1, + Int16 = 2, + Int24 = 3, + Int32 = 4, + PcmFloat = 5, + Adpcm = 6, +}; + +/// See: Codec::DecodeADPCM +struct ADPCMState { + // Two historical samples from previous processed buffer, + // required for ADPCM decoding + s16 yn1; ///< y[n-1] + s16 yn2; ///< y[n-2] +}; + +using ADPCM_Coeff = std::array<s16, 16>; + +/** + * @param data Pointer to buffer that contains ADPCM data to decode + * @param size Size of buffer in bytes + * @param coeff ADPCM coefficients + * @param state ADPCM state, this is updated with new state + * @return Decoded stereo signed PCM16 data, sample_count in length + */ +std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, + ADPCMState& state); + +}; // namespace AudioCore::Codec diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp new file mode 100644 index 0000000000..1501ef1f48 --- /dev/null +++ b/src/audio_core/cubeb_sink.cpp @@ -0,0 +1,200 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstring> + +#include "audio_core/cubeb_sink.h" +#include "audio_core/stream.h" +#include "common/logging/log.h" + +namespace AudioCore { + +class SinkStreamImpl final : public SinkStream { +public: + SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, + const std::string& name) + : ctx{ctx}, num_channels{num_channels_} { + + if (num_channels == 6) { + // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2 + // channel for now + is_6_channel = true; + num_channels = 2; + } + + cubeb_stream_params params{}; + params.rate = sample_rate; + params.channels = num_channels; + params.format = CUBEB_SAMPLE_S16NE; + params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO; + + u32 minimum_latency{}; + if (cubeb_get_min_latency(ctx, ¶ms, &minimum_latency) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "Error getting minimum latency"); + } + + if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device, + ¶ms, std::max(512u, minimum_latency), + &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback, + this) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); + return; + } + + if (cubeb_stream_start(stream_backend) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream"); + return; + } + } + + ~SinkStreamImpl() { + if (!ctx) { + return; + } + + if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); + } + + cubeb_stream_destroy(stream_backend); + } + + void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override { + if (!ctx) { + return; + } + + queue.reserve(queue.size() + samples.size() * GetNumChannels()); + + if (is_6_channel) { + // Downsample 6 channels to 2 + const size_t sample_count_copy_size = samples.size() * 2; + queue.reserve(sample_count_copy_size); + for (size_t i = 0; i < samples.size(); i += num_channels) { + queue.push_back(samples[i]); + queue.push_back(samples[i + 1]); + } + } else { + // Copy as-is + std::copy(samples.begin(), samples.end(), std::back_inserter(queue)); + } + } + + u32 GetNumChannels() const { + return num_channels; + } + +private: + std::vector<std::string> device_list; + + cubeb* ctx{}; + cubeb_stream* stream_backend{}; + u32 num_channels{}; + bool is_6_channel{}; + + std::vector<s16> queue; + + static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, + void* output_buffer, long num_frames); + static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); +}; + +CubebSink::CubebSink(std::string target_device_name) { + if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); + return; + } + + if (target_device_name != auto_device_name && !target_device_name.empty()) { + cubeb_device_collection collection; + if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { + LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); + } else { + const auto collection_end{collection.device + collection.count}; + const auto device{std::find_if(collection.device, collection_end, + [&](const cubeb_device_info& device) { + return target_device_name == device.friendly_name; + })}; + if (device != collection_end) { + output_device = device->devid; + } + cubeb_device_collection_destroy(ctx, &collection); + } + } +} + +CubebSink::~CubebSink() { + if (!ctx) { + return; + } + + for (auto& sink_stream : sink_streams) { + sink_stream.reset(); + } + + cubeb_destroy(ctx); +} + +SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, + const std::string& name) { + sink_streams.push_back( + std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name)); + return *sink_streams.back(); +} + +long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, + void* output_buffer, long num_frames) { + SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data); + u8* buffer = reinterpret_cast<u8*>(output_buffer); + + if (!impl) { + return {}; + } + + const size_t frames_to_write{ + std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))}; + + memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels()); + impl->queue.erase(impl->queue.begin(), + impl->queue.begin() + frames_to_write * impl->GetNumChannels()); + + if (frames_to_write < num_frames) { + // Fill the rest of the frames with silence + memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0, + (num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels()); + } + + return num_frames; +} + +void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {} + +std::vector<std::string> ListCubebSinkDevices() { + std::vector<std::string> device_list; + cubeb* ctx; + + if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); + return {}; + } + + cubeb_device_collection collection; + if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { + LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); + } else { + for (size_t i = 0; i < collection.count; i++) { + const cubeb_device_info& device = collection.device[i]; + if (device.friendly_name) { + device_list.emplace_back(device.friendly_name); + } + } + cubeb_device_collection_destroy(ctx, &collection); + } + + cubeb_destroy(ctx); + return device_list; +} + +} // namespace AudioCore diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h new file mode 100644 index 0000000000..59cbf05e9c --- /dev/null +++ b/src/audio_core/cubeb_sink.h @@ -0,0 +1,32 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <vector> + +#include <cubeb/cubeb.h> + +#include "audio_core/sink.h" + +namespace AudioCore { + +class CubebSink final : public Sink { +public: + explicit CubebSink(std::string device_id); + ~CubebSink() override; + + SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, + const std::string& name) override; + +private: + cubeb* ctx{}; + cubeb_devid output_device{}; + std::vector<SinkStreamPtr> sink_streams; +}; + +std::vector<std::string> ListCubebSinkDevices(); + +} // namespace AudioCore diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h new file mode 100644 index 0000000000..f235d93e56 --- /dev/null +++ b/src/audio_core/null_sink.h @@ -0,0 +1,27 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "audio_core/sink.h" + +namespace AudioCore { + +class NullSink final : public Sink { +public: + explicit NullSink(std::string){}; + ~NullSink() override = default; + + SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, + const std::string& /*name*/) override { + return null_sink_stream; + } + +private: + struct NullSinkStreamImpl final : SinkStream { + void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} + } null_sink_stream; +}; + +} // namespace AudioCore diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h new file mode 100644 index 0000000000..95c7b2b6ec --- /dev/null +++ b/src/audio_core/sink.h @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> + +#include "audio_core/sink_stream.h" +#include "common/common_types.h" + +namespace AudioCore { + +constexpr char auto_device_name[] = "auto"; + +/** + * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed + * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate. + * They are dumb outputs. + */ +class Sink { +public: + virtual ~Sink() = default; + virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, + const std::string& name) = 0; +}; + +using SinkPtr = std::unique_ptr<Sink>; + +} // namespace AudioCore diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp new file mode 100644 index 0000000000..955ba20fb4 --- /dev/null +++ b/src/audio_core/sink_details.cpp @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <memory> +#include <string> +#include <vector> +#include "audio_core/null_sink.h" +#include "audio_core/sink_details.h" +#ifdef HAVE_CUBEB +#include "audio_core/cubeb_sink.h" +#endif +#include "common/logging/log.h" + +namespace AudioCore { + +// g_sink_details is ordered in terms of desirability, with the best choice at the top. +const std::vector<SinkDetails> g_sink_details = { +#ifdef HAVE_CUBEB + SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices}, +#endif + SinkDetails{"null", &std::make_unique<NullSink, std::string>, + [] { return std::vector<std::string>{"null"}; }}, +}; + +const SinkDetails& GetSinkDetails(std::string sink_id) { + auto iter = + std::find_if(g_sink_details.begin(), g_sink_details.end(), + [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); + + if (sink_id == "auto" || iter == g_sink_details.end()) { + if (sink_id != "auto") { + LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id); + } + // Auto-select. + // g_sink_details is ordered in terms of desirability, with the best choice at the front. + iter = g_sink_details.begin(); + } + + return *iter; +} + +} // namespace AudioCore diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h new file mode 100644 index 0000000000..ea666c5549 --- /dev/null +++ b/src/audio_core/sink_details.h @@ -0,0 +1,35 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include <memory> +#include <utility> +#include <vector> + +namespace AudioCore { + +class Sink; + +struct SinkDetails { + using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>; + using ListDevicesFn = std::function<std::vector<std::string>()>; + + SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_) + : id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {} + + /// Name for this sink. + const char* id; + /// A method to call to construct an instance of this type of sink. + FactoryFn factory; + /// A method to call to list available devices. + ListDevicesFn list_devices; +}; + +extern const std::vector<SinkDetails> g_sink_details; + +const SinkDetails& GetSinkDetails(std::string sink_id); + +} // namespace AudioCore diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h new file mode 100644 index 0000000000..41b6736d88 --- /dev/null +++ b/src/audio_core/sink_stream.h @@ -0,0 +1,32 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> + +#include "common/common_types.h" + +namespace AudioCore { + +/** + * Accepts samples in stereo signed PCM16 format to be output. Sinks *do not* handle resampling and + * expect the correct sample rate. They are dumb outputs. + */ +class SinkStream { +public: + virtual ~SinkStream() = default; + + /** + * Feed stereo samples to sink. + * @param num_channels Number of channels used. + * @param samples Samples in interleaved stereo PCM16 format. + */ + virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0; +}; + +using SinkStreamPtr = std::unique_ptr<SinkStream>; + +} // namespace AudioCore diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp new file mode 100644 index 0000000000..ad9e2915ce --- /dev/null +++ b/src/audio_core/stream.cpp @@ -0,0 +1,127 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cmath> + +#include "audio_core/sink.h" +#include "audio_core/sink_details.h" +#include "audio_core/stream.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" +#include "core/settings.h" + +namespace AudioCore { + +constexpr size_t MaxAudioBufferCount{32}; + +u32 Stream::GetNumChannels() const { + switch (format) { + case Format::Mono16: + return 1; + case Format::Stereo16: + return 2; + case Format::Multi51Channel16: + return 6; + } + LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + return {}; +} + +Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, + SinkStream& sink_stream, std::string&& name_) + : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, + sink_stream{sink_stream}, name{std::move(name_)} { + + release_event = CoreTiming::RegisterEvent( + name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); +} + +void Stream::Play() { + state = State::Playing; + PlayNextBuffer(); +} + +void Stream::Stop() { + ASSERT_MSG(false, "Unimplemented"); +} + +s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { + const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; + return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); +} + +static void VolumeAdjustSamples(std::vector<s16>& samples) { + const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)}; + + if (volume == 1.0f) { + return; + } + + // Implementation of a volume slider with a dynamic range of 60 dB + const float volume_scale_factor{std::exp(6.90775f * volume) * 0.001f}; + for (auto& sample : samples) { + sample = static_cast<s16>(sample * volume_scale_factor); + } +} + +void Stream::PlayNextBuffer() { + if (!IsPlaying()) { + // Ensure we are in playing state before playing the next buffer + return; + } + + if (active_buffer) { + // Do not queue a new buffer if we are already playing a buffer + return; + } + + if (queued_buffers.empty()) { + // No queued buffers - we are effectively paused + return; + } + + active_buffer = queued_buffers.front(); + queued_buffers.pop(); + + VolumeAdjustSamples(active_buffer->Samples()); + sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); + + CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); +} + +void Stream::ReleaseActiveBuffer() { + ASSERT(active_buffer); + released_buffers.push(std::move(active_buffer)); + release_callback(); + PlayNextBuffer(); +} + +bool Stream::QueueBuffer(BufferPtr&& buffer) { + if (queued_buffers.size() < MaxAudioBufferCount) { + queued_buffers.push(std::move(buffer)); + PlayNextBuffer(); + return true; + } + return false; +} + +bool Stream::ContainsBuffer(Buffer::Tag tag) const { + ASSERT_MSG(false, "Unimplemented"); + return {}; +} + +std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { + std::vector<Buffer::Tag> tags; + for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { + tags.push_back(released_buffers.front()->GetTag()); + released_buffers.pop(); + } + return tags; +} + +} // namespace AudioCore diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h new file mode 100644 index 0000000000..049b92ca92 --- /dev/null +++ b/src/audio_core/stream.h @@ -0,0 +1,102 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include <memory> +#include <string> +#include <vector> +#include <queue> + +#include "audio_core/buffer.h" +#include "audio_core/sink_stream.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "core/core_timing.h" + +namespace AudioCore { + +/** + * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut + */ +class Stream { +public: + /// Audio format of the stream + enum class Format { + Mono16, + Stereo16, + Multi51Channel16, + }; + + /// Callback function type, used to change guest state on a buffer being released + using ReleaseCallback = std::function<void()>; + + Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, + SinkStream& sink_stream, std::string&& name_); + + /// Plays the audio stream + void Play(); + + /// Stops the audio stream + void Stop(); + + /// Queues a buffer into the audio stream, returns true on success + bool QueueBuffer(BufferPtr&& buffer); + + /// Returns true if the audio stream contains a buffer with the specified tag + bool ContainsBuffer(Buffer::Tag tag) const; + + /// Returns a vector of recently released buffers specified by tag + std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count); + + /// Returns true if the stream is currently playing + bool IsPlaying() const { + return state == State::Playing; + } + + /// Returns the number of queued buffers + size_t GetQueueSize() const { + return queued_buffers.size(); + } + + /// Gets the sample rate + u32 GetSampleRate() const { + return sample_rate; + } + + /// Gets the number of channels + u32 GetNumChannels() const; + +private: + /// Current state of the stream + enum class State { + Stopped, + Playing, + }; + + /// Plays the next queued buffer in the audio stream, starting playback if necessary + void PlayNextBuffer(); + + /// Releases the actively playing buffer, signalling that it has been completed + void ReleaseActiveBuffer(); + + /// Gets the number of core cycles when the specified buffer will be released + s64 GetBufferReleaseCycles(const Buffer& buffer) const; + + u32 sample_rate; ///< Sample rate of the stream + Format format; ///< Format of the stream + ReleaseCallback release_callback; ///< Buffer release callback for the stream + State state{State::Stopped}; ///< Playback state of the stream + CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream + BufferPtr active_buffer; ///< Actively playing buffer in the stream + std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream + std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream + SinkStream& sink_stream; ///< Output sink for the stream + std::string name; ///< Name of the stream, must be unique +}; + +using StreamPtr = std::shared_ptr<Stream>; + +} // namespace AudioCore diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 93f1c00444..8b0d34da6e 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -6,7 +6,7 @@ #include <string> -#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM) +#if !defined(ARCHITECTURE_x86_64) #include <cstdlib> // for exit #endif #include "common/common_types.h" @@ -32,8 +32,6 @@ #ifdef ARCHITECTURE_x86_64 #define Crash() __asm__ __volatile__("int $3") -#elif defined(ARCHITECTURE_ARM) -#define Crash() __asm__ __volatile__("trap") #else #define Crash() exit(1) #endif diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 6799a357ae..df2ce80b13 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -32,6 +32,7 @@ #define SDMC_DIR "sdmc" #define NAND_DIR "nand" #define SYSDATA_DIR "sysdata" +#define KEYS_DIR "keys" #define LOG_DIR "log" // Filenames diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index b8dd92b651..7aeda737f4 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -706,6 +706,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP); paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); + paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); // TODO: Put the logs in a better location for each OS paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP); } @@ -736,6 +737,19 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { return paths[path]; } +std::string GetHactoolConfigurationPath() { +#ifdef _WIN32 + PWSTR pw_local_path = nullptr; + if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK) + return ""; + std::string local_path = Common::UTF16ToUTF8(pw_local_path); + CoTaskMemFree(pw_local_path); + return local_path + "\\.switch"; +#else + return GetHomeDirectory() + "/.switch"; +#endif +} + size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); } diff --git a/src/common/file_util.h b/src/common/file_util.h index bc9272d89d..28697d527c 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -23,6 +23,7 @@ namespace FileUtil { enum class UserPath { CacheDir, ConfigDir, + KeysDir, LogDir, NANDDir, RootDir, @@ -125,6 +126,8 @@ bool SetCurrentDir(const std::string& directory); // directory. To be used in "multi-user" mode (that is, installed). const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); +std::string GetHactoolConfigurationPath(); + // Returns the path to where the sys file are std::string GetSysDirectory(); diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index ad9edbcdf0..355abd6823 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -168,26 +168,41 @@ void FileBackend::Write(const Entry& entry) { SUB(Service, AM) \ SUB(Service, AOC) \ SUB(Service, APM) \ + SUB(Service, ARP) \ SUB(Service, BCAT) \ + SUB(Service, BPC) \ + SUB(Service, BTM) \ + SUB(Service, Capture) \ SUB(Service, Fatal) \ + SUB(Service, FGM) \ SUB(Service, Friend) \ SUB(Service, FS) \ SUB(Service, HID) \ + SUB(Service, LBL) \ SUB(Service, LDN) \ SUB(Service, LM) \ + SUB(Service, Migration) \ + SUB(Service, Mii) \ SUB(Service, MM) \ + SUB(Service, NCM) \ + SUB(Service, NFC) \ SUB(Service, NFP) \ SUB(Service, NIFM) \ SUB(Service, NS) \ SUB(Service, NVDRV) \ + SUB(Service, PCIE) \ SUB(Service, PCTL) \ + SUB(Service, PCV) \ SUB(Service, PREPO) \ + SUB(Service, PSC) \ SUB(Service, SET) \ SUB(Service, SM) \ SUB(Service, SPL) \ SUB(Service, SSL) \ SUB(Service, Time) \ + SUB(Service, USB) \ SUB(Service, VI) \ + SUB(Service, WLAN) \ CLS(HW) \ SUB(HW, Memory) \ SUB(HW, LCD) \ @@ -204,6 +219,7 @@ void FileBackend::Write(const Entry& entry) { CLS(Input) \ CLS(Network) \ CLS(Loader) \ + CLS(Crypto) \ CLS(WebService) // GetClassName is a macro defined by Windows.h, grrr... diff --git a/src/common/logging/log.h b/src/common/logging/log.h index ad3cbf5d15..a889ebefa6 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -54,27 +54,42 @@ enum class Class : ClassType { Service_AM, ///< The AM (Applet manager) service Service_AOC, ///< The AOC (AddOn Content) service Service_APM, ///< The APM (Performance) service + Service_ARP, ///< The ARP service Service_Audio, ///< The Audio (Audio control) service Service_BCAT, ///< The BCAT service + Service_BPC, ///< The BPC service + Service_BTM, ///< The BTM service + Service_Capture, ///< The capture service Service_Fatal, ///< The Fatal service + Service_FGM, ///< The FGM service Service_Friend, ///< The friend service Service_FS, ///< The FS (Filesystem) service Service_HID, ///< The HID (Human interface device) service + Service_LBL, ///< The LBL (LCD backlight) service Service_LDN, ///< The LDN (Local domain network) service Service_LM, ///< The LM (Logger) service + Service_Migration, ///< The migration service + Service_Mii, ///< The Mii service Service_MM, ///< The MM (Multimedia) service + Service_NCM, ///< The NCM service + Service_NFC, ///< The NFC (Near-field communication) service Service_NFP, ///< The NFP service Service_NIFM, ///< The NIFM (Network interface) service Service_NS, ///< The NS services Service_NVDRV, ///< The NVDRV (Nvidia driver) service + Service_PCIE, ///< The PCIe service Service_PCTL, ///< The PCTL (Parental control) service + Service_PCV, ///< The PCV service Service_PREPO, ///< The PREPO (Play report) service + Service_PSC, ///< The PSC service Service_SET, ///< The SET (Settings) service Service_SM, ///< The SM (Service manager) service Service_SPL, ///< The SPL service Service_SSL, ///< The SSL service Service_Time, ///< The time service + Service_USB, ///< The USB (Universal Serial Bus) service Service_VI, ///< The VI (Video interface) service + Service_WLAN, ///< The WLAN (Wireless local area network) service HW, ///< Low-level hardware emulation HW_Memory, ///< Memory-map and address translation HW_LCD, ///< LCD register emulation @@ -89,6 +104,7 @@ enum class Class : ClassType { Audio_DSP, ///< The HLE implementation of the DSP Audio_Sink, ///< Emulator audio output backend Loader, ///< ROM loader + Crypto, ///< Cryptographic engine/functions Input, ///< Input emulation Network, ///< Network emulation WebService, ///< Interface to yuzu Web Services diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index c587faefb4..9609cec7c1 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <string> namespace Log { diff --git a/src/common/math_util.h b/src/common/math_util.h index c6a83c9539..343cdd902f 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -19,12 +19,12 @@ inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start template <class T> struct Rectangle { - T left; - T top; - T right; - T bottom; + T left{}; + T top{}; + T right{}; + T bottom{}; - Rectangle() {} + Rectangle() = default; Rectangle(T left, T top, T right, T bottom) : left(left), top(top), right(right), bottom(bottom) {} diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 1f0456aee9..0ca6630328 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -2,12 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cctype> #include <cerrno> #include <cstdio> #include <cstdlib> #include <cstring> -#include <boost/range/algorithm/transform.hpp> #include "common/common_paths.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -24,13 +24,15 @@ namespace Common { /// Make a string lowercase std::string ToLower(std::string str) { - boost::transform(str, str.begin(), ::tolower); + std::transform(str.begin(), str.end(), str.begin(), + [](unsigned char c) { return std::tolower(c); }); return str; } /// Make a string uppercase std::string ToUpper(std::string str) { - boost::transform(str, str.begin(), ::toupper); + std::transform(str.begin(), str.end(), str.begin(), + [](unsigned char c) { return std::toupper(c); }); return str; } diff --git a/src/common/swap.h b/src/common/swap.h index fc7af42801..32af0b6acc 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) { inline u64 swap64(u64 _data) { return _byteswap_uint64(_data); } -#elif ARCHITECTURE_ARM +#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6) inline u16 swap16(u16 _data) { u32 data = _data; __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index a0c731e8c2..edf13bc496 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -33,9 +33,11 @@ public: bool Empty() const { return !read_ptr->next.load(); } + T& Front() const { return read_ptr->current; } + template <typename Arg> void Push(Arg&& t) { // create the element, add it to the queue @@ -108,15 +110,41 @@ private: // single reader, multiple writer queue template <typename T, bool NeedSize = true> -class MPSCQueue : public SPSCQueue<T, NeedSize> { +class MPSCQueue { public: + u32 Size() const { + return spsc_queue.Size(); + } + + bool Empty() const { + return spsc_queue.Empty(); + } + + T& Front() const { + return spsc_queue.Front(); + } + template <typename Arg> void Push(Arg&& t) { std::lock_guard<std::mutex> lock(write_lock); - SPSCQueue<T, NeedSize>::Push(t); + spsc_queue.Push(t); + } + + void Pop() { + return spsc_queue.Pop(); + } + + bool Pop(T& t) { + return spsc_queue.Pop(t); + } + + // not thread-safe + void Clear() { + spsc_queue.Clear(); } private: + SPSCQueue<T, NeedSize> spsc_queue; std::mutex write_lock; }; } // namespace Common diff --git a/src/common/timer.cpp b/src/common/timer.cpp index f0c5b1a430..2dc15e4343 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -3,31 +3,16 @@ // Refer to the license.txt file included. #include <ctime> - #include <fmt/format.h> - -#ifdef _WIN32 -#include <windows.h> -// windows.h needs to be included before other windows headers -#include <mmsystem.h> -#include <sys/timeb.h> -#else -#include <sys/time.h> -#endif #include "common/common_types.h" #include "common/string_util.h" #include "common/timer.h" namespace Common { -u32 Timer::GetTimeMs() { -#ifdef _WIN32 - return timeGetTime(); -#else - struct timeval t; - (void)gettimeofday(&t, nullptr); - return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); -#endif +std::chrono::milliseconds Timer::GetTimeMs() { + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()); } // -------------------------------------------- @@ -63,7 +48,7 @@ void Timer::Update() { // ------------------------------------- // Get the number of milliseconds since the last Update() -u64 Timer::GetTimeDifference() { +std::chrono::milliseconds Timer::GetTimeDifference() { return GetTimeMs() - m_LastTime; } @@ -74,11 +59,11 @@ void Timer::AddTimeDifference() { } // Get the time elapsed since the Start() -u64 Timer::GetTimeElapsed() { +std::chrono::milliseconds Timer::GetTimeElapsed() { // If we have not started yet, return 1 (because then I don't // have to change the FPS calculation in CoreRerecording.cpp . - if (m_StartTime == 0) - return 1; + if (m_StartTime.count() == 0) + return std::chrono::milliseconds(1); // Return the final timer time if the timer is stopped if (!m_Running) @@ -90,49 +75,34 @@ u64 Timer::GetTimeElapsed() { // Get the formatted time elapsed since the Start() std::string Timer::GetTimeElapsedFormatted() const { // If we have not started yet, return zero - if (m_StartTime == 0) + if (m_StartTime.count() == 0) return "00:00:00:000"; // The number of milliseconds since the start. // Use a different value if the timer is stopped. - u64 Milliseconds; + std::chrono::milliseconds Milliseconds; if (m_Running) Milliseconds = GetTimeMs() - m_StartTime; else Milliseconds = m_LastTime - m_StartTime; // Seconds - u32 Seconds = (u32)(Milliseconds / 1000); + std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds); // Minutes - u32 Minutes = Seconds / 60; + std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds); // Hours - u32 Hours = Minutes / 60; + std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds); - std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours, Minutes % 60, Seconds % 60, - Milliseconds % 1000); + std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60, + Seconds.count() % 60, Milliseconds.count() % 1000); return TmpStr; } -// Get current time -void Timer::IncreaseResolution() { -#ifdef _WIN32 - timeBeginPeriod(1); -#endif -} - -void Timer::RestoreResolution() { -#ifdef _WIN32 - timeEndPeriod(1); -#endif -} - // Get the number of seconds since January 1 1970 -u64 Timer::GetTimeSinceJan1970() { - time_t ltime; - time(<ime); - return ((u64)ltime); +std::chrono::seconds Timer::GetTimeSinceJan1970() { + return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs()); } -u64 Timer::GetLocalTimeSinceJan1970() { +std::chrono::seconds Timer::GetLocalTimeSinceJan1970() { time_t sysTime, tzDiff, tzDST; struct tm* gmTime; @@ -149,7 +119,7 @@ u64 Timer::GetLocalTimeSinceJan1970() { gmTime = gmtime(&sysTime); tzDiff = sysTime - mktime(gmTime); - return (u64)(sysTime + tzDiff + tzDST); + return std::chrono::seconds(sysTime + tzDiff + tzDST); } // Return the current time formatted as Minutes:Seconds:Milliseconds @@ -164,30 +134,16 @@ std::string Timer::GetTimeFormatted() { strftime(tmp, 6, "%M:%S", gmTime); -// Now tack on the milliseconds -#ifdef _WIN32 - struct timeb tp; - (void)::ftime(&tp); - return fmt::format("{}:{:03}", tmp, tp.millitm); -#else - struct timeval t; - (void)gettimeofday(&t, nullptr); - return fmt::format("{}:{:03}", tmp, static_cast<int>(t.tv_usec / 1000)); -#endif + u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000; + return fmt::format("{}:{:03}", tmp, milliseconds); } // Returns a timestamp with decimals for precise time comparisons // ---------------- double Timer::GetDoubleTime() { -#ifdef _WIN32 - struct timeb tp; - (void)::ftime(&tp); -#else - struct timeval t; - (void)gettimeofday(&t, nullptr); -#endif // Get continuous timestamp - u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); + u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count()); + double ms = static_cast<u64>(GetTimeMs().count()) % 1000; // Remove a few years. We only really want enough seconds to make // sure that we are detecting actual actions, perhaps 60 seconds is @@ -196,12 +152,7 @@ double Timer::GetDoubleTime() { TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60); // Make a smaller integer that fits in the double - u32 Seconds = (u32)TmpSeconds; -#ifdef _WIN32 - double ms = tp.millitm / 1000.0 / 1000.0; -#else - double ms = t.tv_usec / 1000000.0; -#endif + u32 Seconds = static_cast<u32>(TmpSeconds); double TmpTime = Seconds + ms; return TmpTime; diff --git a/src/common/timer.h b/src/common/timer.h index 78d37426b4..27b521baac 100644 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -4,6 +4,7 @@ #pragma once +#include <chrono> #include <string> #include "common/common_types.h" @@ -18,24 +19,22 @@ public: // The time difference is always returned in milliseconds, regardless of alternative internal // representation - u64 GetTimeDifference(); + std::chrono::milliseconds GetTimeDifference(); void AddTimeDifference(); - static void IncreaseResolution(); - static void RestoreResolution(); - static u64 GetTimeSinceJan1970(); - static u64 GetLocalTimeSinceJan1970(); + static std::chrono::seconds GetTimeSinceJan1970(); + static std::chrono::seconds GetLocalTimeSinceJan1970(); static double GetDoubleTime(); static std::string GetTimeFormatted(); std::string GetTimeElapsedFormatted() const; - u64 GetTimeElapsed(); + std::chrono::milliseconds GetTimeElapsed(); - static u32 GetTimeMs(); + static std::chrono::milliseconds GetTimeMs(); private: - u64 m_LastTime; - u64 m_StartTime; + std::chrono::milliseconds m_LastTime; + std::chrono::milliseconds m_StartTime; bool m_Running; }; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b74e495ef1..0abf7edc1a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -12,6 +12,16 @@ add_library(core STATIC core_timing.h core_timing_util.cpp core_timing_util.h + crypto/aes_util.cpp + crypto/aes_util.h + crypto/encryption_layer.cpp + crypto/encryption_layer.h + crypto/key_manager.cpp + crypto/key_manager.h + crypto/ctr_encryption_layer.cpp + crypto/ctr_encryption_layer.h + file_sys/card_image.cpp + file_sys/card_image.h file_sys/content_archive.cpp file_sys/content_archive.h file_sys/control_metadata.cpp @@ -23,6 +33,8 @@ add_library(core STATIC file_sys/partition_filesystem.h file_sys/program_metadata.cpp file_sys/program_metadata.h + file_sys/romfs.cpp + file_sys/romfs.h file_sys/romfs_factory.cpp file_sys/romfs_factory.h file_sys/savedata_factory.cpp @@ -35,6 +47,8 @@ add_library(core STATIC file_sys/vfs_offset.h file_sys/vfs_real.cpp file_sys/vfs_real.h + file_sys/vfs_vector.cpp + file_sys/vfs_vector.h frontend/emu_window.cpp frontend/emu_window.h frontend/framebuffer_layout.cpp @@ -59,12 +73,10 @@ add_library(core STATIC hle/kernel/hle_ipc.h hle/kernel/kernel.cpp hle/kernel/kernel.h - hle/kernel/memory.cpp - hle/kernel/memory.h hle/kernel/mutex.cpp hle/kernel/mutex.h - hle/kernel/object_address_table.cpp - hle/kernel/object_address_table.h + hle/kernel/object.cpp + hle/kernel/object.h hle/kernel/process.cpp hle/kernel/process.h hle/kernel/resource_limit.cpp @@ -110,23 +122,41 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/idle.cpp + hle/service/am/idle.h + hle/service/am/omm.cpp + hle/service/am/omm.h + hle/service/am/spsm.cpp + hle/service/am/spsm.h hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp hle/service/apm/apm.h hle/service/apm/interface.cpp hle/service/apm/interface.h + hle/service/arp/arp.cpp + hle/service/arp/arp.h + hle/service/audio/audctl.cpp + hle/service/audio/audctl.h + hle/service/audio/auddbg.cpp + hle/service/audio/auddbg.h + hle/service/audio/audin_a.cpp + hle/service/audio/audin_a.h hle/service/audio/audin_u.cpp hle/service/audio/audin_u.h hle/service/audio/audio.cpp hle/service/audio/audio.h + hle/service/audio/audout_a.cpp + hle/service/audio/audout_a.h hle/service/audio/audout_u.cpp hle/service/audio/audout_u.h + hle/service/audio/audrec_a.cpp + hle/service/audio/audrec_a.h hle/service/audio/audrec_u.cpp hle/service/audio/audrec_u.h + hle/service/audio/audren_a.cpp + hle/service/audio/audren_a.h hle/service/audio/audren_u.cpp - hle/service/audio/audren_u.cpp - hle/service/audio/audren_u.h hle/service/audio/audren_u.h hle/service/audio/codecctl.cpp hle/service/audio/codecctl.h @@ -136,6 +166,14 @@ add_library(core STATIC hle/service/bcat/bcat.h hle/service/bcat/module.cpp hle/service/bcat/module.h + hle/service/bpc/bpc.cpp + hle/service/bpc/bpc.h + hle/service/btdrv/btdrv.cpp + hle/service/btdrv/btdrv.h + hle/service/btm/btm.cpp + hle/service/btm/btm.h + hle/service/caps/caps.cpp + hle/service/caps/caps.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp @@ -150,8 +188,14 @@ add_library(core STATIC hle/service/fatal/fatal_u.h hle/service/filesystem/filesystem.cpp hle/service/filesystem/filesystem.h + hle/service/filesystem/fsp_ldr.cpp + hle/service/filesystem/fsp_ldr.h + hle/service/filesystem/fsp_pr.cpp + hle/service/filesystem/fsp_pr.h hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.h + hle/service/fgm/fgm.cpp + hle/service/fgm/fgm.h hle/service/friend/friend.cpp hle/service/friend/friend.h hle/service/friend/interface.cpp @@ -164,14 +208,24 @@ add_library(core STATIC hle/service/hid/irs.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h + hle/service/lbl/lbl.cpp + hle/service/lbl/lbl.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h hle/service/ldr/ldr.cpp hle/service/ldr/ldr.h hle/service/lm/lm.cpp hle/service/lm/lm.h + hle/service/mig/mig.cpp + hle/service/mig/mig.h + hle/service/mii/mii.cpp + hle/service/mii/mii.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h + hle/service/ncm/ncm.cpp + hle/service/ncm/ncm.h + hle/service/nfc/nfc.cpp + hle/service/nfc/nfc.h hle/service/nfp/nfp.cpp hle/service/nfp/nfp.h hle/service/nfp/nfp_user.cpp @@ -209,14 +263,20 @@ add_library(core STATIC hle/service/nvflinger/buffer_queue.h hle/service/nvflinger/nvflinger.cpp hle/service/nvflinger/nvflinger.h + hle/service/pcie/pcie.cpp + hle/service/pcie/pcie.h hle/service/pctl/module.cpp hle/service/pctl/module.h hle/service/pctl/pctl.cpp hle/service/pctl/pctl.h + hle/service/pcv/pcv.cpp + hle/service/pcv/pcv.h hle/service/pm/pm.cpp hle/service/pm/pm.h hle/service/prepo/prepo.cpp hle/service/prepo/prepo.h + hle/service/psc/psc.cpp + hle/service/psc/psc.h hle/service/service.cpp hle/service/service.h hle/service/set/set.cpp @@ -255,6 +315,8 @@ add_library(core STATIC hle/service/time/interface.h hle/service/time/time.cpp hle/service/time/time.h + hle/service/usb/usb.cpp + hle/service/usb/usb.h hle/service/vi/vi.cpp hle/service/vi/vi.h hle/service/vi/vi_m.cpp @@ -263,10 +325,8 @@ add_library(core STATIC hle/service/vi/vi_s.h hle/service/vi/vi_u.cpp hle/service/vi/vi_u.h - hw/hw.cpp - hw/hw.h - hw/lcd.cpp - hw/lcd.h + hle/service/wlan/wlan.cpp + hle/service/wlan/wlan.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/elf.cpp @@ -281,6 +341,8 @@ add_library(core STATIC loader/nro.h loader/nso.cpp loader/nso.h + loader/xci.cpp + loader/xci.h memory.cpp memory.h memory_hook.cpp @@ -299,8 +361,8 @@ add_library(core STATIC create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) +target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn) if (ARCHITECTURE_x86_64) target_sources(core PRIVATE diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 57b8634b93..ceb3f76835 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -10,7 +10,7 @@ #include "core/arm/dynarmic/arm_dynarmic.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" @@ -139,14 +139,12 @@ void ARM_Dynarmic::Step() { } ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) - : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), - jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>( - exclusive_monitor)}, - core_index{core_index} { - ARM_Interface::ThreadContext ctx; + : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, + exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { + ThreadContext ctx; inner_unicorn.SaveContext(ctx); - LoadContext(ctx); PageTableChanged(); + LoadContext(ctx); } ARM_Dynarmic::~ARM_Dynarmic() = default; @@ -205,7 +203,7 @@ u64 ARM_Dynarmic::GetTlsAddress() const { return cb->tpidrro_el0; } -void ARM_Dynarmic::SetTlsAddress(u64 address) { +void ARM_Dynarmic::SetTlsAddress(VAddr address) { cb->tpidrro_el0 = address; } @@ -217,7 +215,7 @@ void ARM_Dynarmic::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } -void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { +void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); ctx.pc = jit->GetPC(); @@ -226,7 +224,7 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { ctx.fpscr = jit->GetFpcr(); } -void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { +void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 4c11f35a49..6bc3494601 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { } Kernel::Thread* thread = Kernel::GetCurrentThread(); SaveContext(thread->context); - if (last_bkpt_hit || (num_instructions == 1)) { + if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; GDBStub::Break(); GDBStub::SendTrap(thread, 5); diff --git a/src/core/core.cpp b/src/core/core.cpp index b7f4b45323..085ba68d0f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -15,11 +15,10 @@ #include "core/hle/service/service.h" #include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" -#include "core/hw/hw.h" #include "core/loader/loader.h" -#include "core/memory_setup.h" #include "core/settings.h" #include "file_sys/vfs_real.h" +#include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Core { @@ -63,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) { // execute. Otherwise, get out of the loop function. if (GDBStub::GetCpuHaltFlag()) { if (GDBStub::GetCpuStepFlag()) { - GDBStub::SetCpuStepFlag(false); tight_loop = false; } else { return ResultStatus::Success; @@ -79,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { } } + if (GDBStub::IsServerEnabled()) { + GDBStub::SetCpuStepFlag(false); + } + return status; } @@ -86,7 +88,7 @@ System::ResultStatus System::SingleStep() { return RunLoop(false); } -System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { +System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); if (!app_loader) { @@ -101,8 +103,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file static_cast<int>(system_mode.second)); switch (system_mode.second) { - case Loader::ResultStatus::ErrorEncrypted: - return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorMissingKeys: + return ResultStatus::ErrorLoader_ErrorMissingKeys; + case Loader::ResultStatus::ErrorDecrypting: + return ResultStatus::ErrorLoader_ErrorDecrypting; case Loader::ResultStatus::ErrorInvalidFormat: return ResultStatus::ErrorLoader_ErrorInvalidFormat; case Loader::ResultStatus::ErrorUnsupportedArch: @@ -112,7 +116,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file } } - ResultStatus init_result{Init(emu_window, system_mode.first.get())}; + ResultStatus init_result{Init(emu_window)}; if (init_result != ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", static_cast<int>(init_result)); @@ -126,8 +130,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file System::Shutdown(); switch (load_result) { - case Loader::ResultStatus::ErrorEncrypted: - return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorMissingKeys: + return ResultStatus::ErrorLoader_ErrorMissingKeys; + case Loader::ResultStatus::ErrorDecrypting: + return ResultStatus::ErrorLoader_ErrorDecrypting; case Loader::ResultStatus::ErrorInvalidFormat: return ResultStatus::ErrorLoader_ErrorInvalidFormat; case Loader::ResultStatus::ErrorUnsupportedArch: @@ -163,7 +169,7 @@ Cpu& System::CpuCore(size_t core_index) { return *cpu_cores[core_index]; } -System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { +System::ResultStatus System::Init(EmuWindow& emu_window) { LOG_DEBUG(HW_Memory, "initialized OK"); CoreTiming::Init(); @@ -176,19 +182,20 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); } - gpu_core = std::make_unique<Tegra::GPU>(); telemetry_session = std::make_unique<Core::TelemetrySession>(); service_manager = std::make_shared<Service::SM::ServiceManager>(); - HW::Init(); - Kernel::Init(system_mode); + Kernel::Init(); Service::Init(service_manager); GDBStub::Init(); - if (!VideoCore::Init(emu_window)) { + renderer = VideoCore::CreateRenderer(emu_window); + if (!renderer->Init()) { return ResultStatus::ErrorVideoCore; } + gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); + // Create threads for CPU cores 1-3, and build thread_to_cpu map // CPU core 0 is run on the main thread thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; @@ -220,11 +227,10 @@ void System::Shutdown() { perf_results.frametime * 1000.0); // Shutdown emulation session - VideoCore::Shutdown(); + renderer.reset(); GDBStub::Shutdown(); Service::Shutdown(); Kernel::Shutdown(); - HW::Shutdown(); service_manager.reset(); telemetry_session.reset(); gpu_core.reset(); diff --git a/src/core/core.h b/src/core/core.h index c123fe4018..c8ca4b247e 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "core/arm/exclusive_monitor.h" #include "core/core_cpu.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -27,6 +27,10 @@ namespace Service::SM { class ServiceManager; } +namespace VideoCore { +class RendererBase; +} + namespace Core { class System { @@ -43,12 +47,14 @@ public: /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { - Success, ///< Succeeded - ErrorNotInitialized, ///< Error trying to use core prior to initialization - ErrorGetLoader, ///< Error finding the correct application loader - ErrorSystemMode, ///< Error determining the system mode - ErrorLoader, ///< Error loading the specified application - ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption + Success, ///< Succeeded + ErrorNotInitialized, ///< Error trying to use core prior to initialization + ErrorGetLoader, ///< Error finding the correct application loader + ErrorSystemMode, ///< Error determining the system mode + ErrorLoader, ///< Error loading the specified application + ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be + ///< found. + ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an /// invalid format ErrorSystemFiles, ///< Error in finding system files @@ -76,16 +82,28 @@ public: */ ResultStatus SingleStep(); + /** + * Invalidate the CPU instruction caches + * This function should only be used by GDB Stub to support breakpoints, memory updates and + * step/continue commands. + */ + void InvalidateCpuInstructionCaches() { + for (auto& cpu : cpu_cores) { + cpu->ArmInterface().ClearInstructionCache(); + } + } + /// Shutdown the emulated system. void Shutdown(); /** * Load an executable application. - * @param emu_window Pointer to the host-system window used for video output and keyboard input. + * @param emu_window Reference to the host-system window used for video output and keyboard + * input. * @param filepath String path to the executable application to load on the host file system. * @returns ResultStatus code, indicating if the operation succeeded. */ - ResultStatus Load(EmuWindow* emu_window, const std::string& filepath); + ResultStatus Load(EmuWindow& emu_window, const std::string& filepath); /** * Indicates if the emulated system is powered on (all subsystems initialized and able to run an @@ -126,11 +144,26 @@ public: /// Gets a CPU interface to the CPU core with the specified index Cpu& CpuCore(size_t core_index); - /// Gets the GPU interface + /// Gets a mutable reference to the GPU interface Tegra::GPU& GPU() { return *gpu_core; } + /// Gets an immutable reference to the GPU interface. + const Tegra::GPU& GPU() const { + return *gpu_core; + } + + /// Gets a mutable reference to the renderer. + VideoCore::RendererBase& Renderer() { + return *renderer; + } + + /// Gets an immutable reference to the renderer. + const VideoCore::RendererBase& Renderer() const { + return *renderer; + } + /// Gets the scheduler for the CPU core that is currently running Kernel::Scheduler& CurrentScheduler() { return *CurrentCpuCore().Scheduler(); @@ -186,14 +219,15 @@ private: /** * Initialize the emulated system. - * @param emu_window Pointer to the host-system window used for video output and keyboard input. - * @param system_mode The system mode. + * @param emu_window Reference to the host-system window used for video output and keyboard + * input. * @return ResultStatus code, indicating if the operation succeeded. */ - ResultStatus Init(EmuWindow* emu_window, u32 system_mode); + ResultStatus Init(EmuWindow& emu_window); /// AppLoader used to load the current executing application std::unique_ptr<Loader::AppLoader> app_loader; + std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<Tegra::GPU> gpu_core; std::shared_ptr<Tegra::DebugContext> debug_context; Kernel::SharedPtr<Kernel::Process> current_process; diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 54e15a7019..46a522fcd4 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -12,7 +12,6 @@ #include "core/arm/unicorn/arm_unicorn.h" #include "core/core_cpu.h" #include "core/core_timing.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/settings.h" diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a1b6f96f10..d3bb6f8188 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user ForceExceptionCheck(cycles_into_future); event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { @@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) { // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -190,7 +190,7 @@ void MoveEvents() { for (Event ev; ts_queue.Pop(ev);) { ev.fifo_order = event_fifo_id++; event_queue.emplace_back(std::move(ev)); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -205,7 +205,7 @@ void Advance() { while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); } @@ -226,8 +226,8 @@ void Idle() { downcount = 0; } -u64 GetGlobalTimeUs() { - return GetTicks() * 1000000 / BASE_CLOCK_RATE; +std::chrono::microseconds GetGlobalTimeUs() { + return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE}; } int GetDowncount() { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 7fe6380ad5..dfa161c0d0 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -17,12 +17,17 @@ * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") */ +#include <chrono> #include <functional> #include <string> #include "common/common_types.h" namespace CoreTiming { +struct EventType; + +using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; + /** * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is * required to end slice -1 and start slice 0 before the first cycle of code is executed. @@ -30,8 +35,6 @@ namespace CoreTiming { void Init(); void Shutdown(); -typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; - /** * This should only be called from the emu thread, if you are calling it any other thread, you are * doing something evil @@ -40,8 +43,6 @@ u64 GetTicks(); u64 GetIdleTicks(); void AddTicks(u64 ticks); -struct EventType; - /** * Returns the event_type identifier. if name is not unique, it will assert. */ @@ -86,7 +87,7 @@ void ClearPendingEvents(); void ForceExceptionCheck(s64 cycles); -u64 GetGlobalTimeUs(); +std::chrono::microseconds GetGlobalTimeUs(); int GetDowncount(); diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp new file mode 100644 index 0000000000..a9876c83e0 --- /dev/null +++ b/src/core/crypto/aes_util.cpp @@ -0,0 +1,115 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <mbedtls/cipher.h> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/crypto/aes_util.h" +#include "core/crypto/key_manager.h" + +namespace Core::Crypto { +namespace { +std::vector<u8> CalculateNintendoTweak(size_t sector_id) { + std::vector<u8> out(0x10); + for (size_t i = 0xF; i <= 0xF; --i) { + out[i] = sector_id & 0xFF; + sector_id >>= 8; + } + return out; +} +} // Anonymous namespace + +static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), + "CTR has incorrect value."); +static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), + "ECB has incorrect value."); +static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), + "XTS has incorrect value."); + +// Structure to hide mbedtls types from header file +struct CipherContext { + mbedtls_cipher_context_t encryption_context; + mbedtls_cipher_context_t decryption_context; +}; + +template <typename Key, size_t KeySize> +Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) + : ctx(std::make_unique<CipherContext>()) { + mbedtls_cipher_init(&ctx->encryption_context); + mbedtls_cipher_init(&ctx->decryption_context); + + ASSERT_MSG((mbedtls_cipher_setup( + &ctx->encryption_context, + mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) || + mbedtls_cipher_setup( + &ctx->decryption_context, + mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0, + "Failed to initialize mbedtls ciphers."); + + ASSERT( + !mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT)); + ASSERT( + !mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT)); + //"Failed to set key on mbedtls ciphers."); +} + +template <typename Key, size_t KeySize> +AESCipher<Key, KeySize>::~AESCipher() { + mbedtls_cipher_free(&ctx->encryption_context); + mbedtls_cipher_free(&ctx->decryption_context); +} + +template <typename Key, size_t KeySize> +void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { + ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || + mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, + "Failed to set IV on mbedtls ciphers."); +} + +template <typename Key, size_t KeySize> +void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const { + auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; + + mbedtls_cipher_reset(context); + + size_t written = 0; + if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { + mbedtls_cipher_update(context, src, size, dest, &written); + if (written != size) { + LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", + size, written); + } + } else { + const auto block_size = mbedtls_cipher_get_block_size(context); + + for (size_t offset = 0; offset < size; offset += block_size) { + auto length = std::min<size_t>(block_size, size - offset); + mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); + if (written != length) { + LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", + length, written); + } + } + } + + mbedtls_cipher_finish(context, nullptr, nullptr); +} + +template <typename Key, size_t KeySize> +void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, + size_t sector_size, Op op) { + if (size % sector_size > 0) { + LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size."); + return; + } + + for (size_t i = 0; i < size; i += sector_size) { + SetIV(CalculateNintendoTweak(sector_id++)); + Transcode<u8, u8>(src + i, sector_size, dest + i, op); + } +} + +template class AESCipher<Key128>; +template class AESCipher<Key256>; +} // namespace Core::Crypto
\ No newline at end of file diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h new file mode 100644 index 0000000000..8ce9d6612a --- /dev/null +++ b/src/core/crypto/aes_util.h @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <type_traits> +#include <vector> +#include "common/common_types.h" +#include "core/file_sys/vfs.h" + +namespace Core::Crypto { + +struct CipherContext; + +enum class Mode { + CTR = 11, + ECB = 2, + XTS = 70, +}; + +enum class Op { + Encrypt, + Decrypt, +}; + +template <typename Key, size_t KeySize = sizeof(Key)> +class AESCipher { + static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); + static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); + +public: + AESCipher(Key key, Mode mode); + + ~AESCipher(); + + void SetIV(std::vector<u8> iv); + + template <typename Source, typename Dest> + void Transcode(const Source* src, size_t size, Dest* dest, Op op) const { + static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, + "Transcode source and destination types must be trivially copyable."); + Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); + } + + void Transcode(const u8* src, size_t size, u8* dest, Op op) const; + + template <typename Source, typename Dest> + void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, + size_t sector_size, Op op) { + static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, + "XTSTranscode source and destination types must be trivially copyable."); + XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, + sector_size, op); + } + + void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, + Op op); + +private: + std::unique_ptr<CipherContext> ctx; +}; +} // namespace Core::Crypto diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp new file mode 100644 index 0000000000..106db02b39 --- /dev/null +++ b/src/core/crypto/ctr_encryption_layer.cpp @@ -0,0 +1,56 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/assert.h" +#include "core/crypto/ctr_encryption_layer.h" + +namespace Core::Crypto { + +CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) + : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), + iv(16, 0) {} + +size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { + if (length == 0) + return 0; + + const auto sector_offset = offset & 0xF; + if (sector_offset == 0) { + UpdateIV(base_offset + offset); + std::vector<u8> raw = base->ReadBytes(length, offset); + if (raw.size() != length) + return Read(data, raw.size(), offset); + cipher.Transcode(raw.data(), length, data, Op::Decrypt); + return length; + } + + // offset does not fall on block boundary (0x10) + std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); + UpdateIV(base_offset + offset - sector_offset); + cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); + size_t read = 0x10 - sector_offset; + + if (length + sector_offset < 0x10) { + std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); + return read; + } + std::memcpy(data, block.data() + sector_offset, read); + return read + Read(data + read, length - read, offset + read); +} + +void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { + const auto length = std::min(iv_.size(), iv.size()); + iv.assign(iv_.cbegin(), iv_.cbegin() + length); +} + +void CTREncryptionLayer::UpdateIV(size_t offset) const { + offset >>= 4; + for (size_t i = 0; i < 8; ++i) { + iv[16 - i - 1] = offset & 0xFF; + offset >>= 8; + } + cipher.SetIV(iv); +} +} // namespace Core::Crypto diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h new file mode 100644 index 0000000000..11b8683c78 --- /dev/null +++ b/src/core/crypto/ctr_encryption_layer.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "core/crypto/aes_util.h" +#include "core/crypto/encryption_layer.h" +#include "core/crypto/key_manager.h" + +namespace Core::Crypto { + +// Sits on top of a VirtualFile and provides CTR-mode AES decription. +class CTREncryptionLayer : public EncryptionLayer { +public: + CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); + + size_t Read(u8* data, size_t length, size_t offset) const override; + + void SetIV(const std::vector<u8>& iv); + +private: + size_t base_offset; + + // Must be mutable as operations modify cipher contexts. + mutable AESCipher<Key128> cipher; + mutable std::vector<u8> iv; + + void UpdateIV(size_t offset) const; +}; + +} // namespace Core::Crypto diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp new file mode 100644 index 0000000000..4204527e33 --- /dev/null +++ b/src/core/crypto/encryption_layer.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/crypto/encryption_layer.h" + +namespace Core::Crypto { + +EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {} + +std::string EncryptionLayer::GetName() const { + return base->GetName(); +} + +size_t EncryptionLayer::GetSize() const { + return base->GetSize(); +} + +bool EncryptionLayer::Resize(size_t new_size) { + return false; +} + +std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const { + return base->GetContainingDirectory(); +} + +bool EncryptionLayer::IsWritable() const { + return false; +} + +bool EncryptionLayer::IsReadable() const { + return true; +} + +size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { + return 0; +} + +bool EncryptionLayer::Rename(std::string_view name) { + return base->Rename(name); +} +} // namespace Core::Crypto diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h new file mode 100644 index 0000000000..7f05af9b4f --- /dev/null +++ b/src/core/crypto/encryption_layer.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/file_sys/vfs.h" + +namespace Core::Crypto { + +// Basically non-functional class that implements all of the methods that are irrelevant to an +// EncryptionLayer. Reduces duplicate code. +class EncryptionLayer : public FileSys::VfsFile { +public: + explicit EncryptionLayer(FileSys::VirtualFile base); + + size_t Read(u8* data, size_t length, size_t offset) const override = 0; + + std::string GetName() const override; + size_t GetSize() const override; + bool Resize(size_t new_size) override; + std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + size_t Write(const u8* data, size_t length, size_t offset) override; + bool Rename(std::string_view name) override; + +protected: + FileSys::VirtualFile base; +}; + +} // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp new file mode 100644 index 0000000000..fc45e7ab57 --- /dev/null +++ b/src/core/crypto/key_manager.cpp @@ -0,0 +1,208 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include <fstream> +#include <locale> +#include <sstream> +#include <string_view> +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/crypto/key_manager.h" +#include "core/settings.h" + +namespace Core::Crypto { + +static u8 ToHexNibble(char c1) { + if (c1 >= 65 && c1 <= 70) + return c1 - 55; + if (c1 >= 97 && c1 <= 102) + return c1 - 87; + if (c1 >= 48 && c1 <= 57) + return c1 - 48; + throw std::logic_error("Invalid hex digit"); +} + +template <size_t Size> +static std::array<u8, Size> HexStringToArray(std::string_view str) { + std::array<u8, Size> out{}; + for (size_t i = 0; i < 2 * Size; i += 2) { + auto d1 = str[i]; + auto d2 = str[i + 1]; + out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2); + } + return out; +} + +std::array<u8, 16> operator""_array16(const char* str, size_t len) { + if (len != 32) + throw std::logic_error("Not of correct size."); + return HexStringToArray<16>(str); +} + +std::array<u8, 32> operator""_array32(const char* str, size_t len) { + if (len != 64) + throw std::logic_error("Not of correct size."); + return HexStringToArray<32>(str); +} + +KeyManager::KeyManager() { + // Initialize keys + const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); + const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); + if (Settings::values.use_dev_keys) { + dev_mode = true; + AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); + } else { + dev_mode = false; + AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false); + } + + AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); +} + +void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { + std::ifstream file(filename); + if (!file.is_open()) + return; + + std::string line; + while (std::getline(file, line)) { + std::vector<std::string> out; + std::stringstream stream(line); + std::string item; + while (std::getline(stream, item, '=')) + out.push_back(std::move(item)); + + if (out.size() != 2) + continue; + + out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); + out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); + + if (is_title_keys) { + auto rights_id_raw = HexStringToArray<16>(out[0]); + u128 rights_id{}; + std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size()); + Key128 key = HexStringToArray<16>(out[1]); + SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + } else { + std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower); + if (s128_file_id.find(out[0]) != s128_file_id.end()) { + const auto index = s128_file_id.at(out[0]); + Key128 key = HexStringToArray<16>(out[1]); + SetKey(index.type, key, index.field1, index.field2); + } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { + const auto index = s256_file_id.at(out[0]); + Key256 key = HexStringToArray<32>(out[1]); + SetKey(index.type, key, index.field1, index.field2); + } + } + } +} + +void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, + const std::string& filename, bool title) { + if (FileUtil::Exists(dir1 + DIR_SEP + filename)) + LoadFromFile(dir1 + DIR_SEP + filename, title); + else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) + LoadFromFile(dir2 + DIR_SEP + filename, title); +} + +bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const { + return s128_keys.find({id, field1, field2}) != s128_keys.end(); +} + +bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const { + return s256_keys.find({id, field1, field2}) != s256_keys.end(); +} + +Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { + if (!HasKey(id, field1, field2)) + return {}; + return s128_keys.at({id, field1, field2}); +} + +Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { + if (!HasKey(id, field1, field2)) + return {}; + return s256_keys.at({id, field1, field2}); +} + +void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { + s128_keys[{id, field1, field2}] = key; +} + +void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { + s256_keys[{id, field1, field2}] = key; +} + +bool KeyManager::KeyFileExists(bool title) { + const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); + const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); + if (title) { + return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || + FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); + } + + if (Settings::values.use_dev_keys) { + return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || + FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); + } + + return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || + FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); +} + +const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { + {"master_key_00", {S128KeyType::Master, 0, 0}}, + {"master_key_01", {S128KeyType::Master, 1, 0}}, + {"master_key_02", {S128KeyType::Master, 2, 0}}, + {"master_key_03", {S128KeyType::Master, 3, 0}}, + {"master_key_04", {S128KeyType::Master, 4, 0}}, + {"package1_key_00", {S128KeyType::Package1, 0, 0}}, + {"package1_key_01", {S128KeyType::Package1, 1, 0}}, + {"package1_key_02", {S128KeyType::Package1, 2, 0}}, + {"package1_key_03", {S128KeyType::Package1, 3, 0}}, + {"package1_key_04", {S128KeyType::Package1, 4, 0}}, + {"package2_key_00", {S128KeyType::Package2, 0, 0}}, + {"package2_key_01", {S128KeyType::Package2, 1, 0}}, + {"package2_key_02", {S128KeyType::Package2, 2, 0}}, + {"package2_key_03", {S128KeyType::Package2, 3, 0}}, + {"package2_key_04", {S128KeyType::Package2, 4, 0}}, + {"titlekek_00", {S128KeyType::Titlekek, 0, 0}}, + {"titlekek_01", {S128KeyType::Titlekek, 1, 0}}, + {"titlekek_02", {S128KeyType::Titlekek, 2, 0}}, + {"titlekek_03", {S128KeyType::Titlekek, 3, 0}}, + {"titlekek_04", {S128KeyType::Titlekek, 4, 0}}, + {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, + {"key_area_key_application_00", + {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_01", + {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_02", + {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_03", + {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_04", + {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}}, +}; + +const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { + {"header_key", {S256KeyType::Header, 0, 0}}, + {"sd_card_save_key", {S256KeyType::SDSave, 0, 0}}, + {"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}}, +}; +} // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h new file mode 100644 index 0000000000..c4c53cefc3 --- /dev/null +++ b/src/core/crypto/key_manager.h @@ -0,0 +1,120 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <vector> +#include <fmt/format.h> +#include "common/common_types.h" + +namespace Core::Crypto { + +using Key128 = std::array<u8, 0x10>; +using Key256 = std::array<u8, 0x20>; +using SHA256Hash = std::array<u8, 0x20>; + +static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); +static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); + +enum class S256KeyType : u64 { + Header, // + SDSave, // + SDNCA, // +}; + +enum class S128KeyType : u64 { + Master, // f1=crypto revision + Package1, // f1=crypto revision + Package2, // f1=crypto revision + Titlekek, // f1=crypto revision + ETicketRSAKek, // + KeyArea, // f1=crypto revision f2=type {app, ocean, system} + SDSeed, // + Titlekey, // f1=rights id LSB f2=rights id MSB +}; + +enum class KeyAreaKeyType : u8 { + Application, + Ocean, + System, +}; + +template <typename KeyType> +struct KeyIndex { + KeyType type; + u64 field1; + u64 field2; + + std::string DebugInfo() const { + u8 key_size = 16; + if constexpr (std::is_same_v<KeyType, S256KeyType>) + key_size = 32; + return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size, + static_cast<u8>(type), field1, field2); + } +}; + +// The following two (== and hash) are so KeyIndex can be a key in unordered_map + +template <typename KeyType> +bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { + return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2); +} + +template <typename KeyType> +bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { + return !operator==(lhs, rhs); +} + +} // namespace Core::Crypto + +namespace std { +template <typename KeyType> +struct hash<Core::Crypto::KeyIndex<KeyType>> { + size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const { + using std::hash; + + return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^ + (hash<u64>()(k.field2) << 1); + } +}; +} // namespace std + +namespace Core::Crypto { + +std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); +std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); + +class KeyManager { +public: + KeyManager(); + + bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; + bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; + + Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; + Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; + + void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); + void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); + + static bool KeyFileExists(bool title); + +private: + std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys; + std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys; + + bool dev_mode; + void LoadFromFile(const std::string& filename, bool is_title_keys); + void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, + const std::string& filename, bool title); + + static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id; + static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id; +}; +} // namespace Core::Crypto diff --git a/src/core/crypto/sha_util.cpp b/src/core/crypto/sha_util.cpp new file mode 100644 index 0000000000..180008a859 --- /dev/null +++ b/src/core/crypto/sha_util.cpp @@ -0,0 +1,5 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +namespace Crypto {} // namespace Crypto diff --git a/src/core/crypto/sha_util.h b/src/core/crypto/sha_util.h new file mode 100644 index 0000000000..fa3fa9d33e --- /dev/null +++ b/src/core/crypto/sha_util.h @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "core/file_sys/vfs.h" +#include "key_manager.h" +#include "mbedtls/cipher.h" + +namespace Crypto { +typedef std::array<u8, 0x20> SHA256Hash; + +inline SHA256Hash operator"" _HASH(const char* data, size_t len) { + if (len != 0x40) + return {}; +} + +} // namespace Crypto diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp new file mode 100644 index 0000000000..395eea8ae0 --- /dev/null +++ b/src/core/file_sys/card_image.cpp @@ -0,0 +1,149 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <string> +#include <core/loader/loader.h> +#include "core/file_sys/card_image.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/vfs_offset.h" + +namespace FileSys { + +XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { + if (file->ReadObject(&header) != sizeof(GamecardHeader)) { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } + + if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } + + PartitionFilesystem main_hfs( + std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset)); + + if (main_hfs.GetStatus() != Loader::ResultStatus::Success) { + status = main_hfs.GetStatus(); + return; + } + + static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", + "logo"}; + + for (XCIPartition partition : + {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { + auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); + if (raw != nullptr) + partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); + } + + auto result = AddNCAFromPartition(XCIPartition::Secure); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + result = AddNCAFromPartition(XCIPartition::Update); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + result = AddNCAFromPartition(XCIPartition::Normal); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + if (GetFormatVersion() >= 0x2) { + result = AddNCAFromPartition(XCIPartition::Logo); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + } + + status = Loader::ResultStatus::Success; +} + +Loader::ResultStatus XCI::GetStatus() const { + return status; +} + +VirtualDir XCI::GetPartition(XCIPartition partition) const { + return partitions[static_cast<size_t>(partition)]; +} + +VirtualDir XCI::GetSecurePartition() const { + return GetPartition(XCIPartition::Secure); +} + +VirtualDir XCI::GetNormalPartition() const { + return GetPartition(XCIPartition::Normal); +} + +VirtualDir XCI::GetUpdatePartition() const { + return GetPartition(XCIPartition::Update); +} + +VirtualDir XCI::GetLogoPartition() const { + return GetPartition(XCIPartition::Logo); +} + +std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { + const auto iter = + std::find_if(ncas.begin(), ncas.end(), + [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; }); + return iter == ncas.end() ? nullptr : *iter; +} + +VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { + auto nca = GetNCAByType(type); + if (nca != nullptr) + return nca->GetBaseFile(); + return nullptr; +} + +std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const { + return {}; +} + +std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const { + return std::vector<std::shared_ptr<VfsDirectory>>(); +} + +std::string XCI::GetName() const { + return file->GetName(); +} + +std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const { + return file->GetContainingDirectory(); +} + +bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { + return false; +} + +Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { + if (partitions[static_cast<size_t>(part)] == nullptr) { + return Loader::ResultStatus::ErrorInvalidFormat; + } + + for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { + if (file->GetExtension() != "nca") + continue; + auto nca = std::make_shared<NCA>(file); + if (nca->GetStatus() == Loader::ResultStatus::Success) + ncas.push_back(std::move(nca)); + } + + return Loader::ResultStatus::Success; +} + +u8 XCI::GetFormatVersion() const { + return GetLogoPartition() == nullptr ? 0x1 : 0x2; +} +} // namespace FileSys diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h new file mode 100644 index 0000000000..e089d737cb --- /dev/null +++ b/src/core/file_sys/card_image.h @@ -0,0 +1,96 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/vfs.h" +#include "core/loader/loader.h" + +namespace FileSys { + +enum class GamecardSize : u8 { + S_1GB = 0xFA, + S_2GB = 0xF8, + S_4GB = 0xF0, + S_8GB = 0xE0, + S_16GB = 0xE1, + S_32GB = 0xE2, +}; + +struct GamecardInfo { + std::array<u8, 0x70> data; +}; +static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); + +struct GamecardHeader { + std::array<u8, 0x100> signature; + u32_le magic; + u32_le secure_area_start; + u32_le backup_area_start; + u8 kek_index; + GamecardSize size; + u8 header_version; + u8 flags; + u64_le package_id; + u64_le valid_data_end; + u128 info_iv; + u64_le hfs_offset; + u64_le hfs_size; + std::array<u8, 0x20> hfs_header_hash; + std::array<u8, 0x20> initial_data_hash; + u32_le secure_mode_flag; + u32_le title_key_flag; + u32_le key_flag; + u32_le normal_area_end; + GamecardInfo info; +}; +static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size."); + +enum class XCIPartition : u8 { Update, Normal, Secure, Logo }; + +class XCI : public ReadOnlyVfsDirectory { +public: + explicit XCI(VirtualFile file); + + Loader::ResultStatus GetStatus() const; + + u8 GetFormatVersion() const; + + VirtualDir GetPartition(XCIPartition partition) const; + VirtualDir GetSecurePartition() const; + VirtualDir GetNormalPartition() const; + VirtualDir GetUpdatePartition() const; + VirtualDir GetLogoPartition() const; + + std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; + VirtualFile GetNCAFileByType(NCAContentType type) const; + + std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; + + std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; + + std::string GetName() const override; + + std::shared_ptr<VfsDirectory> GetParentDirectory() const override; + +protected: + bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; + +private: + Loader::ResultStatus AddNCAFromPartition(XCIPartition part); + + VirtualFile file; + GamecardHeader header{}; + + Loader::ResultStatus status; + + std::vector<VirtualDir> partitions; + std::vector<std::shared_ptr<NCA>> ncas; +}; +} // namespace FileSys diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index d6b20c0476..3529166ac3 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -4,9 +4,12 @@ #include <algorithm> #include <utility> - +#include <boost/optional.hpp> #include "common/logging/log.h" +#include "core/crypto/aes_util.h" +#include "core/crypto/ctr_encryption_layer.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/romfs.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" @@ -28,11 +31,19 @@ enum class NCASectionFilesystemType : u8 { struct NCASectionHeaderBlock { INSERT_PADDING_BYTES(3); NCASectionFilesystemType filesystem_type; - u8 crypto_type; + NCASectionCryptoType crypto_type; INSERT_PADDING_BYTES(3); }; static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); +struct NCASectionRaw { + NCASectionHeaderBlock header; + std::array<u8, 0x138> block_data; + std::array<u8, 0x8> section_ctr; + INSERT_PADDING_BYTES(0xB8); +}; +static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); + struct PFS0Superblock { NCASectionHeaderBlock header_block; std::array<u8, 0x20> hash; @@ -42,79 +53,200 @@ struct PFS0Superblock { u64_le hash_table_size; u64_le pfs0_header_offset; u64_le pfs0_size; - INSERT_PADDING_BYTES(432); + INSERT_PADDING_BYTES(0x1B0); }; static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); -struct IVFCLevel { - u64_le offset; - u64_le size; - u32_le block_size; - u32_le reserved; -}; -static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); - struct RomFSSuperblock { NCASectionHeaderBlock header_block; - u32_le magic; - u32_le magic_number; - INSERT_PADDING_BYTES(8); - std::array<IVFCLevel, 6> levels; - INSERT_PADDING_BYTES(64); + IVFCHeader ivfc; + INSERT_PADDING_BYTES(0x118); }; -static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); +static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); + +union NCASectionHeader { + NCASectionRaw raw; + PFS0Superblock pfs0; + RomFSSuperblock romfs; +}; +static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); + +bool IsValidNCA(const NCAHeader& header) { + // TODO(DarkLordZach): Add NCA2/NCA0 support. + return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); +} + +u8 NCA::GetCryptoRevision() const { + u8 master_key_id = header.crypto_type; + if (header.crypto_type_2 > master_key_id) + master_key_id = header.crypto_type_2; + if (master_key_id > 0) + --master_key_id; + return master_key_id; +} + +boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { + const auto master_key_id = GetCryptoRevision(); + + if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) + return boost::none; + + std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); + Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( + keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index), + Core::Crypto::Mode::ECB); + cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); + + Core::Crypto::Key128 out; + if (type == NCASectionCryptoType::XTS) + std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); + else if (type == NCASectionCryptoType::CTR) + std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); + else + LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", + static_cast<u8>(type)); + u128 out_128{}; + memcpy(out_128.data(), out.data(), 16); + LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", + master_key_id, header.key_index, out_128[1], out_128[0]); + + return out; +} + +boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { + const auto master_key_id = GetCryptoRevision(); + + u128 rights_id{}; + memcpy(rights_id.data(), header.rights_id.data(), 16); + if (rights_id == u128{}) + return boost::none; + + auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); + if (titlekey == Core::Crypto::Key128{}) + return boost::none; + Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( + keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB); + cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt); + + return titlekey; +} + +VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const { + if (!encrypted) + return in; + + switch (s_header.raw.header.crypto_type) { + case NCASectionCryptoType::NONE: + LOG_DEBUG(Crypto, "called with mode=NONE"); + return in; + case NCASectionCryptoType::CTR: + LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); + { + boost::optional<Core::Crypto::Key128> key = boost::none; + if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(), + [](char c) { return c == 0; }) == header.rights_id.end()) { + key = GetKeyAreaKey(NCASectionCryptoType::CTR); + } else { + key = GetTitlekey(); + } + + if (key == boost::none) + return nullptr; + auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( + std::move(in), key.value(), starting_offset); + std::vector<u8> iv(16); + for (u8 i = 0; i < 8; ++i) + iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; + out->SetIV(iv); + return std::static_pointer_cast<VfsFile>(out); + } + case NCASectionCryptoType::XTS: + // TODO(DarkLordZach): Implement XTSEncryptionLayer. + default: + LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", + static_cast<u8>(s_header.raw.header.crypto_type)); + return nullptr; + } +} NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (sizeof(NCAHeader) != file->ReadObject(&header)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); + LOG_ERROR(Loader, "File reader errored out during header read."); + + encrypted = false; if (!IsValidNCA(header)) { - status = Loader::ResultStatus::ErrorInvalidFormat; - return; + NCAHeader dec_header{}; + Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, + Core::Crypto::Op::Decrypt); + if (IsValidNCA(dec_header)) { + header = dec_header; + encrypted = true; + } else { + if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) + status = Loader::ResultStatus::ErrorMissingKeys; + else + status = Loader::ResultStatus::ErrorDecrypting; + return; + } } - std::ptrdiff_t number_sections = + const std::ptrdiff_t number_sections = std::count_if(std::begin(header.section_tables), std::end(header.section_tables), [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); + std::vector<NCASectionHeader> sections(number_sections); + const auto length_sections = SECTION_HEADER_SIZE * number_sections; + + if (encrypted) { + auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); + Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, + Core::Crypto::Op::Decrypt); + } else { + file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); + } + for (std::ptrdiff_t i = 0; i < number_sections; ++i) { - // Seek to beginning of this section. - NCASectionHeaderBlock block{}; - if (sizeof(NCASectionHeaderBlock) != - file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); - - if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { - RomFSSuperblock sb{}; - if (sizeof(RomFSSuperblock) != - file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); + auto section = sections[i]; + if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { const size_t romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + - sb.levels[IVFC_MAX_LEVEL - 1].offset; - const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; - files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); - romfs = files.back(); - } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { - PFS0Superblock sb{}; - // Seek back to beginning of this section. - if (sizeof(PFS0Superblock) != - file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); - + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; + auto dec = + Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset), + romfs_offset); + if (dec != nullptr) { + files.push_back(std::move(dec)); + romfs = files.back(); + } else { + status = Loader::ResultStatus::ErrorMissingKeys; + return; + } + } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * MEDIA_OFFSET_MULTIPLIER) + - sb.pfs0_header_offset; + section.pfs0.pfs0_header_offset; u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - header.section_tables[i].media_offset); - auto npfs = std::make_shared<PartitionFilesystem>( - std::make_shared<OffsetVfsFile>(file, size, offset)); + auto dec = + Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset); + if (dec != nullptr) { + auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec)); - if (npfs->GetStatus() == Loader::ResultStatus::Success) { - dirs.emplace_back(npfs); - if (IsDirectoryExeFS(dirs.back())) - exefs = dirs.back(); + if (npfs->GetStatus() == Loader::ResultStatus::Success) { + dirs.push_back(std::move(npfs)); + if (IsDirectoryExeFS(dirs.back())) + exefs = dirs.back(); + } + } else { + status = Loader::ResultStatus::ErrorMissingKeys; + return; } } } @@ -164,6 +296,10 @@ VirtualDir NCA::GetExeFS() const { return exefs; } +VirtualFile NCA::GetBaseFile() const { + return file; +} + bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { return false; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 0b8b9db612..a8879d9a8f 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -8,14 +8,18 @@ #include <memory> #include <string> #include <vector> - +#include <boost/optional.hpp> #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/crypto/key_manager.h" #include "core/file_sys/partition_filesystem.h" +#include "core/loader/loader.h" namespace FileSys { +union NCASectionHeader; + enum class NCAContentType : u8 { Program = 0, Meta = 1, @@ -24,6 +28,13 @@ enum class NCAContentType : u8 { Data = 4, }; +enum class NCASectionCryptoType : u8 { + NONE = 1, + XTS = 2, + CTR = 3, + BKTR = 4, +}; + struct NCASectionTableEntry { u32_le media_offset; u32_le media_end_offset; @@ -48,7 +59,7 @@ struct NCAHeader { std::array<u8, 0x10> rights_id; std::array<NCASectionTableEntry, 0x4> section_tables; std::array<std::array<u8, 0x20>, 0x4> hash_tables; - std::array<std::array<u8, 0x10>, 0x4> key_area; + std::array<u8, 0x40> key_area; INSERT_PADDING_BYTES(0xC0); }; static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); @@ -58,10 +69,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) { return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; } -inline bool IsValidNCA(const NCAHeader& header) { - return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || - header.magic == Common::MakeMagic('N', 'C', 'A', '3'); -} +bool IsValidNCA(const NCAHeader& header); // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. // After construction, use GetStatus to determine if the file is valid and ready to be used. @@ -81,10 +89,17 @@ public: VirtualFile GetRomFS() const; VirtualDir GetExeFS() const; + VirtualFile GetBaseFile() const; + protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + u8 GetCryptoRevision() const; + boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; + boost::optional<Core::Crypto::Key128> GetTitlekey() const; + VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; + std::vector<VirtualDir> dirs; std::vector<VirtualFile> files; @@ -95,6 +110,10 @@ private: NCAHeader header{}; Loader::ResultStatus status{}; + + bool encrypted; + + Core::Crypto::KeyManager keys; }; } // namespace FileSys diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 521e210789..47e032b197 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -97,9 +97,8 @@ void PartitionFilesystem::PrintDebugInfo() const { LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic); LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); for (u32 i = 0; i < pfs_header.num_entries; i++) { - LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, - pfs_files[i]->GetName(), pfs_files[i]->GetSize(), - dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset()); + LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes)", i, + pfs_files[i]->GetName(), pfs_files[i]->GetSize()); } } diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp new file mode 100644 index 0000000000..ff3ddb29ca --- /dev/null +++ b/src/core/file_sys/romfs.cpp @@ -0,0 +1,124 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_types.h" +#include "common/swap.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs_vector.h" + +namespace FileSys { + +constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF; + +struct TableLocation { + u64_le offset; + u64_le size; +}; +static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size."); + +struct RomFSHeader { + u64_le header_size; + TableLocation directory_hash; + TableLocation directory_meta; + TableLocation file_hash; + TableLocation file_meta; + u64_le data_offset; +}; +static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); + +struct DirectoryEntry { + u32_le sibling; + u32_le child_dir; + u32_le child_file; + u32_le hash; + u32_le name_length; +}; +static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); + +struct FileEntry { + u32_le parent; + u32_le sibling; + u64_le offset; + u64_le size; + u32_le hash; + u32_le name_length; +}; +static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); + +template <typename Entry> +static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { + Entry entry{}; + if (file->ReadObject(&entry, offset) != sizeof(Entry)) + return {}; + std::string string(entry.name_length, '\0'); + if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size()) + return {}; + return {entry, string}; +} + +void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, + std::shared_ptr<VectorVfsDirectory> parent) { + while (true) { + auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); + + parent->AddFile(std::make_shared<OffsetVfsFile>( + file, entry.first.size, entry.first.offset + data_offset, entry.second, parent)); + + if (entry.first.sibling == ROMFS_ENTRY_EMPTY) + break; + + this_file_offset = entry.first.sibling; + } +} + +void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, + u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) { + while (true) { + auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); + auto current = std::make_shared<VectorVfsDirectory>( + std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second); + + if (entry.first.child_file != ROMFS_ENTRY_EMPTY) { + ProcessFile(file, file_offset, data_offset, entry.first.child_file, current); + } + + if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) { + ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir, + current); + } + + parent->AddDirectory(current); + if (entry.first.sibling == ROMFS_ENTRY_EMPTY) + break; + this_dir_offset = entry.first.sibling; + } +} + +VirtualDir ExtractRomFS(VirtualFile file) { + RomFSHeader header{}; + if (file->ReadObject(&header) != sizeof(RomFSHeader)) + return nullptr; + + if (header.header_size != sizeof(RomFSHeader)) + return nullptr; + + const u64 file_offset = header.file_meta.offset; + const u64 dir_offset = header.directory_meta.offset + 4; + + const auto root = + std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, + file->GetContainingDirectory(), file->GetName()); + + ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); + + VirtualDir out = std::move(root); + + while (out->GetSubdirectory("") != nullptr) + out = out->GetSubdirectory(""); + + return out; +} +} // namespace FileSys diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h new file mode 100644 index 0000000000..03a876d229 --- /dev/null +++ b/src/core/file_sys/romfs.h @@ -0,0 +1,35 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_funcs.h" +#include "common/swap.h" +#include "core/file_sys/vfs.h" + +namespace FileSys { + +struct IVFCLevel { + u64_le offset; + u64_le size; + u32_le block_size; + u32_le reserved; +}; +static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); + +struct IVFCHeader { + u32_le magic; + u32_le magic_number; + INSERT_PADDING_BYTES(8); + std::array<IVFCLevel, 6> levels; + INSERT_PADDING_BYTES(64); +}; +static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); + +// Converts a RomFS binary blob to VFS Filesystem +// Returns nullptr on failure +VirtualDir ExtractRomFS(VirtualFile file); + +} // namespace FileSys diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index b99a4fd5be..dae1c16ef0 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -46,6 +46,13 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) { return Write(data.data(), data.size(), offset); } +std::string VfsFile::GetFullPath() const { + if (GetContainingDirectory() == nullptr) + return "/" + GetName(); + + return GetContainingDirectory()->GetFullPath() + "/" + GetName(); +} + std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { auto vec = FileUtil::SplitPathComponents(path); vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), @@ -243,6 +250,13 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); } +std::string VfsDirectory::GetFullPath() const { + if (IsRoot()) + return GetName(); + + return GetParentDirectory()->GetFullPath() + "/" + GetName(); +} + bool ReadOnlyVfsDirectory::IsWritable() const { return false; } @@ -270,4 +284,33 @@ bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { bool ReadOnlyVfsDirectory::Rename(std::string_view name) { return false; } + +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { + if (file1->GetSize() != file2->GetSize()) + return false; + + std::vector<u8> f1_v(block_size); + std::vector<u8> f2_v(block_size); + for (size_t i = 0; i < file1->GetSize(); i += block_size) { + auto f1_vs = file1->Read(f1_v.data(), block_size, i); + auto f2_vs = file2->Read(f2_v.data(), block_size, i); + + if (f1_vs != f2_vs) + return false; + auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); + if (iters.first != f1_v.end() && iters.second != f2_v.end()) + return false; + } + + return true; +} + +bool VfsRawCopy(VirtualFile src, VirtualFile dest) { + if (src == nullptr || dest == nullptr) + return false; + if (!dest->Resize(src->GetSize())) + return false; + std::vector<u8> data = src->ReadAllBytes(); + return dest->WriteBytes(data, 0) == data.size(); +} } // namespace FileSys diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 4a13b8378e..fab9e2b45f 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -113,6 +113,9 @@ struct VfsFile : NonCopyable { // Renames the file to name. Returns whether or not the operation was successsful. virtual bool Rename(std::string_view name) = 0; + + // Returns the full path of this file as a string, recursively + virtual std::string GetFullPath() const; }; // A class representing a directory in an abstract filesystem. @@ -213,6 +216,17 @@ struct VfsDirectory : NonCopyable { return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); } + bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function, + const std::string& file) { + auto file_p = GetFile(file); + if (file_p == nullptr) + return false; + return ReplaceFileWithSubdirectory(file_p, function(file_p)); + } + + // Returns the full path of this directory as a string, recursively + virtual std::string GetFullPath() const; + protected: // Backend for InterpretAsDirectory. // Removes all references to file and adds a reference to dir in the directory's implementation. @@ -230,4 +244,13 @@ struct ReadOnlyVfsDirectory : public VfsDirectory { bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; }; + +// Compare the two files, byte-for-byte, in increments specificed by block_size +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); + +// A method that copies the raw data between two different implementations of VirtualFile. If you +// are using the same implementation, it is probably better to use the Copy method in the parent +// directory of src/dest. +bool VfsRawCopy(VirtualFile src, VirtualFile dest); + } // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index a40331cefd..847cde2f5c 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp @@ -10,8 +10,9 @@ namespace FileSys { OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, - std::string name_) - : file(std::move(file_)), offset(offset_), size(size_), name(std::move(name_)) {} + std::string name_, VirtualDir parent_) + : file(file_), offset(offset_), size(size_), name(std::move(name_)), + parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} std::string OffsetVfsFile::GetName() const { return name.empty() ? file->GetName() : name; @@ -35,7 +36,7 @@ bool OffsetVfsFile::Resize(size_t new_size) { } std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { - return file->GetContainingDirectory(); + return parent; } bool OffsetVfsFile::IsWritable() const { diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index 4f471e3ba4..235970dc5e 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h @@ -17,7 +17,7 @@ namespace FileSys { // the size of this wrapper. struct OffsetVfsFile : public VfsFile { OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, - std::string new_name = ""); + std::string new_name = "", VirtualDir new_parent = nullptr); std::string GetName() const override; size_t GetSize() const override; @@ -44,6 +44,7 @@ private: size_t offset; size_t size; std::string name; + VirtualDir parent; }; } // namespace FileSys diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 9ce2e1efa0..82d54da4a3 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -195,6 +195,12 @@ bool RealVfsDirectory::Rename(std::string_view name) { return FileUtil::Rename(path, new_name); } +std::string RealVfsDirectory::GetFullPath() const { + auto out = path; + std::replace(out.begin(), out.end(), '\\', '/'); + return out; +} + bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { const auto iter = std::find(files.begin(), files.end(), file); if (iter == files.end()) diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 2151211c93..243d585767 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -41,7 +41,7 @@ private: // An implementation of VfsDirectory that represents a directory on the user's computer. struct RealVfsDirectory : public VfsDirectory { - RealVfsDirectory(const std::string& path, Mode perms); + RealVfsDirectory(const std::string& path, Mode perms = Mode::Read); std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; @@ -54,6 +54,7 @@ struct RealVfsDirectory : public VfsDirectory { bool DeleteSubdirectory(std::string_view name) override; bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; + std::string GetFullPath() const override; protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp new file mode 100644 index 0000000000..fda6039606 --- /dev/null +++ b/src/core/file_sys/vfs_vector.cpp @@ -0,0 +1,86 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <utility> +#include "core/file_sys/vfs_vector.h" + +namespace FileSys { +VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, + std::vector<VirtualDir> dirs_, VirtualDir parent_, + std::string name_) + : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), + name(std::move(name_)) {} + +std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { + return files; +} + +std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const { + return dirs; +} + +bool VectorVfsDirectory::IsWritable() const { + return false; +} + +bool VectorVfsDirectory::IsReadable() const { + return true; +} + +std::string VectorVfsDirectory::GetName() const { + return name; +} + +std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const { + return parent; +} + +template <typename T> +static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) { + const auto iter = + std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); + if (iter == vec.end()) + return false; + + vec.erase(iter); + return true; +} + +bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) { + return FindAndRemoveVectorElement(dirs, name); +} + +bool VectorVfsDirectory::DeleteFile(std::string_view name) { + return FindAndRemoveVectorElement(files, name); +} + +bool VectorVfsDirectory::Rename(std::string_view name_) { + name = name_; + return true; +} + +std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) { + return nullptr; +} + +std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) { + return nullptr; +} + +void VectorVfsDirectory::AddFile(VirtualFile file) { + files.push_back(std::move(file)); +} + +void VectorVfsDirectory::AddDirectory(VirtualDir dir) { + dirs.push_back(std::move(dir)); +} + +bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { + if (!DeleteFile(file->GetName())) + return false; + dirs.emplace_back(std::move(dir)); + return true; +} +} // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h new file mode 100644 index 0000000000..ba469647bf --- /dev/null +++ b/src/core/file_sys/vfs_vector.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/file_sys/vfs.h" + +namespace FileSys { + +// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. +// Vector data is supplied upon construction. +struct VectorVfsDirectory : public VfsDirectory { + explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, + std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr, + std::string name = ""); + + std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; + std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + std::shared_ptr<VfsDirectory> GetParentDirectory() const override; + bool DeleteSubdirectory(std::string_view name) override; + bool DeleteFile(std::string_view name) override; + bool Rename(std::string_view name) override; + std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override; + std::shared_ptr<VfsFile> CreateFile(std::string_view name) override; + + virtual void AddFile(VirtualFile file); + virtual void AddDirectory(VirtualDir dir); + +protected: + bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; + +private: + std::vector<VirtualFile> files; + std::vector<VirtualDir> dirs; + + VirtualDir parent; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 5ca573652f..332e5c3d0d 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -37,45 +37,46 @@ #include "core/core.h" #include "core/core_cpu.h" #include "core/gdbstub/gdbstub.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" -const int GDB_BUFFER_SIZE = 10000; +namespace GDBStub { +namespace { +constexpr int GDB_BUFFER_SIZE = 10000; -const char GDB_STUB_START = '$'; -const char GDB_STUB_END = '#'; -const char GDB_STUB_ACK = '+'; -const char GDB_STUB_NACK = '-'; +constexpr char GDB_STUB_START = '$'; +constexpr char GDB_STUB_END = '#'; +constexpr char GDB_STUB_ACK = '+'; +constexpr char GDB_STUB_NACK = '-'; #ifndef SIGTRAP -const u32 SIGTRAP = 5; +constexpr u32 SIGTRAP = 5; #endif #ifndef SIGTERM -const u32 SIGTERM = 15; +constexpr u32 SIGTERM = 15; #endif #ifndef MSG_WAITALL -const u32 MSG_WAITALL = 8; +constexpr u32 MSG_WAITALL = 8; #endif -const u32 LR_REGISTER = 30; -const u32 SP_REGISTER = 31; -const u32 PC_REGISTER = 32; -const u32 CPSR_REGISTER = 33; -const u32 UC_ARM64_REG_Q0 = 34; -const u32 FPSCR_REGISTER = 66; +constexpr u32 LR_REGISTER = 30; +constexpr u32 SP_REGISTER = 31; +constexpr u32 PC_REGISTER = 32; +constexpr u32 CPSR_REGISTER = 33; +constexpr u32 UC_ARM64_REG_Q0 = 34; +constexpr u32 FPSCR_REGISTER = 66; // TODO/WiP - Used while working on support for FPU -const u32 TODO_DUMMY_REG_997 = 997; -const u32 TODO_DUMMY_REG_998 = 998; +constexpr u32 TODO_DUMMY_REG_997 = 997; +constexpr u32 TODO_DUMMY_REG_998 = 998; // For sample XML files see the GDB source /gdb/features // GDB also wants the l character at the start // This XML defines what the registers are for this specific ARM device -static const char* target_xml = +constexpr char target_xml[] = R"(l<?xml version="1.0"?> <!DOCTYPE target SYSTEM "gdb-target.dtd"> <target version="1.0"> @@ -141,30 +142,28 @@ static const char* target_xml = </target> )"; -namespace GDBStub { - -static int gdbserver_socket = -1; +int gdbserver_socket = -1; -static u8 command_buffer[GDB_BUFFER_SIZE]; -static u32 command_length; +u8 command_buffer[GDB_BUFFER_SIZE]; +u32 command_length; -static u32 latest_signal = 0; -static bool memory_break = false; +u32 latest_signal = 0; +bool memory_break = false; -static Kernel::Thread* current_thread = nullptr; -static u32 current_core = 0; +Kernel::Thread* current_thread = nullptr; +u32 current_core = 0; // Binding to a port within the reserved ports range (0-1023) requires root permissions, // so default to a port outside of that range. -static u16 gdbstub_port = 24689; +u16 gdbstub_port = 24689; -static bool halt_loop = true; -static bool step_loop = false; -static bool send_trap = false; +bool halt_loop = true; +bool step_loop = false; +bool send_trap = false; // If set to false, the server will never be started and no // gdbstub-related functions will be executed. -static std::atomic<bool> server_enabled(false); +std::atomic<bool> server_enabled(false); #ifdef _WIN32 WSADATA InitData; @@ -172,23 +171,26 @@ WSADATA InitData; struct Breakpoint { bool active; - PAddr addr; + VAddr addr; u64 len; + std::array<u8, 4> inst; }; -static std::map<u64, Breakpoint> breakpoints_execute; -static std::map<u64, Breakpoint> breakpoints_read; -static std::map<u64, Breakpoint> breakpoints_write; +using BreakpointMap = std::map<VAddr, Breakpoint>; +BreakpointMap breakpoints_execute; +BreakpointMap breakpoints_read; +BreakpointMap breakpoints_write; struct Module { std::string name; - PAddr beg; - PAddr end; + VAddr beg; + VAddr end; }; -static std::vector<Module> modules; +std::vector<Module> modules; +} // Anonymous namespace -void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { +void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) { Module module; if (add_elf_ext) { Common::SplitPath(name, nullptr, &module.name, nullptr); @@ -419,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) { } /** - * Get the list of breakpoints for a given breakpoint type. + * Get the map of breakpoints for a given breakpoint type. * - * @param type Type of breakpoint list. + * @param type Type of breakpoint map. */ -static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { +static BreakpointMap& GetBreakpointMap(BreakpointType type) { switch (type) { case BreakpointType::Execute: return breakpoints_execute; @@ -442,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { * @param type Type of breakpoint. * @param addr Address of breakpoint. */ -static void RemoveBreakpoint(BreakpointType type, PAddr addr) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); +static void RemoveBreakpoint(BreakpointType type, VAddr addr) { + BreakpointMap& p = GetBreakpointMap(type); - auto bp = p.find(static_cast<u64>(addr)); - if (bp != p.end()) { - LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", - bp->second.len, bp->second.addr, static_cast<int>(type)); - p.erase(static_cast<u64>(addr)); + const auto bp = p.find(addr); + if (bp == p.end()) { + return; } + + LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", + bp->second.len, bp->second.addr, static_cast<int>(type)); + Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + p.erase(addr); } -BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); - auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); +BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) { + const BreakpointMap& p = GetBreakpointMap(type); + const auto next_breakpoint = p.lower_bound(addr); BreakpointAddress breakpoint; if (next_breakpoint != p.end()) { @@ -469,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) return breakpoint; } -bool CheckBreakpoint(PAddr addr, BreakpointType type) { +bool CheckBreakpoint(VAddr addr, BreakpointType type) { if (!IsConnected()) { return false; } - std::map<u64, Breakpoint>& p = GetBreakpointList(type); + const BreakpointMap& p = GetBreakpointMap(type); + const auto bp = p.find(addr); - auto bp = p.find(static_cast<u64>(addr)); - if (bp != p.end()) { - u64 len = bp->second.len; + if (bp == p.end()) { + return false; + } - // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints - // no matter if it's a 4-byte or 2-byte instruction. When you execute a - // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on - // two instructions instead of the single instruction you placed the breakpoint - // on. So, as a way to make sure that execution breakpoints are only breaking - // on the instruction that was specified, set the length of an execution - // breakpoint to 1. This should be fine since the CPU should never begin executing - // an instruction anywhere except the beginning of the instruction. - if (type == BreakpointType::Execute) { - len = 1; - } + u64 len = bp->second.len; - if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { - LOG_DEBUG(Debug_GDBStub, - "Found breakpoint type {} @ {:016X}, range: {:016X}" - " - {:016X} ({:X} bytes)", - static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); - return true; - } + // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints + // no matter if it's a 4-byte or 2-byte instruction. When you execute a + // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on + // two instructions instead of the single instruction you placed the breakpoint + // on. So, as a way to make sure that execution breakpoints are only breaking + // on the instruction that was specified, set the length of an execution + // breakpoint to 1. This should be fine since the CPU should never begin executing + // an instruction anywhere except the beginning of the instruction. + if (type == BreakpointType::Execute) { + len = 1; + } + + if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { + LOG_DEBUG(Debug_GDBStub, + "Found breakpoint type {} @ {:016X}, range: {:016X}" + " - {:016X} ({:X} bytes)", + static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); + return true; } return false; @@ -932,6 +940,7 @@ static void WriteMemory() { GdbHexToMem(data.data(), len_pos + 1, len); Memory::WriteBlock(addr, data.data(), len); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); SendReply("OK"); } @@ -951,6 +960,7 @@ static void Step() { step_loop = true; halt_loop = true; send_trap = true; + Core::System::GetInstance().InvalidateCpuInstructionCaches(); } /// Tell the CPU if we hit a memory breakpoint. @@ -967,6 +977,7 @@ static void Continue() { memory_break = false; step_loop = false; halt_loop = false; + Core::System::GetInstance().InvalidateCpuInstructionCaches(); } /** @@ -976,13 +987,17 @@ static void Continue() { * @param addr Address of breakpoint. * @param len Length of breakpoint. */ -static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); +static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { + BreakpointMap& p = GetBreakpointMap(type); Breakpoint breakpoint; breakpoint.active = true; breakpoint.addr = addr; breakpoint.len = len; + Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); + static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}}; + Memory::WriteBlock(addr, btrap.data(), btrap.size()); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); p.insert({addr, breakpoint}); LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", @@ -1016,7 +1031,7 @@ static void AddBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; u64 len = @@ -1065,7 +1080,7 @@ static void RemoveBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index a6b50c26cf..5a36524b2e 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -22,7 +22,7 @@ enum class BreakpointType { }; struct BreakpointAddress { - PAddr address; + VAddr address; BreakpointType type; }; @@ -53,7 +53,7 @@ bool IsServerEnabled(); bool IsConnected(); /// Register module. -void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); +void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true); /** * Signal to the gdbstub server that it should halt CPU execution. @@ -74,7 +74,7 @@ void HandlePacket(); * @param addr Address to search from. * @param type Type of breakpoint. */ -BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); +BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type); /** * Check if a breakpoint of the specified type exists at the given address. @@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy * @param addr Address of breakpoint. * @param type Type of breakpoint. */ -bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); +bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type); /// If set to true, the CPU will halt at the beginning of the next CPU loop. bool GetCpuHaltFlag(); diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 7fb0da408a..d3a7348319 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -5,15 +5,18 @@ #pragma once #include <array> +#include <cstring> +#include <memory> #include <tuple> #include <type_traits> #include <utility> +#include "common/assert.h" +#include "common/common_types.h" #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" namespace IPC { diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 233fdab258..03a954a9f9 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -2,15 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <vector> + #include "common/assert.h" -#include "common/common_funcs.h" #include "common/common_types.h" #include "core/core.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" -#include "core/hle/lock.h" +#include "core/hle/result.h" #include "core/memory.h" namespace Kernel { @@ -30,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) { } // Gets the threads waiting on an address. -static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, - VAddr address) { - auto RetrieveWaitingThreads = +static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { + const auto RetrieveWaitingThreads = [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); auto& thread_list = scheduler->GetThreadList(); @@ -43,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t } }; - // Retrieve a list of all threads that are waiting for this address. - RetrieveWaitingThreads(0, waiting_threads, address); - RetrieveWaitingThreads(1, waiting_threads, address); - RetrieveWaitingThreads(2, waiting_threads, address); - RetrieveWaitingThreads(3, waiting_threads, address); + // Retrieve all threads that are waiting for this address. + std::vector<SharedPtr<Thread>> threads; + RetrieveWaitingThreads(0, threads, address); + RetrieveWaitingThreads(1, threads, address); + RetrieveWaitingThreads(2, threads, address); + RetrieveWaitingThreads(3, threads, address); + // Sort them by priority, such that the highest priority ones come first. - std::sort(waiting_threads.begin(), waiting_threads.end(), + std::sort(threads.begin(), threads.end(), [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { return lhs->current_priority < rhs->current_priority; }); + + return threads; } // Wake up num_to_wake (or all) threads in a vector. @@ -74,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num // Signals an address being waited on. ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { - // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); + std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; @@ -108,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu } // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); + std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); // Determine the modified value depending on the waiting count. s32 updated_value; - if (waiting_threads.size() == 0) { + if (waiting_threads.empty()) { updated_value = value - 1; } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { updated_value = value + 1; diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index f20f3dbc0d..e3657b8e93 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -4,7 +4,9 @@ #pragma once -#include "core/hle/result.h" +#include "common/common_types.h" + +union ResultCode; namespace Kernel { diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index fb2b6f7a3c..134e41ebc6 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -2,19 +2,20 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/assert.h" +#include <tuple> + #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" namespace Kernel { -ClientPort::ClientPort() {} -ClientPort::~ClientPort() {} +ClientPort::ClientPort() = default; +ClientPort::~ClientPort() = default; ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { // Note: Threads do not wait for the server endpoint to call @@ -39,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); } +void ClientPort::ConnectionClosed() { + if (active_sessions == 0) { + return; + } + + --active_sessions; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index a829aeb6d0..b1269ea5c6 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -6,7 +6,7 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { @@ -37,14 +37,20 @@ public: */ ResultVal<SharedPtr<ClientSession>> Connect(); - SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. - u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have - u32 active_sessions; ///< Number of currently open sessions to this port - std::string name; ///< Name of client port (optional) + /** + * Signifies that a previously active connection has been closed, + * decreasing the total number of active connections to this port. + */ + void ConnectionClosed(); private: ClientPort(); ~ClientPort() override; + + SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. + u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have + u32 active_sessions = 0; ///< Number of currently open sessions to this port + std::string name; ///< Name of client port (optional) }; } // namespace Kernel diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 72773d8b19..fdffc648d9 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/assert.h" - #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 2258f95bc8..dabd93ed7c 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -7,7 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 9cae2369ff..5623c4b6a7 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -3,11 +3,9 @@ // Refer to the license.txt file included. #include <algorithm> -#include <map> -#include <vector> #include "common/assert.h" #include "core/hle/kernel/event.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" namespace Kernel { diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index e5c924a755..3c20c05e84 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -5,7 +5,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { @@ -31,10 +31,9 @@ public: return HANDLE_TYPE; } - ResetType reset_type; ///< Current ResetType - - bool signaled; ///< Whether the event has already been signaled - std::string name; ///< Name of event (optional) + ResetType GetResetType() const { + return reset_type; + } bool ShouldWait(Thread* thread) const override; void Acquire(Thread* thread) override; @@ -47,6 +46,11 @@ public: private: Event(); ~Event() override; + + ResetType reset_type; ///< Current ResetType + + bool signaled; ///< Whether the event has already been signaled + std::string name; ///< Name of event (optional) }; } // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 7dd67f80f0..28e21428a5 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index ba968c6667..22ddda6300 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -7,7 +7,7 @@ #include <array> #include <cstddef> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index f243925204..5dd1b68d76 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -2,17 +2,22 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <array> +#include <sstream> #include <utility> #include <boost/range/algorithm_ext/erase.hpp> + #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" #include "core/memory.h" diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 84727f7485..9ce52db24b 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -5,7 +5,6 @@ #pragma once #include <array> -#include <iterator> #include <memory> #include <string> #include <type_traits> @@ -14,7 +13,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/hle/ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1beb985663..1b0cd0abf9 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -4,8 +4,6 @@ #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/object_address_table.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" @@ -16,9 +14,7 @@ namespace Kernel { unsigned int Object::next_object_id; /// Initialize the kernel -void Init(u32 system_mode) { - Kernel::MemoryInit(system_mode); - +void Init() { Kernel::ResourceLimitsInit(); Kernel::ThreadingInit(); Kernel::TimersInit(); @@ -33,13 +29,11 @@ void Init(u32 system_mode) { void Shutdown() { // Free all kernel objects g_handle_table.Clear(); - g_object_address_table.Clear(); Kernel::ThreadingShutdown(); Kernel::TimersShutdown(); Kernel::ResourceLimitsShutdown(); - Kernel::MemoryShutdown(); } } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 402ae900f4..1313114720 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,122 +4,12 @@ #pragma once -#include <cstddef> -#include <string> -#include <utility> -#include <boost/smart_ptr/intrusive_ptr.hpp> -#include "common/assert.h" #include "common/common_types.h" namespace Kernel { -using Handle = u32; - -enum class HandleType : u32 { - Unknown, - Event, - SharedMemory, - Thread, - Process, - AddressArbiter, - Timer, - ResourceLimit, - CodeSet, - ClientPort, - ServerPort, - ClientSession, - ServerSession, -}; - -enum class ResetType { - OneShot, - Sticky, - Pulse, -}; - -class Object : NonCopyable { -public: - virtual ~Object() {} - - /// Returns a unique identifier for the object. For debugging purposes only. - unsigned int GetObjectId() const { - return object_id; - } - - virtual std::string GetTypeName() const { - return "[BAD KERNEL OBJECT TYPE]"; - } - virtual std::string GetName() const { - return "[UNKNOWN KERNEL OBJECT]"; - } - virtual Kernel::HandleType GetHandleType() const = 0; - - /** - * Check if a thread can wait on the object - * @return True if a thread can wait on the object, otherwise false - */ - bool IsWaitable() const { - switch (GetHandleType()) { - case HandleType::Event: - case HandleType::Thread: - case HandleType::Timer: - case HandleType::ServerPort: - case HandleType::ServerSession: - return true; - - case HandleType::Unknown: - case HandleType::SharedMemory: - case HandleType::Process: - case HandleType::AddressArbiter: - case HandleType::ResourceLimit: - case HandleType::CodeSet: - case HandleType::ClientPort: - case HandleType::ClientSession: - return false; - } - - UNREACHABLE(); - } - -public: - static unsigned int next_object_id; - -private: - friend void intrusive_ptr_add_ref(Object*); - friend void intrusive_ptr_release(Object*); - - unsigned int ref_count = 0; - unsigned int object_id = next_object_id++; -}; - -// Special functions used by boost::instrusive_ptr to do automatic ref-counting -inline void intrusive_ptr_add_ref(Object* object) { - ++object->ref_count; -} - -inline void intrusive_ptr_release(Object* object) { - if (--object->ref_count == 0) { - delete object; - } -} - -template <typename T> -using SharedPtr = boost::intrusive_ptr<T>; - -/** - * Attempts to downcast the given Object pointer to a pointer to T. - * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. - */ -template <typename T> -inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { - if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { - return boost::static_pointer_cast<T>(std::move(object)); - } - return nullptr; -} - /// Initialize the kernel with the specified system mode. -void Init(u32 system_mode); +void Init(); /// Shutdown the kernel void Shutdown(); diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp deleted file mode 100644 index 94eac677c5..0000000000 --- a/src/core/hle/kernel/memory.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cinttypes> -#include <map> -#include <memory> -#include <utility> -#include <vector> -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/vm_manager.h" -#include "core/hle/result.h" -#include "core/memory.h" -#include "core/memory_setup.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -namespace Kernel { - -MemoryRegionInfo memory_regions[3]; - -/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system -/// memory configuration type. -static const u32 memory_region_sizes[8][3] = { - // Old 3DS layouts - {0x04000000, 0x02C00000, 0x01400000}, // 0 - {/* This appears to be unused. */}, // 1 - {0x06000000, 0x00C00000, 0x01400000}, // 2 - {0x05000000, 0x01C00000, 0x01400000}, // 3 - {0x04800000, 0x02400000, 0x01400000}, // 4 - {0x02000000, 0x04C00000, 0x01400000}, // 5 - - // New 3DS layouts - {0x07C00000, 0x06400000, 0x02000000}, // 6 - {0x0B200000, 0x02E00000, 0x02000000}, // 7 -}; - -void MemoryInit(u32 mem_type) { - // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead. - ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!"); - ASSERT(mem_type != 1); - - // The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with - // the sizes specified in the memory_region_sizes table. - VAddr base = 0; - for (int i = 0; i < 3; ++i) { - memory_regions[i].base = base; - memory_regions[i].size = memory_region_sizes[mem_type][i]; - memory_regions[i].used = 0; - memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>(); - // Reserve enough space for this region of FCRAM. - // We do not want this block of memory to be relocated when allocating from it. - memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size); - - base += memory_regions[i].size; - } - - // We must've allocated the entire FCRAM by the end - ASSERT(base == Memory::FCRAM_SIZE); -} - -void MemoryShutdown() { - for (auto& region : memory_regions) { - region.base = 0; - region.size = 0; - region.used = 0; - region.linear_heap_memory = nullptr; - } -} - -MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { - switch (region) { - case MemoryRegion::APPLICATION: - return &memory_regions[0]; - case MemoryRegion::SYSTEM: - return &memory_regions[1]; - case MemoryRegion::BASE: - return &memory_regions[2]; - default: - UNREACHABLE(); - } -} - -void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {} - -void MapSharedPages(VMManager& address_space) {} - -} // namespace Kernel diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h deleted file mode 100644 index 61e30c679a..0000000000 --- a/src/core/hle/kernel/memory.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include "common/common_types.h" -#include "core/hle/kernel/process.h" - -namespace Kernel { - -class VMManager; - -struct MemoryRegionInfo { - u64 base; // Not an address, but offset from start of FCRAM - u64 size; - u64 used; - - std::shared_ptr<std::vector<u8>> linear_heap_memory; -}; - -void MemoryInit(u32 mem_type); -void MemoryShutdown(); -MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); - -void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); -void MapSharedPages(VMManager& address_space); - -extern MemoryRegionInfo memory_regions[3]; -} // namespace Kernel diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index feb7b88d20..cb7f58b358 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -3,16 +3,19 @@ // Refer to the license.txt file included. #include <map> +#include <utility> #include <vector> + #include <boost/range/algorithm_ext/erase.hpp> + #include "common/assert.h" #include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/object_address_table.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 3117e7c709..45268bbe9b 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -4,12 +4,10 @@ #pragma once -#include <string> #include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/wait_object.h" -#include "core/hle/result.h" +#include "core/hle/kernel/object.h" + +union ResultCode; namespace Kernel { diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp new file mode 100644 index 0000000000..cdba272f57 --- /dev/null +++ b/src/core/hle/kernel/object.cpp @@ -0,0 +1,35 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "core/hle/kernel/object.h" + +namespace Kernel { + +Object::~Object() = default; + +bool Object::IsWaitable() const { + switch (GetHandleType()) { + case HandleType::Event: + case HandleType::Thread: + case HandleType::Timer: + case HandleType::ServerPort: + case HandleType::ServerSession: + return true; + + case HandleType::Unknown: + case HandleType::SharedMemory: + case HandleType::Process: + case HandleType::AddressArbiter: + case HandleType::ResourceLimit: + case HandleType::CodeSet: + case HandleType::ClientPort: + case HandleType::ClientSession: + return false; + } + + UNREACHABLE(); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h new file mode 100644 index 0000000000..83df68dfd1 --- /dev/null +++ b/src/core/hle/kernel/object.h @@ -0,0 +1,100 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <utility> + +#include <boost/smart_ptr/intrusive_ptr.hpp> + +#include "common/common_types.h" + +namespace Kernel { + +using Handle = u32; + +enum class HandleType : u32 { + Unknown, + Event, + SharedMemory, + Thread, + Process, + AddressArbiter, + Timer, + ResourceLimit, + CodeSet, + ClientPort, + ServerPort, + ClientSession, + ServerSession, +}; + +enum class ResetType { + OneShot, + Sticky, + Pulse, +}; + +class Object : NonCopyable { +public: + virtual ~Object(); + + /// Returns a unique identifier for the object. For debugging purposes only. + unsigned int GetObjectId() const { + return object_id; + } + + virtual std::string GetTypeName() const { + return "[BAD KERNEL OBJECT TYPE]"; + } + virtual std::string GetName() const { + return "[UNKNOWN KERNEL OBJECT]"; + } + virtual HandleType GetHandleType() const = 0; + + /** + * Check if a thread can wait on the object + * @return True if a thread can wait on the object, otherwise false + */ + bool IsWaitable() const; + +public: + static unsigned int next_object_id; + +private: + friend void intrusive_ptr_add_ref(Object*); + friend void intrusive_ptr_release(Object*); + + unsigned int ref_count = 0; + unsigned int object_id = next_object_id++; +}; + +// Special functions used by boost::instrusive_ptr to do automatic ref-counting +inline void intrusive_ptr_add_ref(Object* object) { + ++object->ref_count; +} + +inline void intrusive_ptr_release(Object* object) { + if (--object->ref_count == 0) { + delete object; + } +} + +template <typename T> +using SharedPtr = boost::intrusive_ptr<T>; + +/** + * Attempts to downcast the given Object pointer to a pointer to T. + * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. + */ +template <typename T> +inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { + if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { + return boost::static_pointer_cast<T>(std::move(object)); + } + return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/object_address_table.cpp b/src/core/hle/kernel/object_address_table.cpp deleted file mode 100644 index ca8a833a1d..0000000000 --- a/src/core/hle/kernel/object_address_table.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <utility> - -#include "common/assert.h" -#include "core/hle/kernel/object_address_table.h" - -namespace Kernel { - -ObjectAddressTable g_object_address_table; - -void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) { - ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x{:X}", addr); - objects[addr] = std::move(obj); -} - -void ObjectAddressTable::Close(VAddr addr) { - ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x{:X}", addr); - objects.erase(addr); -} - -SharedPtr<Object> ObjectAddressTable::GetGeneric(VAddr addr) const { - auto iter = objects.find(addr); - if (iter != objects.end()) { - return iter->second; - } - return {}; -} - -void ObjectAddressTable::Clear() { - objects.clear(); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/object_address_table.h b/src/core/hle/kernel/object_address_table.h deleted file mode 100644 index a09004b321..0000000000 --- a/src/core/hle/kernel/object_address_table.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <map> -#include "common/common_types.h" -#include "core/hle/kernel/kernel.h" - -namespace Kernel { - -/** - * This class is used to keep a table of Kernel objects and their respective addresses in emulated - * memory. For certain Switch SVCs, Kernel objects are referenced by an address to an object the - * guest application manages, so we use this table to look these kernel objects up. This is similiar - * to the HandleTable class. - */ -class ObjectAddressTable final : NonCopyable { -public: - ObjectAddressTable() = default; - - /** - * Inserts an object and address pair into the table. - */ - void Insert(VAddr addr, SharedPtr<Object> obj); - - /** - * Closes an object by its address, removing it from the table and decreasing the object's - * ref-count. - * @return `RESULT_SUCCESS` or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - */ - void Close(VAddr addr); - - /** - * Looks up an object by its address. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. - */ - SharedPtr<Object> GetGeneric(VAddr addr) const; - - /** - * Looks up an object by its address while verifying its type. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its - * type differs from the requested one. - */ - template <class T> - SharedPtr<T> Get(VAddr addr) const { - return DynamicObjectCast<T>(GetGeneric(addr)); - } - - /// Closes all addresses held in this table. - void Clear(); - -private: - /// Stores the Object referenced by the address - std::map<VAddr, SharedPtr<Object>> objects; -}; - -extern ObjectAddressTable g_object_address_table; - -} // namespace Kernel diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 0c05060854..edf34c5a3b 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -8,7 +8,6 @@ #include "common/common_funcs.h" #include "common/logging/log.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" @@ -125,14 +124,6 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Mapped) .Unwrap(); - misc_memory_used += stack_size; - memory_region->used += stack_size; - - // Map special address mappings - MapSharedPages(vm_manager); - for (const auto& mapping : address_mappings) { - HandleSpecialMapping(vm_manager, mapping); - } vm_manager.LogLayout(); status = ProcessStatus::Running; @@ -141,37 +132,19 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { } void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { - memory_region = GetMemoryRegion(flags.memory_region); - - auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, - MemoryState memory_state) { + const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, + MemoryState memory_state) { auto vma = vm_manager .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, segment.size, memory_state) .Unwrap(); vm_manager.Reprotect(vma, permissions); - misc_memory_used += segment.size; - memory_region->used += segment.size; }; // Map CodeSet segments - MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic); - MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable); - MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable); -} - -VAddr Process::GetLinearHeapAreaAddress() const { - // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of - // the extra RAM in the n3DS. - return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; -} - -VAddr Process::GetLinearHeapBase() const { - return GetLinearHeapAreaAddress() + memory_region->base; -} - -VAddr Process::GetLinearHeapLimit() const { - return GetLinearHeapBase() + memory_region->size; + MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); + MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); + MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { @@ -206,7 +179,6 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per vm_manager.Reprotect(vma, perms); heap_used = size; - memory_region->used += size; return MakeResult<VAddr>(heap_end - size); } @@ -226,52 +198,6 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { return result; heap_used -= size; - memory_region->used -= size; - - return RESULT_SUCCESS; -} - -ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) { - UNIMPLEMENTED(); - return {}; -} - -ResultCode Process::LinearFree(VAddr target, u32 size) { - auto& linheap_memory = memory_region->linear_heap_memory; - - if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || - target + size < target) { - - return ERR_INVALID_ADDRESS; - } - - if (size == 0) { - return RESULT_SUCCESS; - } - - VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size(); - if (target + size > heap_end) { - return ERR_INVALID_ADDRESS_STATE; - } - - ResultCode result = vm_manager.UnmapRange(target, size); - if (result.IsError()) - return result; - - linear_heap_used -= size; - memory_region->used -= size; - - if (target + size == heap_end) { - // End of linear heap has been freed, so check what's the last allocated block in it and - // reduce the size. - auto vma = vm_manager.FindVMA(target); - ASSERT(vma != vm_manager.vma_map.end()); - ASSERT(vma->second.type == VMAType::Free); - VAddr new_end = vma->second.base; - if (new_end >= GetLinearHeapBase()) { - linheap_memory->resize(new_end - GetLinearHeapBase()); - } - } return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 68e77a4d16..9926891864 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -4,6 +4,7 @@ #pragma once +#include <array> #include <bitset> #include <cstddef> #include <memory> @@ -12,7 +13,7 @@ #include <boost/container/static_vector.hpp> #include "common/bit_field.h" #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" @@ -53,9 +54,14 @@ union ProcessFlags { enum class ProcessStatus { Created, Running, Exited }; class ResourceLimit; -struct MemoryRegionInfo; struct CodeSet final : public Object { + struct Segment { + size_t offset = 0; + VAddr addr = 0; + u32 size = 0; + }; + static SharedPtr<CodeSet> Create(std::string name); std::string GetTypeName() const override { @@ -70,24 +76,38 @@ struct CodeSet final : public Object { return HANDLE_TYPE; } - /// Name of the process - std::string name; + Segment& CodeSegment() { + return segments[0]; + } - std::shared_ptr<std::vector<u8>> memory; + const Segment& CodeSegment() const { + return segments[0]; + } - struct Segment { - size_t offset = 0; - VAddr addr = 0; - u32 size = 0; - }; + Segment& RODataSegment() { + return segments[1]; + } + + const Segment& RODataSegment() const { + return segments[1]; + } + + Segment& DataSegment() { + return segments[2]; + } + + const Segment& DataSegment() const { + return segments[2]; + } - Segment segments[3]; - Segment& code = segments[0]; - Segment& rodata = segments[1]; - Segment& data = segments[2]; + std::shared_ptr<std::vector<u8>> memory; + std::array<Segment, 3> segments; VAddr entrypoint; + /// Name of the process + std::string name; + private: CodeSet(); ~CodeSet() override; @@ -163,12 +183,11 @@ public: // This makes deallocation and reallocation of holes fast and keeps process memory contiguous // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. std::shared_ptr<std::vector<u8>> heap_memory; - // The left/right bounds of the address space covered by heap_memory. - VAddr heap_start = 0, heap_end = 0; - u64 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0; - - MemoryRegionInfo* memory_region = nullptr; + // The left/right bounds of the address space covered by heap_memory. + VAddr heap_start = 0; + VAddr heap_end = 0; + u64 heap_used = 0; /// The Thread Local Storage area is allocated as processes create threads, /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part @@ -179,16 +198,9 @@ public: std::string name; - VAddr GetLinearHeapAreaAddress() const; - VAddr GetLinearHeapBase() const; - VAddr GetLinearHeapLimit() const; - ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); ResultCode HeapFree(VAddr target, u32 size); - ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms); - ResultCode LinearFree(VAddr target, u32 size); - ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index cc689a27aa..0fa141db3a 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -5,7 +5,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index e307eec986..94065c736f 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -2,8 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <utility> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index a3b5fb8ca6..1a4ee8f36b 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -8,9 +8,11 @@ #include <vector> #include "common/common_types.h" #include "common/thread_queue_list.h" -#include "core/arm/arm_interface.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" +class ARM_Interface; + namespace Kernel { class Scheduler final { diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 0b7061403e..7b6211fd8e 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -6,7 +6,7 @@ #include "common/assert.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 9ef4ecc35b..7f6d6b3eb1 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -7,8 +7,9 @@ #include <memory> #include <string> #include <tuple> +#include <vector> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 29b163528b..93560152f9 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -5,6 +5,8 @@ #include <tuple> #include <utility> +#include "common/assert.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -25,7 +27,7 @@ ServerSession::~ServerSession() { // Decrease the port's connection count. if (parent->port) - parent->port->active_sessions--; + parent->port->ConnectionClosed(); // TODO(Subv): Wake up all the ClientSession's waiting threads and set // the SendSyncRequest result to 0xC920181A. diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 2da8070420..2bce54fee1 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -6,12 +6,12 @@ #include <memory> #include <string> -#include "common/assert.h" +#include <vector> + #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" -#include "core/memory.h" namespace Kernel { diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index e69b034a76..7a551f5e4e 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 4bf11c7e2d..21ddc2f7dc 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -3,10 +3,11 @@ // Refer to the license.txt file included. #include <utility> + +#include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" @@ -28,35 +29,17 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u shared_memory->other_permissions = other_permissions; if (address == 0) { - // We need to allocate a block from the Linear Heap ourselves. - // We'll manually allocate some memory from the linear heap in the specified region. - MemoryRegionInfo* memory_region = GetMemoryRegion(region); - auto& linheap_memory = memory_region->linear_heap_memory; - - ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, - "Not enough space in region to allocate shared memory!"); - - shared_memory->backing_block = linheap_memory; - shared_memory->backing_block_offset = linheap_memory->size(); - // Allocate some memory from the end of the linear heap for this region. - linheap_memory->insert(linheap_memory->end(), size, 0); - memory_region->used += size; - - shared_memory->linear_heap_phys_address = - Memory::FCRAM_PADDR + memory_region->base + - static_cast<PAddr>(shared_memory->backing_block_offset); - - // Increase the amount of used linear heap memory for the owner process. - if (shared_memory->owner_process != nullptr) { - shared_memory->owner_process->linear_heap_used += size; - } + shared_memory->backing_block = std::make_shared<std::vector<u8>>(size); + shared_memory->backing_block_offset = 0; // Refresh the address mappings for the current process. if (Core::CurrentProcess() != nullptr) { - Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings( + shared_memory->backing_block.get()); } } else { auto& vm_manager = shared_memory->owner_process->vm_manager; + // The memory is already available and mapped in the owner process. auto vma = vm_manager.FindVMA(address); ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); @@ -72,6 +55,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u } shared_memory->base_address = address; + return shared_memory; } @@ -122,11 +106,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi VAddr target_address = address; - if (base_address == 0 && target_address == 0) { - // Calculate the address at which to map the memory block. - target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address).value(); - } - // Map the memory block into the target process auto result = target_process->vm_manager.MapMemoryBlock( target_address, backing_block, backing_block_offset, size, MemoryState::Shared); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 86f818e905..c50fee615f 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -4,9 +4,12 @@ #pragma once +#include <memory> #include <string> +#include <vector> + #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" @@ -108,9 +111,6 @@ public: SharedPtr<Process> owner_process; /// Address of shared memory block in the owner process if specified. VAddr base_address; - /// Physical address of the shared memory block in the linear heap if no address was specified - /// during creation. - PAddr linear_heap_phys_address; /// Backing memory for this shared memory block. std::shared_ptr<std::vector<u8>> backing_block; /// Offset into the backing block for this shared memory. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 0b439401a2..5db2db6872 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -5,7 +5,10 @@ #include <algorithm> #include <cinttypes> #include <iterator> +#include <mutex> +#include <vector> +#include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "common/string_util.h" @@ -17,7 +20,6 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/object_address_table.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/shared_memory.h" @@ -265,7 +267,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); - auto& vm_manager = Core::CurrentProcess()->vm_manager; + const auto& vm_manager = Core::CurrentProcess()->vm_manager; switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 94735c86eb..b9022feae2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,8 +4,11 @@ #include <algorithm> #include <cinttypes> -#include <list> #include <vector> + +#include <boost/optional.hpp> +#include <boost/range/algorithm_ext/erase.hpp> + #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -17,9 +20,7 @@ #include "core/core_timing_util.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/result.h" @@ -79,8 +80,8 @@ void Thread::Stop() { wait_objects.clear(); // Mark the TLS slot in the thread's page as free. - u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; - u64 tls_slot = + const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; + const u64 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); } @@ -250,13 +251,14 @@ void Thread::ResumeFromWait() { * slot: The index of the first free slot in the indicated page. * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). */ -std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) { +static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot( + const std::vector<std::bitset<8>>& tls_slots) { // Iterate over all the allocated pages, and try to find one where not all slots are used. - for (unsigned page = 0; page < tls_slots.size(); ++page) { + for (std::size_t page = 0; page < tls_slots.size(); ++page) { const auto& page_tls_slots = tls_slots[page]; if (!page_tls_slots.all()) { // We found a page with at least one free slot, find which slot it is - for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) { + for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { if (!page_tls_slots.test(slot)) { return std::make_tuple(page, slot, false); } @@ -331,42 +333,22 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // Find the next available TLS index, and mark it as used auto& tls_slots = owner_process->tls_slots; - bool needs_allocation = true; - u32 available_page; // Which allocated page has free space - u32 available_slot; // Which slot within the page is free - - std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots); + auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots); if (needs_allocation) { - // There are no already-allocated pages with free slots, lets allocate a new one. - // TLS pages are allocated from the BASE region in the linear heap. - MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE); - auto& linheap_memory = memory_region->linear_heap_memory; - - if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { - LOG_ERROR(Kernel_SVC, - "Not enough space in region to allocate a new TLS page for thread"); - return ERR_OUT_OF_MEMORY; - } - - size_t offset = linheap_memory->size(); - - // Allocate some memory from the end of the linear heap for this region. - linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); - memory_region->used += Memory::PAGE_SIZE; - owner_process->linear_heap_used += Memory::PAGE_SIZE; - tls_slots.emplace_back(0); // The page is completely available at the start - available_page = static_cast<u32>(tls_slots.size() - 1); + available_page = tls_slots.size() - 1; available_slot = 0; // Use the first slot in the new page + // Allocate some memory from the end of the linear heap for this region. + const size_t offset = thread->tls_memory->size(); + thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); + auto& vm_manager = owner_process->vm_manager; - vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get()); - // Map the page to the current process' address space. - // TODO(Subv): Find the correct MemoryState for this region. vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - linheap_memory, offset, Memory::PAGE_SIZE, + thread->tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal); } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 6218960d29..adc804248e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -4,15 +4,14 @@ #pragma once +#include <functional> #include <memory> #include <string> -#include <unordered_map> #include <vector> -#include <boost/container/flat_map.hpp> -#include <boost/container/flat_set.hpp> + #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" @@ -266,6 +265,8 @@ public: private: Thread(); ~Thread() override; + + std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>(); }; /** @@ -289,12 +290,6 @@ Thread* GetCurrentThread(); void WaitCurrentThread_Sleep(); /** - * Waits the current thread from an ArbitrateAddress call - * @param wait_address Arbitration address used to resume from wait - */ -void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); - -/** * Stops the current thread and removes it from the thread_list */ void ExitCurrentThread(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 904a3d0a51..282360745c 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -8,7 +8,7 @@ #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index c63f0ed905..4dddc67e09 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -5,7 +5,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 9d26fd7816..479cacb627 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <iterator> #include <utility> #include "common/assert.h" @@ -175,9 +176,9 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { ResultCode VMManager::UnmapRange(VAddr target, u64 size) { CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); - VAddr target_end = target + size; + const VAddr target_end = target + size; - VMAIter end = vma_map.end(); + const VMAIter end = vma_map.end(); // The comparison against the end of the range must be done using addresses since VMAs can be // merged during this process, causing invalidation of the iterators. while (vma != end && vma->second.base < target_end) { @@ -207,9 +208,9 @@ VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission ne ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) { CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); - VAddr target_end = target + size; + const VAddr target_end = target + size; - VMAIter end = vma_map.end(); + const VMAIter end = vma_map.end(); // The comparison against the end of the range must be done using addresses since VMAs can be // merged during this process, causing invalidation of the iterators. while (vma != end && vma->second.base < target_end) { @@ -258,14 +259,14 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { return ERR_INVALID_ADDRESS; } - VirtualMemoryArea& vma = vma_handle->second; + const VirtualMemoryArea& vma = vma_handle->second; if (vma.type != VMAType::Free) { // Region is already allocated return ERR_INVALID_ADDRESS_STATE; } - u64 start_in_vma = base - vma.base; - u64 end_in_vma = start_in_vma + size; + const VAddr start_in_vma = base - vma.base; + const VAddr end_in_vma = start_in_vma + size; if (end_in_vma > vma.size) { // Requested allocation doesn't fit inside VMA @@ -288,17 +289,16 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size); ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target); - VAddr target_end = target + size; + const VAddr target_end = target + size; ASSERT(target_end >= target); ASSERT(target_end <= MAX_ADDRESS); ASSERT(size > 0); VMAIter begin_vma = StripIterConstness(FindVMA(target)); - VMAIter i_end = vma_map.lower_bound(target_end); - for (auto i = begin_vma; i != i_end; ++i) { - if (i->second.type == VMAType::Free) { - return ERR_INVALID_ADDRESS_STATE; - } + const VMAIter i_end = vma_map.lower_bound(target_end); + if (std::any_of(begin_vma, i_end, + [](const auto& entry) { return entry.second.type == VMAType::Free; })) { + return ERR_INVALID_ADDRESS_STATE; } if (target != begin_vma->second.base) { @@ -346,7 +346,7 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { } VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { - VMAIter next_vma = std::next(iter); + const VMAIter next_vma = std::next(iter); if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { iter->second.size += next_vma->second.size; vma_map.erase(next_vma); @@ -382,22 +382,22 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { } } -u64 VMManager::GetTotalMemoryUsage() { +u64 VMManager::GetTotalMemoryUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; } -u64 VMManager::GetTotalHeapUsage() { +u64 VMManager::GetTotalHeapUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0x0; } -VAddr VMManager::GetAddressSpaceBaseAddr() { +VAddr VMManager::GetAddressSpaceBaseAddr() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0x8000000; } -u64 VMManager::GetAddressSpaceSize() { +u64 VMManager::GetAddressSpaceSize() const { LOG_WARNING(Kernel, "(STUBBED) called"); return MAX_ADDRESS; } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 38e4ebcd3c..98bd04bea2 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -190,16 +190,16 @@ public: void LogLayout() const; /// Gets the total memory usage, used by svcGetInfo - u64 GetTotalMemoryUsage(); + u64 GetTotalMemoryUsage() const; /// Gets the total heap usage, used by svcGetInfo - u64 GetTotalHeapUsage(); + u64 GetTotalHeapUsage() const; /// Gets the total address space base address, used by svcGetInfo - VAddr GetAddressSpaceBaseAddr(); + VAddr GetAddressSpaceBaseAddr() const; /// Gets the total address space address size, used by svcGetInfo - u64 GetAddressSpaceSize(); + u64 GetAddressSpaceSize() const; /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 23af346d0c..7681cdee7d 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -5,11 +5,8 @@ #include <algorithm> #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 78bfd8c6c9..b5fbc647b3 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -7,7 +7,7 @@ #include <vector> #include <boost/smart_ptr/intrusive_ptr.hpp> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 0b158e015a..6d15b46eda 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -10,6 +10,7 @@ #include "core/hle/service/acc/acc_su.h" #include "core/hle/service/acc/acc_u0.h" #include "core/hle/service/acc/acc_u1.h" +#include "core/settings.h" namespace Service::Account { @@ -31,13 +32,14 @@ struct ProfileBase { }; static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size"); +// TODO(ogniK): Generate a real user id based on username, md5(username) maybe? static constexpr u128 DEFAULT_USER_ID{1ull, 0ull}; class IProfile final : public ServiceFramework<IProfile> { public: explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) { static const FunctionInfo functions[] = { - {0, nullptr, "Get"}, + {0, &IProfile::Get, "Get"}, {1, &IProfile::GetBase, "GetBase"}, {10, nullptr, "GetImageSize"}, {11, nullptr, "LoadImage"}, @@ -46,14 +48,36 @@ public: } private: + void Get(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + ProfileBase profile_base{}; + profile_base.user_id = user_id; + if (Settings::values.username.size() > profile_base.username.size()) { + std::copy_n(Settings::values.username.begin(), profile_base.username.size(), + profile_base.username.begin()); + } else { + std::copy(Settings::values.username.begin(), Settings::values.username.end(), + profile_base.username.begin()); + } + + IPC::ResponseBuilder rb{ctx, 16}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile_base); + } + void GetBase(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); // TODO(Subv): Retrieve this information from somewhere. ProfileBase profile_base{}; profile_base.user_id = user_id; - profile_base.username = {'y', 'u', 'z', 'u'}; - + if (Settings::values.username.size() > profile_base.username.size()) { + std::copy_n(Settings::values.username.begin(), profile_base.username.size(), + profile_base.username.begin()); + } else { + std::copy(Settings::values.username.begin(), Settings::values.username.end(), + profile_base.username.begin()); + } IPC::ResponseBuilder rb{ctx, 16}; rb.Push(RESULT_SUCCESS); rb.PushRaw(profile_base); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 97ef07bf98..9404d6b8c6 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -11,6 +11,9 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/idle.h" +#include "core/hle/service/am/omm.h" +#include "core/hle/service/am/spsm.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -649,7 +652,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // TODO(bunnei): This should be configurable IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US)); + rb.Push( + static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index))); LOG_DEBUG(Service_AM, "called"); } @@ -689,6 +693,9 @@ void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); + std::make_shared<IdleSys>()->InstallAsService(service_manager); + std::make_shared<OMM>()->InstallAsService(service_manager); + std::make_shared<SPSM>()->InstallAsService(service_manager); } IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp new file mode 100644 index 0000000000..af46e94948 --- /dev/null +++ b/src/core/hle/service/am/idle.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/idle.h" + +namespace Service::AM { + +IdleSys::IdleSys() : ServiceFramework{"idle:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetAutoPowerDownEvent"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h new file mode 100644 index 0000000000..1eb68d2c95 --- /dev/null +++ b/src/core/hle/service/am/idle.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IdleSys final : public ServiceFramework<IdleSys> { +public: + explicit IdleSys(); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp new file mode 100644 index 0000000000..447fe86694 --- /dev/null +++ b/src/core/hle/service/am/omm.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/omm.h" + +namespace Service::AM { + +OMM::OMM() : ServiceFramework{"omm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetOperationMode"}, + {1, nullptr, "GetOperationModeChangeEvent"}, + {2, nullptr, "EnableAudioVisual"}, + {3, nullptr, "DisableAudioVisual"}, + {4, nullptr, "EnterSleepAndWait"}, + {5, nullptr, "GetCradleStatus"}, + {6, nullptr, "FadeInDisplay"}, + {7, nullptr, "FadeOutDisplay"}, + {8, nullptr, "Unknown1"}, + {9, nullptr, "Unknown2"}, + {10, nullptr, "Unknown3"}, + {11, nullptr, "Unknown4"}, + {12, nullptr, "Unknown5"}, + {13, nullptr, "Unknown6"}, + {14, nullptr, "Unknown7"}, + {15, nullptr, "Unknown8"}, + {16, nullptr, "Unknown9"}, + {17, nullptr, "Unknown10"}, + {18, nullptr, "Unknown11"}, + {19, nullptr, "Unknown12"}, + {20, nullptr, "Unknown13"}, + {21, nullptr, "Unknown14"}, + {22, nullptr, "Unknown15"}, + {23, nullptr, "Unknown16"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h new file mode 100644 index 0000000000..49e5d331c4 --- /dev/null +++ b/src/core/hle/service/am/omm.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class OMM final : public ServiceFramework<OMM> { +public: + explicit OMM(); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp new file mode 100644 index 0000000000..a05d433d04 --- /dev/null +++ b/src/core/hle/service/am/spsm.cpp @@ -0,0 +1,30 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/spsm.h" + +namespace Service::AM { + +SPSM::SPSM() : ServiceFramework{"spsm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetState"}, + {1, nullptr, "SleepSystemAndWaitAwake"}, + {2, nullptr, "Unknown1"}, + {3, nullptr, "Unknown2"}, + {4, nullptr, "GetNotificationMessageEventHandle"}, + {5, nullptr, "Unknown3"}, + {6, nullptr, "Unknown4"}, + {7, nullptr, "Unknown5"}, + {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, + {9, nullptr, "ChangeHomeButtonLongPressingTime"}, + {10, nullptr, "Unknown6"}, + {11, nullptr, "Unknown7"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h new file mode 100644 index 0000000000..57dde62e1d --- /dev/null +++ b/src/core/hle/service/am/spsm.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class SPSM final : public ServiceFramework<SPSM> { +public: + explicit SPSM(); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 7a185c6c81..4109cb7f76 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -13,6 +13,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { auto module_ = std::make_shared<Module>(); std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); + std::make_shared<APM_Sys>()->InstallAsService(service_manager); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index ce943d8294..4cd8132f59 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -74,6 +74,31 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISession>(); + + LOG_DEBUG(Service_APM, "called"); +} + +APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestPerformanceMode"}, + {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"}, + {2, nullptr, "GetThrottlingState"}, + {3, nullptr, "GetLastThrottlingState"}, + {4, nullptr, "ClearLastThrottlingState"}, + {5, nullptr, "LoadAndApplySettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISession>(); + + LOG_DEBUG(Service_APM, "called"); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index fa68c7d93b..d14264ad73 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h @@ -19,4 +19,12 @@ private: std::shared_ptr<Module> apm; }; +class APM_Sys final : public ServiceFramework<APM_Sys> { +public: + explicit APM_Sys(); + +private: + void GetPerformanceEvent(Kernel::HLERequestContext& ctx); +}; + } // namespace Service::APM diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp new file mode 100644 index 0000000000..358ef2576e --- /dev/null +++ b/src/core/hle/service/arp/arp.cpp @@ -0,0 +1,75 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/arp/arp.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::ARP { + +class ARP_R final : public ServiceFramework<ARP_R> { +public: + explicit ARP_R() : ServiceFramework{"arp:r"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetApplicationLaunchProperty"}, + {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"}, + {2, nullptr, "GetApplicationControlProperty"}, + {3, nullptr, "GetApplicationControlPropertyWithApplicationId"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IRegistrar final : public ServiceFramework<IRegistrar> { +public: + explicit IRegistrar() : ServiceFramework{"IRegistrar"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Issue"}, + {1, nullptr, "SetApplicationLaunchProperty"}, + {2, nullptr, "SetApplicationControlProperty"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class ARP_W final : public ServiceFramework<ARP_W> { +public: + explicit ARP_W() : ServiceFramework{"arp:w"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"}, + {1, nullptr, "DeleteProperties"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void AcquireRegistrar(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRegistrar>(); + + LOG_DEBUG(Service_ARP, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<ARP_R>()->InstallAsService(sm); + std::make_shared<ARP_W>()->InstallAsService(sm); +} + +} // namespace Service::ARP diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h new file mode 100644 index 0000000000..9d100187ce --- /dev/null +++ b/src/core/hle/service/arp/arp.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::ARP { + +/// Registers all ARP services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::ARP diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp new file mode 100644 index 0000000000..37c3fdcacb --- /dev/null +++ b/src/core/hle/service/audio/audctl.cpp @@ -0,0 +1,45 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audctl.h" + +namespace Service::Audio { + +AudCtl::AudCtl() : ServiceFramework{"audctl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetTargetVolume"}, + {1, nullptr, "SetTargetVolume"}, + {2, nullptr, "GetTargetVolumeMin"}, + {3, nullptr, "GetTargetVolumeMax"}, + {4, nullptr, "IsTargetMute"}, + {5, nullptr, "SetTargetMute"}, + {6, nullptr, "IsTargetConnected"}, + {7, nullptr, "SetDefaultTarget"}, + {8, nullptr, "GetDefaultTarget"}, + {9, nullptr, "GetAudioOutputMode"}, + {10, nullptr, "SetAudioOutputMode"}, + {11, nullptr, "SetForceMutePolicy"}, + {12, nullptr, "GetForceMutePolicy"}, + {13, nullptr, "GetOutputModeSetting"}, + {14, nullptr, "SetOutputModeSetting"}, + {15, nullptr, "SetOutputTarget"}, + {16, nullptr, "SetInputTargetForceEnabled"}, + {17, nullptr, "SetHeadphoneOutputLevelMode"}, + {18, nullptr, "GetHeadphoneOutputLevelMode"}, + {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"}, + {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"}, + {21, nullptr, "GetAudioOutputTargetForPlayReport"}, + {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"}, + {23, nullptr, "SetSystemOutputMasterVolume"}, + {24, nullptr, "GetSystemOutputMasterVolume"}, + {25, nullptr, "GetAudioVolumeDataForPlayReport"}, + {26, nullptr, "UpdateHeadphoneSettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h new file mode 100644 index 0000000000..ed837bdf24 --- /dev/null +++ b/src/core/hle/service/audio/audctl.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudCtl final : public ServiceFramework<AudCtl> { +public: + explicit AudCtl(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp new file mode 100644 index 0000000000..b08c21a206 --- /dev/null +++ b/src/core/hle/service/audio/auddbg.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/auddbg.h" + +namespace Service::Audio { + +AudDbg::AudDbg(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendForDebug"}, + {1, nullptr, "RequestResumeForDebug"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h new file mode 100644 index 0000000000..a2f540b75a --- /dev/null +++ b/src/core/hle/service/audio/auddbg.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudDbg final : public ServiceFramework<AudDbg> { +public: + explicit AudDbg(const char* name); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp new file mode 100644 index 0000000000..a70d5bca4d --- /dev/null +++ b/src/core/hle/service/audio/audin_a.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audin_a.h" + +namespace Service::Audio { + +AudInA::AudInA() : ServiceFramework{"audin:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendAudioIns"}, + {1, nullptr, "RequestResumeAudioIns"}, + {2, nullptr, "GetAudioInsProcessMasterVolume"}, + {3, nullptr, "SetAudioInsProcessMasterVolume"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h new file mode 100644 index 0000000000..e4c75510f0 --- /dev/null +++ b/src/core/hle/service/audio/audin_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudInA final : public ServiceFramework<AudInA> { +public: + explicit AudInA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index d231e91e1d..6b5e156330 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -2,10 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/audio/audctl.h" +#include "core/hle/service/audio/auddbg.h" +#include "core/hle/service/audio/audin_a.h" #include "core/hle/service/audio/audin_u.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/audio/audout_a.h" #include "core/hle/service/audio/audout_u.h" +#include "core/hle/service/audio/audrec_a.h" #include "core/hle/service/audio/audrec_u.h" +#include "core/hle/service/audio/audren_a.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/codecctl.h" #include "core/hle/service/audio/hwopus.h" @@ -13,12 +19,22 @@ namespace Service::Audio { void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<AudCtl>()->InstallAsService(service_manager); + std::make_shared<AudOutA>()->InstallAsService(service_manager); std::make_shared<AudOutU>()->InstallAsService(service_manager); + std::make_shared<AudInA>()->InstallAsService(service_manager); std::make_shared<AudInU>()->InstallAsService(service_manager); + std::make_shared<AudRecA>()->InstallAsService(service_manager); std::make_shared<AudRecU>()->InstallAsService(service_manager); + std::make_shared<AudRenA>()->InstallAsService(service_manager); std::make_shared<AudRenU>()->InstallAsService(service_manager); std::make_shared<CodecCtl>()->InstallAsService(service_manager); std::make_shared<HwOpus>()->InstallAsService(service_manager); + + std::make_shared<AudDbg>("audin:d")->InstallAsService(service_manager); + std::make_shared<AudDbg>("audout:d")->InstallAsService(service_manager); + std::make_shared<AudDbg>("audrec:d")->InstallAsService(service_manager); + std::make_shared<AudDbg>("audren:d")->InstallAsService(service_manager); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp new file mode 100644 index 0000000000..bf8d40157b --- /dev/null +++ b/src/core/hle/service/audio/audout_a.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audout_a.h" + +namespace Service::Audio { + +AudOutA::AudOutA() : ServiceFramework{"audout:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendAudioOuts"}, + {1, nullptr, "RequestResumeAudioOuts"}, + {2, nullptr, "GetAudioOutsProcessMasterVolume"}, + {3, nullptr, "SetAudioOutsProcessMasterVolume"}, + {4, nullptr, "GetAudioOutsProcessRecordVolume"}, + {5, nullptr, "SetAudioOutsProcessRecordVolume"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h new file mode 100644 index 0000000000..91a0691525 --- /dev/null +++ b/src/core/hle/service/audio/audout_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudOutA final : public ServiceFramework<AudOutA> { +public: + explicit AudOutA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 1dcd84d98e..108a7c6eba 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -4,9 +4,10 @@ #include <array> #include <vector> + +#include "audio_core/codec.h" #include "common/logging/log.h" -#include "core/core_timing.h" -#include "core/core_timing_util.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" @@ -14,17 +15,21 @@ namespace Service::Audio { -/// Switch sample rate frequency -constexpr u32 sample_rate{48000}; -/// TODO(st4rk): dynamic number of channels, as I think Switch has support -/// to more audio channels (probably when Docked I guess) -constexpr u32 audio_channels{2}; -/// TODO(st4rk): find a proper value for the audio_ticks -constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)}; +namespace ErrCodes { +enum { + ErrorUnknown = 2, + BufferCountExceeded = 8, +}; +} + +constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; +constexpr int DefaultSampleRate{48000}; class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { + IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) + : ServiceFramework("IAudioOut"), audio_params(audio_params), audio_core(audio_core) { + static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, @@ -32,66 +37,65 @@ public: {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, - {6, nullptr, "ContainsAudioOutBuffer"}, + {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, - {9, nullptr, "GetAudioOutBufferCount"}, + {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, nullptr, "GetAudioOutPlayedSampleCount"}, {11, nullptr, "FlushAudioOutBuffers"}, }; RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - buffer_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); - - // Register event callback to update the Audio Buffer - audio_event = CoreTiming::RegisterEvent( - "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) { - UpdateAudioBuffersCallback(); - CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); - }); - - // Start the audio event - CoreTiming::ScheduleEvent(audio_ticks, audio_event); - } + buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); - ~IAudioOut() { - CoreTiming::UnscheduleEvent(audio_event, 0); + stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, + "IAudioOut", [=]() { buffer_event->Signal(); }); } private: + struct AudioBuffer { + u64_le next; + u64_le buffer; + u64_le buffer_capacity; + u64_le buffer_size; + u64_le offset; + }; + static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size"); + void GetAudioOutState(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(audio_out_state)); + rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); } void StartAudioOut(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - // Start audio - audio_out_state = AudioState::Started; + if (stream->IsPlaying()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown)); + return; + } + + audio_core.StartStream(stream); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void StopAudioOut(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - - // Stop audio - audio_out_state = AudioState::Stopped; + LOG_DEBUG(Service_Audio, "called"); - queue_keys.clear(); + audio_core.StopStream(stream); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -99,101 +103,107 @@ private: } void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); IPC::RequestParser rp{ctx}; - const u64 key{rp.Pop<u64>()}; - queue_keys.insert(queue_keys.begin(), key); + const auto& input_buffer{ctx.ReadBuffer()}; + ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), + "AudioBuffer input is an invalid size!"); + AudioBuffer audio_buffer{}; + std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); + const u64 tag{rp.Pop<u64>()}; + + std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); + Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); + + if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - - // TODO(st4rk): This is how libtransistor currently implements the - // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address - // is used to know which buffer should be filled with data and send again to the service - // through AppendAudioOutBuffer. Check if this is the proper way to do it. - u64 key{0}; - - if (queue_keys.size()) { - key = queue_keys.back(); - queue_keys.pop_back(); - } + LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); + IPC::RequestParser rp{ctx}; + const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; + const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; - ctx.WriteBuffer(&key, sizeof(u64)); + std::vector<u64> tags{released_buffers}; + tags.resize(max_count); + ctx.WriteBuffer(tags); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - // TODO(st4rk): This might be the total of released buffers, needs to be verified on - // hardware - rb.Push<u32>(static_cast<u32>(queue_keys.size())); + rb.Push<u32>(static_cast<u32>(released_buffers.size())); } - void UpdateAudioBuffersCallback() { - if (audio_out_state != AudioState::Started) { - return; - } - - if (queue_keys.empty()) { - return; - } + void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; + const u64 tag{rp.Pop<u64>()}; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(stream->ContainsBuffer(tag)); + } - buffer_event->Signal(); + void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(stream->GetQueueSize())); } - enum class AudioState : u32 { - Started, - Stopped, - }; + AudioCore::AudioOut& audio_core; + AudioCore::StreamPtr stream; - /// This is used to trigger the audio event callback that is going to read the samples from the - /// audio_buffer list and enqueue the samples using the sink (audio_core). - CoreTiming::EventType* audio_event; + AudoutParams audio_params{}; /// This is the evend handle used to check if the audio buffer was released Kernel::SharedPtr<Kernel::Event> buffer_event; - - /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor - /// uses the key as an address in the App, so we need to return when the - /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because - /// libtransistor uses the key returned as an pointer. - std::vector<u64> queue_keys; - - AudioState audio_out_state; }; void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); IPC::RequestParser rp{ctx}; - constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; - ctx.WriteBuffer(audio_interface); + ctx.WriteBuffer(DefaultDevice); IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); rb.Push(RESULT_SUCCESS); - // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, - // it's highly possible to have more than one interface (despite that libtransistor requires - // only one). - rb.Push<u32>(1); + rb.Push<u32>(1); // Amount of audio devices } void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - if (!audio_out_interface) { - audio_out_interface = std::make_shared<IAudioOut>(); + ctx.WriteBuffer(DefaultDevice); + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<AudoutParams>()}; + if (params.channel_count <= 2) { + // Mono does not exist for audout + params.channel_count = 2; + } else { + params.channel_count = 6; } + if (!params.sample_rate) { + params.sample_rate = DefaultSampleRate; + } + + // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl + // will likely need to be updated as well. + ASSERT_MSG(!audio_out_interface, "Unimplemented"); + audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core); IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(sample_rate); - rb.Push<u32>(audio_channels); - rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); - rb.Push<u32>(0); // This field is unknown + rb.Push<u32>(DefaultSampleRate); + rb.Push<u32>(params.channel_count); + rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); + rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); } @@ -203,6 +213,7 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") { {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; RegisterHandlers(functions); + audio_core = std::make_unique<AudioCore::AudioOut>(); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 847d86aa6e..fd491f65d7 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -4,6 +4,7 @@ #pragma once +#include "audio_core/audio_out.h" #include "core/hle/service/service.h" namespace Kernel { @@ -12,6 +13,18 @@ class HLERequestContext; namespace Service::Audio { +struct AudoutParams { + s32_le sample_rate; + u16_le channel_count; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); + +enum class AudioState : u32 { + Started, + Stopped, +}; + class IAudioOut; class AudOutU final : public ServiceFramework<AudOutU> { @@ -21,19 +34,10 @@ public: private: std::shared_ptr<IAudioOut> audio_out_interface; + std::unique_ptr<AudioCore::AudioOut> audio_core; void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); - - enum class PcmFormat : u32 { - Invalid = 0, - Int8 = 1, - Int16 = 2, - Int24 = 3, - Int32 = 4, - PcmFloat = 5, - Adpcm = 6, - }; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp new file mode 100644 index 0000000000..016eabf538 --- /dev/null +++ b/src/core/hle/service/audio/audrec_a.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audrec_a.h" + +namespace Service::Audio { + +AudRecA::AudRecA() : ServiceFramework{"audrec:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendFinalOutputRecorders"}, + {1, nullptr, "RequestResumeFinalOutputRecorders"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h new file mode 100644 index 0000000000..9685047f2f --- /dev/null +++ b/src/core/hle/service/audio/audrec_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudRecA final : public ServiceFramework<AudRecA> { +public: + explicit AudRecA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp new file mode 100644 index 0000000000..616ff3dc45 --- /dev/null +++ b/src/core/hle/service/audio/audren_a.cpp @@ -0,0 +1,26 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audren_a.h" + +namespace Service::Audio { + +AudRenA::AudRenA() : ServiceFramework{"audren:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendAudioRenderers"}, + {1, nullptr, "RequestResumeAudioRenderers"}, + {2, nullptr, "GetAudioRenderersProcessMasterVolume"}, + {3, nullptr, "SetAudioRenderersProcessMasterVolume"}, + {4, nullptr, "RegisterAppletResourceUserId"}, + {5, nullptr, "UnregisterAppletResourceUserId"}, + {6, nullptr, "GetAudioRenderersProcessRecordVolume"}, + {7, nullptr, "SetAudioRenderersProcessRecordVolume"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h new file mode 100644 index 0000000000..5ecf2e184b --- /dev/null +++ b/src/core/hle/service/audio/audren_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudRenA final : public ServiceFramework<AudRenA> { +public: + explicit AudRenA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6aed9e2fa0..f99304de54 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -15,13 +15,10 @@ namespace Service::Audio { -/// TODO(bunnei): Find a proper value for the audio_ticks -constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)}; - class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(AudioRendererParameter audren_params) - : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { + explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) + : ServiceFramework("IAudioRenderer") { static const FunctionInfo functions[] = { {0, nullptr, "GetAudioRendererSampleRate"}, {1, nullptr, "GetAudioRendererSampleCount"}, @@ -39,21 +36,8 @@ public: RegisterHandlers(functions); system_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); - - // Register event callback to update the Audio Buffer - audio_event = CoreTiming::RegisterEvent( - "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) { - UpdateAudioCallback(); - CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); - }); - - // Start the audio event - CoreTiming::ScheduleEvent(audio_ticks, audio_event); - voice_status_list.resize(worker_params.voice_count); - } - ~IAudioRenderer() { - CoreTiming::UnscheduleEvent(audio_event, 0); + Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); + renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event); } private: @@ -62,60 +46,9 @@ private: } void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { - UpdateDataHeader config{}; - auto buf = ctx.ReadBuffer(); - std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader)); - u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); - - std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); - std::memcpy(mem_pool_info.data(), - buf.data() + sizeof(UpdateDataHeader) + config.behavior_size, - memory_pool_count * sizeof(MemoryPoolInfo)); - - std::vector<VoiceInfo> voice_info(worker_params.voice_count); - std::memcpy(voice_info.data(), - buf.data() + sizeof(UpdateDataHeader) + config.behavior_size + - config.memory_pools_size + config.voice_resource_size, - worker_params.voice_count * sizeof(VoiceInfo)); - - UpdateDataHeader response_data{worker_params}; - - ASSERT(ctx.GetWriteBufferSize() == response_data.total_size); - - std::vector<u8> output(response_data.total_size); - std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader)); - std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); - for (unsigned i = 0; i < memory_pool.size(); i++) { - if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach) - memory_pool[i].state = MemoryPoolStates::Attached; - else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach) - memory_pool[i].state = MemoryPoolStates::Detached; - } - std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(), - response_data.memory_pools_size); - - for (unsigned i = 0; i < voice_info.size(); i++) { - if (voice_info[i].is_new) { - voice_status_list[i].played_sample_count = 0; - voice_status_list[i].wave_buffer_consumed = 0; - } else if (voice_info[i].play_state == (u8)PlayStates::Started) { - for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) { - voice_status_list[i].played_sample_count += - (voice_info[i].wave_buffer[buff_idx].end_sample_offset - - voice_info[i].wave_buffer[buff_idx].start_sample_offset) / - 2; - voice_status_list[i].wave_buffer_consumed++; - } - } - } - std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size, - voice_status_list.data(), response_data.voices_size); - - ctx.WriteBuffer(output); - + ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_Audio, "(STUBBED) called"); } @@ -136,8 +69,6 @@ private: } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { - // system_event->Signal(); - IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(system_event); @@ -145,131 +76,8 @@ private: LOG_WARNING(Service_Audio, "(STUBBED) called"); } - enum class MemoryPoolStates : u32 { // Should be LE - Invalid = 0x0, - Unknown = 0x1, - RequestDetach = 0x2, - Detached = 0x3, - RequestAttach = 0x4, - Attached = 0x5, - Released = 0x6, - }; - - enum class PlayStates : u8 { - Started = 0, - Stopped = 1, - }; - - struct MemoryPoolEntry { - MemoryPoolStates state; - u32_le unknown_4; - u32_le unknown_8; - u32_le unknown_c; - }; - static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size"); - - struct MemoryPoolInfo { - u64_le pool_address; - u64_le pool_size; - MemoryPoolStates pool_state; - INSERT_PADDING_WORDS(3); // Unknown - }; - static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size"); - - struct UpdateDataHeader { - UpdateDataHeader() {} - - explicit UpdateDataHeader(const AudioRendererParameter& config) { - revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision - behavior_size = 0xb0; - memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; - voices_size = config.voice_count * 0x10; - voice_resource_size = 0x0; - effects_size = config.effect_count * 0x10; - mixes_size = 0x0; - sinks_size = config.sink_count * 0x20; - performance_manager_size = 0x10; - total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + - voices_size + effects_size + sinks_size + performance_manager_size; - } - - u32_le revision; - u32_le behavior_size; - u32_le memory_pools_size; - u32_le voices_size; - u32_le voice_resource_size; - u32_le effects_size; - u32_le mixes_size; - u32_le sinks_size; - u32_le performance_manager_size; - INSERT_PADDING_WORDS(6); - u32_le total_size; - }; - static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); - - struct BiquadFilter { - u8 enable; - INSERT_PADDING_BYTES(1); - s16_le numerator[3]; - s16_le denominator[2]; - }; - static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size"); - - struct WaveBuffer { - u64_le buffer_addr; - u64_le buffer_sz; - s32_le start_sample_offset; - s32_le end_sample_offset; - u8 loop; - u8 end_of_stream; - u8 sent_to_server; - INSERT_PADDING_BYTES(5); - u64 context_addr; - u64 context_sz; - INSERT_PADDING_BYTES(8); - }; - static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); - - struct VoiceInfo { - u32_le id; - u32_le node_id; - u8 is_new; - u8 is_in_use; - u8 play_state; - u8 sample_format; - u32_le sample_rate; - u32_le priority; - u32_le sorting_order; - u32_le channel_count; - float_le pitch; - float_le volume; - BiquadFilter biquad_filter[2]; - u32_le wave_buffer_count; - u16_le wave_buffer_head; - INSERT_PADDING_BYTES(6); - u64_le additional_params_addr; - u64_le additional_params_sz; - u32_le mix_id; - u32_le splitter_info_id; - WaveBuffer wave_buffer[4]; - u32_le voice_channel_resource_ids[6]; - INSERT_PADDING_BYTES(24); - }; - static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size"); - - struct VoiceOutStatus { - u64_le played_sample_count; - u32_le wave_buffer_consumed; - INSERT_PADDING_WORDS(1); - }; - static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); - - /// This is used to trigger the audio event callback. - CoreTiming::EventType* audio_event; - Kernel::SharedPtr<Kernel::Event> system_event; - AudioRendererParameter worker_params; - std::vector<VoiceOutStatus> voice_status_list; + std::unique_ptr<AudioCore::AudioRenderer> renderer; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { @@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioRendererParameter>(); + auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioRendererParameter>(); + auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); buffer_sz += params.unknown_c * 1024; diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index b9b81db4f8..14907f8aea 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -4,6 +4,7 @@ #pragma once +#include "audio_core/audio_renderer.h" #include "core/hle/service/service.h" namespace Kernel { @@ -12,24 +13,6 @@ class HLERequestContext; namespace Service::Audio { -struct AudioRendererParameter { - u32_le sample_rate; - u32_le sample_count; - u32_le unknown_8; - u32_le unknown_c; - u32_le voice_count; - u32_le sink_count; - u32_le effect_count; - u32_le unknown_1c; - u8 unknown_20; - INSERT_PADDING_BYTES(3); - u32_le splitter_count; - u32_le unknown_2c; - INSERT_PADDING_WORDS(1); - u32_le revision; -}; -static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size"); - class AudRenU final : public ServiceFramework<AudRenU> { public: explicit AudRenU(); diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 844df382ca..371cd4997d 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> +#include <opus.h> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -9,19 +11,142 @@ namespace Service::Audio { +struct OpusDeleter { + void operator()(void* ptr) const { + operator delete(ptr); + } +}; + +class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { +public: + IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate, + u32 channel_count) + : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)), + sample_rate(sample_rate), channel_count(channel_count) { + static const FunctionInfo functions[] = { + {0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, + {1, nullptr, "SetContext"}, + {2, nullptr, "DecodeInterleavedForMultiStream"}, + {3, nullptr, "SetContextForMultiStream"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + }; + RegisterHandlers(functions); + } + +private: + void DecodeInterleaved(Kernel::HLERequestContext& ctx) { + u32 consumed = 0; + u32 sample_count = 0; + std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); + if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) { + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(ogniK): Use correct error code + rb.Push(ResultCode(-1)); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(consumed); + rb.Push<u32>(sample_count); + ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); + } + + bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, + std::vector<opus_int16>& output) { + size_t raw_output_sz = output.size() * sizeof(opus_int16); + if (sizeof(OpusHeader) > input.size()) + return false; + OpusHeader hdr{}; + std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); + if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { + return false; + } + auto frame = input.data() + sizeof(OpusHeader); + auto decoded_sample_count = opus_packet_get_nb_samples( + frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), + static_cast<opus_int32>(sample_rate)); + if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) + return false; + auto out_sample_count = + opus_decode(decoder.get(), frame, hdr.sz, output.data(), + (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); + if (out_sample_count < 0) + return false; + sample_count = out_sample_count; + consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); + return true; + } + + struct OpusHeader { + u32_be sz; // Needs to be BE for some odd reason + INSERT_PADDING_WORDS(1); + }; + static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size"); + + std::unique_ptr<OpusDecoder, OpusDeleter> decoder; + u32 sample_rate; + u32 channel_count; +}; + +static size_t WorkerBufferSize(u32 channel_count) { + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + return opus_decoder_get_size(static_cast<int>(channel_count)); +} + void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto sample_rate = rp.Pop<u32>(); + auto channel_count = rp.Pop<u32>(); + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); + LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0x4000); + rb.Push<u32>(worker_buffer_sz); +} + +void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto sample_rate = rp.Pop<u32>(); + auto channel_count = rp.Pop<u32>(); + auto buffer_sz = rp.Pop<u32>(); + LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, + channel_count, buffer_sz); + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + + size_t worker_sz = WorkerBufferSize(channel_count); + ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); + std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ + static_cast<OpusDecoder*>(operator new(worker_sz))}; + if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(ogniK): Use correct error code + rb.Push(ResultCode(-1)); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate, + channel_count); } HwOpus::HwOpus() : ServiceFramework("hwopus") { static const FunctionInfo functions[] = { - {0, nullptr, "Initialize"}, + {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"}, {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, - {2, nullptr, "InitializeMultiStream"}, - {3, nullptr, "GetWorkBufferSizeMultiStream"}, + {2, nullptr, "OpenOpusDecoderForMultiStream"}, + {3, nullptr, "GetWorkBufferSizeForMultiStream"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 090b8c8257..5258d59f33 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -14,6 +14,7 @@ public: ~HwOpus() = default; private: + void OpenOpusDecoder(Kernel::HLERequestContext& ctx); void GetWorkBufferSize(Kernel::HLERequestContext& ctx); }; diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp new file mode 100644 index 0000000000..1c1ecdb602 --- /dev/null +++ b/src/core/hle/service/bpc/bpc.cpp @@ -0,0 +1,57 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/bpc/bpc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BPC { + +class BPC final : public ServiceFramework<BPC> { +public: + explicit BPC() : ServiceFramework{"bpc"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ShutdownSystem"}, + {1, nullptr, "RebootSystem"}, + {2, nullptr, "GetWakeupReason"}, + {3, nullptr, "GetShutdownReason"}, + {4, nullptr, "GetAcOk"}, + {5, nullptr, "GetBoardPowerControlEvent"}, + {6, nullptr, "GetSleepButtonState"}, + {7, nullptr, "GetPowerEvent"}, + {8, nullptr, "Unknown1"}, + {9, nullptr, "Unknown2"}, + {10, nullptr, "Unknown3"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class BPC_R final : public ServiceFramework<BPC_R> { +public: + explicit BPC_R() : ServiceFramework{"bpc:r"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetExternalRtcValue"}, + {1, nullptr, "SetExternalRtcValue"}, + {2, nullptr, "ReadExternalRtcResetFlag"}, + {3, nullptr, "ClearExternalRtcResetFlag"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<BPC>()->InstallAsService(sm); + std::make_shared<BPC_R>()->InstallAsService(sm); +} + +} // namespace Service::BPC diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h new file mode 100644 index 0000000000..eaa37be8dc --- /dev/null +++ b/src/core/hle/service/bpc/bpc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::BPC { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::BPC diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp new file mode 100644 index 0000000000..d0a15cc4c4 --- /dev/null +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -0,0 +1,72 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/btdrv/btdrv.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BtDrv { + +class BtDrv final : public ServiceFramework<BtDrv> { +public: + explicit BtDrv() : ServiceFramework{"btdrv"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown"}, + {1, nullptr, "Init"}, + {2, nullptr, "Enable"}, + {3, nullptr, "Disable"}, + {4, nullptr, "CleanupAndShutdown"}, + {5, nullptr, "GetAdapterProperties"}, + {6, nullptr, "GetAdapterProperty"}, + {7, nullptr, "SetAdapterProperty"}, + {8, nullptr, "StartDiscovery"}, + {9, nullptr, "CancelDiscovery"}, + {10, nullptr, "CreateBond"}, + {11, nullptr, "RemoveBond"}, + {12, nullptr, "CancelBond"}, + {13, nullptr, "PinReply"}, + {14, nullptr, "SspReply"}, + {15, nullptr, "Unknown2"}, + {16, nullptr, "InitInterfaces"}, + {17, nullptr, "HidHostInterface_Connect"}, + {18, nullptr, "HidHostInterface_Disconnect"}, + {19, nullptr, "HidHostInterface_SendData"}, + {20, nullptr, "HidHostInterface_SendData2"}, + {21, nullptr, "HidHostInterface_SetReport"}, + {22, nullptr, "HidHostInterface_GetReport"}, + {23, nullptr, "HidHostInterface_WakeController"}, + {24, nullptr, "HidHostInterface_AddPairedDevice"}, + {25, nullptr, "HidHostInterface_GetPairedDevice"}, + {26, nullptr, "HidHostInterface_CleanupAndShutdown"}, + {27, nullptr, "Unknown3"}, + {28, nullptr, "ExtInterface_SetTSI"}, + {29, nullptr, "ExtInterface_SetBurstMode"}, + {30, nullptr, "ExtInterface_SetZeroRetran"}, + {31, nullptr, "ExtInterface_SetMcMode"}, + {32, nullptr, "ExtInterface_StartLlrMode"}, + {33, nullptr, "ExtInterface_ExitLlrMode"}, + {34, nullptr, "ExtInterface_SetRadio"}, + {35, nullptr, "ExtInterface_SetVisibility"}, + {36, nullptr, "Unknown4"}, + {37, nullptr, "Unknown5"}, + {38, nullptr, "HidHostInterface_GetLatestPlr"}, + {39, nullptr, "ExtInterface_GetPendingConnections"}, + {40, nullptr, "HidHostInterface_GetChannelMap"}, + {41, nullptr, "SetIsBluetoothBoostEnabled"}, + {42, nullptr, "GetIsBluetoothBoostEnabled"}, + {43, nullptr, "SetIsBluetoothAfhEnabled"}, + {44, nullptr, "GetIsBluetoothAfhEnabled"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<BtDrv>()->InstallAsService(sm); +} + +} // namespace Service::BtDrv diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h new file mode 100644 index 0000000000..164e56f436 --- /dev/null +++ b/src/core/hle/service/btdrv/btdrv.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::BtDrv { + +/// Registers all BtDrv services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::BtDrv diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp new file mode 100644 index 0000000000..b949bfabd0 --- /dev/null +++ b/src/core/hle/service/btm/btm.cpp @@ -0,0 +1,121 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/btm/btm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BTM { + +class BTM final : public ServiceFramework<BTM> { +public: + explicit BTM() : ServiceFramework{"btm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "RegisterSystemEventForConnectedDeviceConditionImpl"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfoImpl"}, + {9, nullptr, "Unknown8"}, + {10, nullptr, "Unknown9"}, + {11, nullptr, "Unknown10"}, + {12, nullptr, "Unknown11"}, + {13, nullptr, "Unknown12"}, + {14, nullptr, "EnableRadioImpl"}, + {15, nullptr, "DisableRadioImpl"}, + {16, nullptr, "Unknown13"}, + {17, nullptr, "Unknown14"}, + {18, nullptr, "Unknown15"}, + {19, nullptr, "Unknown16"}, + {20, nullptr, "Unknown17"}, + {21, nullptr, "Unknown18"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class BTM_DBG final : public ServiceFramework<BTM_DBG> { +public: + explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterSystemEventForDiscoveryImpl"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> { +public: + explicit IBtmSystemCore() : ServiceFramework{"IBtmSystemCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "StartGamepadPairingImpl"}, + {1, nullptr, "CancelGamepadPairingImpl"}, + {2, nullptr, "ClearGamepadPairingDatabaseImpl"}, + {3, nullptr, "GetPairedGamepadCountImpl"}, + {4, nullptr, "EnableRadioImpl"}, + {5, nullptr, "DisableRadioImpl"}, + {6, nullptr, "GetRadioOnOffImpl"}, + {7, nullptr, "AcquireRadioEventImpl"}, + {8, nullptr, "AcquireGamepadPairingEventImpl"}, + {9, nullptr, "IsGamepadPairingStartedImpl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class BTM_SYS final : public ServiceFramework<BTM_SYS> { +public: + explicit BTM_SYS() : ServiceFramework{"btm:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &BTM_SYS::GetCoreImpl, "GetCoreImpl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetCoreImpl(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IBtmSystemCore>(); + + LOG_DEBUG(Service_BTM, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<BTM>()->InstallAsService(sm); + std::make_shared<BTM_DBG>()->InstallAsService(sm); + std::make_shared<BTM_SYS>()->InstallAsService(sm); +} + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h new file mode 100644 index 0000000000..e6425a7e3d --- /dev/null +++ b/src/core/hle/service/btm/btm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::BTM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::BTM diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp new file mode 100644 index 0000000000..ae7b0720bd --- /dev/null +++ b/src/core/hle/service/caps/caps.cpp @@ -0,0 +1,152 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/caps/caps.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Capture { + +class CAPS_A final : public ServiceFramework<CAPS_A> { +public: + explicit CAPS_A() : ServiceFramework{"caps:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "Unknown7"}, + {7, nullptr, "Unknown8"}, + {8, nullptr, "Unknown9"}, + {9, nullptr, "Unknown10"}, + {10, nullptr, "Unknown11"}, + {11, nullptr, "Unknown12"}, + {12, nullptr, "Unknown13"}, + {13, nullptr, "Unknown14"}, + {14, nullptr, "Unknown15"}, + {301, nullptr, "Unknown16"}, + {401, nullptr, "Unknown17"}, + {501, nullptr, "Unknown18"}, + {1001, nullptr, "Unknown19"}, + {1002, nullptr, "Unknown20"}, + {8001, nullptr, "Unknown21"}, + {8002, nullptr, "Unknown22"}, + {8011, nullptr, "Unknown23"}, + {8012, nullptr, "Unknown24"}, + {8021, nullptr, "Unknown25"}, + {10011, nullptr, "Unknown26"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_C final : public ServiceFramework<CAPS_C> { +public: + explicit CAPS_C() : ServiceFramework{"caps:c"} { + // clang-format off + static const FunctionInfo functions[] = { + {2001, nullptr, "Unknown1"}, + {2002, nullptr, "Unknown2"}, + {2011, nullptr, "Unknown3"}, + {2012, nullptr, "Unknown4"}, + {2013, nullptr, "Unknown5"}, + {2014, nullptr, "Unknown6"}, + {2101, nullptr, "Unknown7"}, + {2102, nullptr, "Unknown8"}, + {2201, nullptr, "Unknown9"}, + {2301, nullptr, "Unknown10"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_SC final : public ServiceFramework<CAPS_SC> { +public: + explicit CAPS_SC() : ServiceFramework{"caps:sc"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {1001, nullptr, "Unknown3"}, + {1002, nullptr, "Unknown4"}, + {1003, nullptr, "Unknown5"}, + {1011, nullptr, "Unknown6"}, + {1012, nullptr, "Unknown7"}, + {1201, nullptr, "Unknown8"}, + {1202, nullptr, "Unknown9"}, + {1203, nullptr, "Unknown10"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_SS final : public ServiceFramework<CAPS_SS> { +public: + explicit CAPS_SS() : ServiceFramework{"caps:ss"} { + // clang-format off + static const FunctionInfo functions[] = { + {201, nullptr, "Unknown1"}, + {202, nullptr, "Unknown2"}, + {203, nullptr, "Unknown3"}, + {204, nullptr, "Unknown4"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_SU final : public ServiceFramework<CAPS_SU> { +public: + explicit CAPS_SU() : ServiceFramework{"caps:su"} { + // clang-format off + static const FunctionInfo functions[] = { + {201, nullptr, "SaveScreenShot"}, + {203, nullptr, "SaveScreenShotEx0"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_U final : public ServiceFramework<CAPS_U> { +public: + explicit CAPS_U() : ServiceFramework{"caps:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {102, nullptr, "GetAlbumFileListByAruid"}, + {103, nullptr, "DeleteAlbumFileByAruid"}, + {104, nullptr, "GetAlbumFileSizeByAruid"}, + {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, + {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, + {60002, nullptr, "OpenAccessorSessionForApplication"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<CAPS_A>()->InstallAsService(sm); + std::make_shared<CAPS_C>()->InstallAsService(sm); + std::make_shared<CAPS_SC>()->InstallAsService(sm); + std::make_shared<CAPS_SS>()->InstallAsService(sm); + std::make_shared<CAPS_SU>()->InstallAsService(sm); + std::make_shared<CAPS_U>()->InstallAsService(sm); +} + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h new file mode 100644 index 0000000000..471185dfa0 --- /dev/null +++ b/src/core/hle/service/caps/caps.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::Capture { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::Capture diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp new file mode 100644 index 0000000000..566fbf924a --- /dev/null +++ b/src/core/hle/service/fgm/fgm.cpp @@ -0,0 +1,75 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/fgm/fgm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::FGM { + +class IRequest final : public ServiceFramework<IRequest> { +public: + explicit IRequest() : ServiceFramework{"IRequest"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Set"}, + {2, nullptr, "Get"}, + {3, nullptr, "Cancel"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class FGM final : public ServiceFramework<FGM> { +public: + explicit FGM(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &FGM::Initialize, "Initialize"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Initialize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRequest>(); + + LOG_DEBUG(Service_FGM, "called"); + } +}; + +class FGM_DBG final : public ServiceFramework<FGM_DBG> { +public: + explicit FGM_DBG() : ServiceFramework{"fgm:dbg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Read"}, + {2, nullptr, "Cancel"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<FGM>("fgm")->InstallAsService(sm); + std::make_shared<FGM>("fgm:0")->InstallAsService(sm); + std::make_shared<FGM>("fgm:9")->InstallAsService(sm); + std::make_shared<FGM_DBG>()->InstallAsService(sm); +} + +} // namespace Service::FGM diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h new file mode 100644 index 0000000000..e596912643 --- /dev/null +++ b/src/core/hle/service/fgm/fgm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::FGM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::FGM diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index fdd2fda182..e17d637e43 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -14,6 +14,8 @@ #include "core/file_sys/vfs_offset.h" #include "core/file_sys/vfs_real.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_ldr.h" +#include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" namespace Service::FileSystem { @@ -298,6 +300,8 @@ void RegisterFileSystems() { void InstallInterfaces(SM::ServiceManager& service_manager) { RegisterFileSystems(); + std::make_shared<FSP_LDR>()->InstallAsService(service_manager); + std::make_shared<FSP_PR>()->InstallAsService(service_manager); std::make_shared<FSP_SRV>()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp new file mode 100644 index 0000000000..0ab9c26060 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_ldr.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/filesystem/fsp_ldr.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +FSP_LDR::FSP_LDR() : ServiceFramework{"fsp:ldr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "OpenCodeFileSystem"}, + {1, nullptr, "IsArchivedProgram"}, + {2, nullptr, "SetCurrentProcess"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h new file mode 100644 index 0000000000..fa8e11b4cf --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_ldr.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class FSP_LDR final : public ServiceFramework<FSP_LDR> { +public: + explicit FSP_LDR(); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp new file mode 100644 index 0000000000..32b0ae454b --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_pr.cpp @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/filesystem/fsp_pr.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +FSP_PR::FSP_PR() : ServiceFramework{"fsp:pr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterProgram"}, + {1, nullptr, "UnregisterProgram"}, + {2, nullptr, "SetCurrentProcess"}, + {256, nullptr, "SetEnabledProgramVerification"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h new file mode 100644 index 0000000000..62edcd08a6 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_pr.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class FSP_PR final : public ServiceFramework<FSP_PR> { +public: + explicit FSP_PR(); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e4619a547b..8f0262e343 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -326,7 +326,7 @@ public: {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, {80, nullptr, "GetGyroscopeZeroDriftMode"}, {81, nullptr, "ResetGyroscopeZeroDriftMode"}, - {82, nullptr, "IsSixAxisSensorAtRest"}, + {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, {91, nullptr, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, @@ -337,13 +337,14 @@ public: "AcquireNpadStyleSetUpdateEventHandle"}, {107, nullptr, "DisconnectNpad"}, {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, + {109, nullptr, "ActivateNpadWithRevision"}, {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, - {125, nullptr, "MergeSingleJoyAsDualJoy"}, + {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, {126, nullptr, "StartLrAssignmentMode"}, {127, nullptr, "StopLrAssignmentMode"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, @@ -455,6 +456,14 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. + rb.Push(true); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -530,6 +539,12 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp new file mode 100644 index 0000000000..8fc8b10579 --- /dev/null +++ b/src/core/hle/service/lbl/lbl.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/lbl/lbl.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::LBL { + +class LBL final : public ServiceFramework<LBL> { +public: + explicit LBL() : ServiceFramework{"lbl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "TurnOffBacklight"}, + {7, nullptr, "TurnOnBacklight"}, + {8, nullptr, "GetBacklightStatus"}, + {9, nullptr, "Unknown7"}, + {10, nullptr, "Unknown8"}, + {11, nullptr, "Unknown9"}, + {12, nullptr, "Unknown10"}, + {13, nullptr, "Unknown11"}, + {14, nullptr, "Unknown12"}, + {15, nullptr, "Unknown13"}, + {16, nullptr, "ReadRawLightSensor"}, + {17, nullptr, "Unknown14"}, + {18, nullptr, "Unknown15"}, + {19, nullptr, "Unknown16"}, + {20, nullptr, "Unknown17"}, + {21, nullptr, "Unknown18"}, + {22, nullptr, "Unknown19"}, + {23, nullptr, "Unknown20"}, + {24, nullptr, "Unknown21"}, + {25, nullptr, "Unknown22"}, + {26, &LBL::EnableVrMode, "EnableVrMode"}, + {27, &LBL::DisableVrMode, "DisableVrMode"}, + {28, &LBL::GetVrMode, "GetVrMode"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void EnableVrMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + vr_mode_enabled = true; + + LOG_DEBUG(Service_LBL, "called"); + } + + void DisableVrMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + vr_mode_enabled = false; + + LOG_DEBUG(Service_LBL, "called"); + } + + void GetVrMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(vr_mode_enabled); + + LOG_DEBUG(Service_LBL, "called"); + } + + bool vr_mode_enabled = false; +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<LBL>()->InstallAsService(sm); +} + +} // namespace Service::LBL diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h new file mode 100644 index 0000000000..bf6f400f8d --- /dev/null +++ b/src/core/hle/service/lbl/lbl.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::LBL { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::LBL diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index b497376d7b..2e99ddf51b 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -13,11 +13,11 @@ namespace Service::LM { -class Logger final : public ServiceFramework<Logger> { +class ILogger final : public ServiceFramework<ILogger> { public: - Logger() : ServiceFramework("Logger") { + ILogger() : ServiceFramework("ILogger") { static const FunctionInfo functions[] = { - {0x00000000, &Logger::Initialize, "Initialize"}, + {0x00000000, &ILogger::Initialize, "Initialize"}, {0x00000001, nullptr, "SetDestination"}, }; RegisterHandlers(functions); @@ -182,7 +182,7 @@ public: void OpenLogger(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Logger>(); + rb.PushIpcInterface<ILogger>(); LOG_DEBUG(Service_LM, "called"); } diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp new file mode 100644 index 0000000000..d16367f2cf --- /dev/null +++ b/src/core/hle/service/mig/mig.cpp @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/mig/mig.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Migration { + +class MIG_USR final : public ServiceFramework<MIG_USR> { +public: + explicit MIG_USR() : ServiceFramework{"mig:usr"} { + // clang-format off + static const FunctionInfo functions[] = { + {10, nullptr, "TryGetLastMigrationInfo"}, + {100, nullptr, "CreateServer"}, + {101, nullptr, "ResumeServer"}, + {200, nullptr, "CreateClient"}, + {201, nullptr, "ResumeClient"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<MIG_USR>()->InstallAsService(sm); +} + +} // namespace Service::Migration diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h new file mode 100644 index 0000000000..288c1c1b34 --- /dev/null +++ b/src/core/hle/service/mig/mig.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::Migration { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::Migration diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp new file mode 100644 index 0000000000..a6197124a4 --- /dev/null +++ b/src/core/hle/service/mii/mii.cpp @@ -0,0 +1,107 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/mii/mii.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Mii { + +class IDatabaseService final : public ServiceFramework<IDatabaseService> { +public: + explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "IsUpdated"}, + {1, nullptr, "IsFullDatabase"}, + {2, nullptr, "GetCount"}, + {3, nullptr, "Get"}, + {4, nullptr, "Get1"}, + {5, nullptr, "UpdateLatest"}, + {6, nullptr, "BuildRandom"}, + {7, nullptr, "BuildDefault"}, + {8, nullptr, "Get2"}, + {9, nullptr, "Get3"}, + {10, nullptr, "UpdateLatest1"}, + {11, nullptr, "FindIndex"}, + {12, nullptr, "Move"}, + {13, nullptr, "AddOrReplace"}, + {14, nullptr, "Delete"}, + {15, nullptr, "DestroyFile"}, + {16, nullptr, "DeleteFile"}, + {17, nullptr, "Format"}, + {18, nullptr, "Import"}, + {19, nullptr, "Export"}, + {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, + {21, nullptr, "GetIndex"}, + {22, nullptr, "SetInterfaceVersion"}, + {23, nullptr, "Convert"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class MiiDBModule final : public ServiceFramework<MiiDBModule> { +public: + explicit MiiDBModule(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetDatabaseService(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDatabaseService>(); + + LOG_DEBUG(Service_Mii, "called"); + } +}; + +class MiiImg final : public ServiceFramework<MiiImg> { +public: + explicit MiiImg() : ServiceFramework{"miiimg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {10, nullptr, "Reload"}, + {11, nullptr, "GetCount"}, + {12, nullptr, "IsEmpty"}, + {13, nullptr, "IsFull"}, + {14, nullptr, "GetAttribute"}, + {15, nullptr, "LoadImage"}, + {16, nullptr, "AddOrUpdateImage"}, + {17, nullptr, "DeleteImages"}, + {100, nullptr, "DeleteFile"}, + {101, nullptr, "DestroyFile"}, + {102, nullptr, "ImportFile"}, + {103, nullptr, "ExportFile"}, + {104, nullptr, "ForceInitialize"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<MiiDBModule>("mii:e")->InstallAsService(sm); + std::make_shared<MiiDBModule>("mii:u")->InstallAsService(sm); + + std::make_shared<MiiImg>()->InstallAsService(sm); +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h new file mode 100644 index 0000000000..7ce9be50eb --- /dev/null +++ b/src/core/hle/service/mii/mii.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::Mii { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::Mii diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp new file mode 100644 index 0000000000..0297edca01 --- /dev/null +++ b/src/core/hle/service/ncm/ncm.cpp @@ -0,0 +1,59 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/ncm/ncm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::NCM { + +class LocationResolver final : public ServiceFramework<LocationResolver> { +public: + explicit LocationResolver() : ServiceFramework{"lr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "OpenLocationResolver"}, + {1, nullptr, "OpenRegisteredLocationResolver"}, + {2, nullptr, "RefreshLocationResolver"}, + {3, nullptr, "OpenAddOnContentLocationResolver"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NCM final : public ServiceFramework<NCM> { +public: + explicit NCM() : ServiceFramework{"ncm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateContentStorage"}, + {1, nullptr, "CreateContentMetaDatabase"}, + {2, nullptr, "VerifyContentStorage"}, + {3, nullptr, "VerifyContentMetaDatabase"}, + {4, nullptr, "OpenContentStorage"}, + {5, nullptr, "OpenContentMetaDatabase"}, + {6, nullptr, "CloseContentStorageForcibly"}, + {7, nullptr, "CloseContentMetaDatabaseForcibly"}, + {8, nullptr, "CleanupContentMetaDatabase"}, + {9, nullptr, "OpenContentStorage2"}, + {10, nullptr, "CloseContentStorage"}, + {11, nullptr, "OpenContentMetaDatabase2"}, + {12, nullptr, "CloseContentMetaDatabase"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<LocationResolver>()->InstallAsService(sm); + std::make_shared<NCM>()->InstallAsService(sm); +} + +} // namespace Service::NCM diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h new file mode 100644 index 0000000000..7bc8518a6a --- /dev/null +++ b/src/core/hle/service/ncm/ncm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NCM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::NCM diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp new file mode 100644 index 0000000000..8fec97db83 --- /dev/null +++ b/src/core/hle/service/nfc/nfc.cpp @@ -0,0 +1,222 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/nfc/nfc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::NFC { + +class IAm final : public ServiceFramework<IAm> { +public: + explicit IAm() : ServiceFramework{"IAm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "NotifyForegroundApplet"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_AM final : public ServiceFramework<NFC_AM> { +public: + explicit NFC_AM() : ServiceFramework{"nfc:am"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_AM::CreateAmInterface, "CreateAmInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateAmInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAm>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +class MFIUser final : public ServiceFramework<MFIUser> { +public: + explicit MFIUser() : ServiceFramework{"IUser"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "ListDevices"}, + {3, nullptr, "StartDetection"}, + {4, nullptr, "StopDetection"}, + {5, nullptr, "Read"}, + {6, nullptr, "Write"}, + {7, nullptr, "GetTagInfo"}, + {8, nullptr, "GetActivateEventHandle"}, + {9, nullptr, "GetDeactivateEventHandle"}, + {10, nullptr, "GetState"}, + {11, nullptr, "GetDeviceState"}, + {12, nullptr, "GetNpadId"}, + {13, nullptr, "GetAvailabilityChangeEventHandle"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_MF_U final : public ServiceFramework<NFC_MF_U> { +public: + explicit NFC_MF_U() : ServiceFramework{"nfc:mf:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateUserInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<MFIUser>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +class IUser final : public ServiceFramework<IUser> { +public: + explicit IUser() : ServiceFramework{"IUser"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "GetState"}, + {3, nullptr, "IsNfcEnabled"}, + {400, nullptr, "Initialize"}, + {401, nullptr, "Finalize"}, + {402, nullptr, "GetState"}, + {403, nullptr, "IsNfcEnabled"}, + {404, nullptr, "ListDevices"}, + {405, nullptr, "GetDeviceState"}, + {406, nullptr, "GetNpadId"}, + {407, nullptr, "AttachAvailabilityChangeEvent"}, + {408, nullptr, "StartDetection"}, + {409, nullptr, "StopDetection"}, + {410, nullptr, "GetTagInfo"}, + {411, nullptr, "AttachActivateEvent"}, + {412, nullptr, "AttachDeactivateEvent"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, nullptr, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_U final : public ServiceFramework<NFC_U> { +public: + explicit NFC_U() : ServiceFramework{"nfc:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateUserInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IUser>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +class ISystem final : public ServiceFramework<ISystem> { +public: + explicit ISystem() : ServiceFramework{"ISystem"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "GetState"}, + {3, nullptr, "IsNfcEnabled"}, + {100, nullptr, "SetNfcEnabled"}, + {400, nullptr, "InitializeSystem"}, + {401, nullptr, "FinalizeSystem"}, + {402, nullptr, "GetState"}, + {403, nullptr, "IsNfcEnabled"}, + {404, nullptr, "ListDevices"}, + {405, nullptr, "GetDeviceState"}, + {406, nullptr, "GetNpadId"}, + {407, nullptr, "AttachAvailabilityChangeEvent"}, + {408, nullptr, "StartDetection"}, + {409, nullptr, "StopDetection"}, + {410, nullptr, "GetTagInfo"}, + {411, nullptr, "AttachActivateEvent"}, + {412, nullptr, "AttachDeactivateEvent"}, + {500, nullptr, "SetNfcEnabled"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, nullptr, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_SYS final : public ServiceFramework<NFC_SYS> { +public: + explicit NFC_SYS() : ServiceFramework{"nfc:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateSystemInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystem>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<NFC_AM>()->InstallAsService(sm); + std::make_shared<NFC_MF_U>()->InstallAsService(sm); + std::make_shared<NFC_U>()->InstallAsService(sm); + std::make_shared<NFC_SYS>()->InstallAsService(sm); +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h new file mode 100644 index 0000000000..4d2d815f90 --- /dev/null +++ b/src/core/hle/service/nfc/nfc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NFC { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::NFC diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 89c703310d..98017267ca 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -2,12 +2,459 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pl_u.h" namespace Service::NS { +class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { +public: + explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateUserAccount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { +public: + explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ListApplicationRecord"}, + {1, nullptr, "GenerateApplicationRecordCount"}, + {2, nullptr, "GetApplicationRecordUpdateSystemEvent"}, + {3, nullptr, "GetApplicationViewDeprecated"}, + {4, nullptr, "DeleteApplicationEntity"}, + {5, nullptr, "DeleteApplicationCompletely"}, + {6, nullptr, "IsAnyApplicationEntityRedundant"}, + {7, nullptr, "DeleteRedundantApplicationEntity"}, + {8, nullptr, "IsApplicationEntityMovable"}, + {9, nullptr, "MoveApplicationEntity"}, + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {16, nullptr, "PushApplicationRecord"}, + {17, nullptr, "ListApplicationRecordContentMeta"}, + {19, nullptr, "LaunchApplication"}, + {21, nullptr, "GetApplicationContentPath"}, + {22, nullptr, "TerminateApplication"}, + {23, nullptr, "ResolveApplicationContentPath"}, + {26, nullptr, "BeginInstallApplication"}, + {27, nullptr, "DeleteApplicationRecord"}, + {30, nullptr, "RequestApplicationUpdateInfo"}, + {32, nullptr, "CancelApplicationDownload"}, + {33, nullptr, "ResumeApplicationDownload"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {38, nullptr, "CheckApplicationLaunchVersion"}, + {39, nullptr, "CheckApplicationLaunchRights"}, + {40, nullptr, "GetApplicationLogoData"}, + {41, nullptr, "CalculateApplicationDownloadRequiredSize"}, + {42, nullptr, "CleanupSdCard"}, + {43, nullptr, "CheckSdCardMountStatus"}, + {44, nullptr, "GetSdCardMountStatusChangedEvent"}, + {45, nullptr, "GetGameCardAttachmentEvent"}, + {46, nullptr, "GetGameCardAttachmentInfo"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, nullptr, "GetFreeSpaceSize"}, + {49, nullptr, "GetSdCardRemovedEvent"}, + {52, nullptr, "GetGameCardUpdateDetectionEvent"}, + {53, nullptr, "DisableApplicationAutoDelete"}, + {54, nullptr, "EnableApplicationAutoDelete"}, + {55, nullptr, "GetApplicationDesiredLanguage"}, + {56, nullptr, "SetApplicationTerminateResult"}, + {57, nullptr, "ClearApplicationTerminateResult"}, + {58, nullptr, "GetLastSdCardMountUnexpectedResult"}, + {59, nullptr, "ConvertApplicationLanguageToLanguageCode"}, + {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, + {61, nullptr, "GetBackgroundDownloadStressTaskInfo"}, + {62, nullptr, "GetGameCardStopper"}, + {63, nullptr, "IsSystemProgramInstalled"}, + {64, nullptr, "StartApplyDeltaTask"}, + {65, nullptr, "GetRequestServerStopper"}, + {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, + {67, nullptr, "CancelApplicationApplyDelta"}, + {68, nullptr, "ResumeApplicationApplyDelta"}, + {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"}, + {70, nullptr, "ResumeAll"}, + {71, nullptr, "GetStorageSize"}, + {80, nullptr, "RequestDownloadApplication"}, + {81, nullptr, "RequestDownloadAddOnContent"}, + {82, nullptr, "DownloadApplication"}, + {83, nullptr, "CheckApplicationResumeRights"}, + {84, nullptr, "GetDynamicCommitEvent"}, + {85, nullptr, "RequestUpdateApplication2"}, + {86, nullptr, "EnableApplicationCrashReport"}, + {87, nullptr, "IsApplicationCrashReportEnabled"}, + {90, nullptr, "BoostSystemMemoryResourceLimit"}, + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, + {200, nullptr, "CalculateUserSaveDataStatistics"}, + {201, nullptr, "DeleteUserSaveDataAll"}, + {210, nullptr, "DeleteUserSystemSaveData"}, + {220, nullptr, "UnregisterNetworkServiceAccount"}, + {300, nullptr, "GetApplicationShellEvent"}, + {301, nullptr, "PopApplicationShellEventInfo"}, + {302, nullptr, "LaunchLibraryApplet"}, + {303, nullptr, "TerminateLibraryApplet"}, + {304, nullptr, "LaunchSystemApplet"}, + {305, nullptr, "TerminateSystemApplet"}, + {306, nullptr, "LaunchOverlayApplet"}, + {307, nullptr, "TerminateOverlayApplet"}, + {400, nullptr, "GetApplicationControlData"}, + {401, nullptr, "InvalidateAllApplicationControlCache"}, + {402, nullptr, "RequestDownloadApplicationControlData"}, + {403, nullptr, "GetMaxApplicationControlCacheCount"}, + {404, nullptr, "InvalidateApplicationControlCache"}, + {405, nullptr, "ListApplicationControlCacheEntryInfo"}, + {502, nullptr, "RequestCheckGameCardRegistration"}, + {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, + {504, nullptr, "RequestRegisterGameCard"}, + {505, nullptr, "GetGameCardMountFailureEvent"}, + {506, nullptr, "IsGameCardInserted"}, + {507, nullptr, "EnsureGameCardAccess"}, + {508, nullptr, "GetLastGameCardMountFailureResult"}, + {509, nullptr, "ListApplicationIdOnGameCard"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {602, nullptr, "ListAvailableAddOnContent"}, + {603, nullptr, "GetOwnedApplicationContentMetaStatus"}, + {604, nullptr, "RegisterContentsExternalKey"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {606, nullptr, "GetContentMetaStorage"}, + {700, nullptr, "PushDownloadTaskList"}, + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {900, nullptr, "GetApplicationRecord"}, + {901, nullptr, "GetApplicationRecordProperty"}, + {902, nullptr, "EnableApplicationAutoUpdate"}, + {903, nullptr, "DisableApplicationAutoUpdate"}, + {904, nullptr, "TouchApplication"}, + {905, nullptr, "RequestApplicationUpdate"}, + {906, nullptr, "IsApplicationUpdateRequested"}, + {907, nullptr, "WithdrawApplicationUpdateRequest"}, + {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, + {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, + {1000, nullptr, "RequestVerifyApplicationDeprecated"}, + {1001, nullptr, "CorruptApplicationForDebug"}, + {1002, nullptr, "RequestVerifyAddOnContentsRights"}, + {1003, nullptr, "RequestVerifyApplication"}, + {1004, nullptr, "CorruptContentForDebug"}, + {1200, nullptr, "NeedsUpdateVulnerability"}, + {1300, nullptr, "IsAnyApplicationEntityInstalled"}, + {1301, nullptr, "DeleteApplicationContentEntities"}, + {1302, nullptr, "CleanupUnrecordedApplicationEntity"}, + {1303, nullptr, "CleanupAddOnContentsWithNoRights"}, + {1304, nullptr, "DeleteApplicationContentEntity"}, + {1305, nullptr, "TryDeleteRunningApplicationEntity"}, + {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, + {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, + {1400, nullptr, "PrepareShutdown"}, + {1500, nullptr, "FormatSdCard"}, + {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, + {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"}, + {1504, nullptr, "InsertSdCard"}, + {1505, nullptr, "RemoveSdCard"}, + {1600, nullptr, "GetSystemSeedForPseudoDeviceId"}, + {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"}, + {1700, nullptr, "ListApplicationDownloadingContentMeta"}, + {1701, nullptr, "GetApplicationView"}, + {1702, nullptr, "GetApplicationDownloadTaskStatus"}, + {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, + {1800, nullptr, "IsNotificationSetupCompleted"}, + {1801, nullptr, "GetLastNotificationInfoCount"}, + {1802, nullptr, "ListLastNotificationInfo"}, + {1803, nullptr, "ListNotificationTask"}, + {1900, nullptr, "IsActiveAccount"}, + {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"}, + {1902, nullptr, "GetApplicationTicketInfo"}, + {2000, nullptr, "GetSystemDeliveryInfo"}, + {2001, nullptr, "SelectLatestSystemDeliveryInfo"}, + {2002, nullptr, "VerifyDeliveryProtocolVersion"}, + {2003, nullptr, "GetApplicationDeliveryInfo"}, + {2004, nullptr, "HasAllContentsToDeliver"}, + {2005, nullptr, "CompareApplicationDeliveryInfo"}, + {2006, nullptr, "CanDeliverApplication"}, + {2007, nullptr, "ListContentMetaKeyToDeliverApplication"}, + {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"}, + {2009, nullptr, "EstimateRequiredSize"}, + {2010, nullptr, "RequestReceiveApplication"}, + {2011, nullptr, "CommitReceiveApplication"}, + {2012, nullptr, "GetReceiveApplicationProgress"}, + {2013, nullptr, "RequestSendApplication"}, + {2014, nullptr, "GetSendApplicationProgress"}, + {2015, nullptr, "CompareSystemDeliveryInfo"}, + {2016, nullptr, "ListNotCommittedContentMeta"}, + {2017, nullptr, "CreateDownloadTask"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { +public: + explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetLaunchRequiredVersion"}, + {1, nullptr, "UpgradeLaunchRequiredVersion"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {1000, nullptr, "PerformAutoUpdate"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> { +public: + explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {43, nullptr, "CheckSdCardMountStatus"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, nullptr, "GetFreeSpaceSize"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {607, nullptr, "IsAnyApplicationRunning"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { +public: + explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {21, nullptr, "GetApplicationContentPath"}, + {23, nullptr, "ResolveApplicationContentPath"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { +public: + explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {706, nullptr, "TryCommitCurrentApplicationDownloadTask"}, + {707, nullptr, "EnableAutoCommit"}, + {708, nullptr, "DisableAutoCommit"}, + {709, nullptr, "TriggerDynamicCommitEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IECommerceInterface final : public ServiceFramework<IECommerceInterface> { +public: + explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestLinkDevice"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> { +public: + explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment "}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NS final : public ServiceFramework<NS> { +public: + explicit NS(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, + {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, + {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, + {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"}, + {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"}, + {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, + {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"}, + {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + template <typename T> + void PushInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<T>(); + + LOG_DEBUG(Service_NS, "called"); + } +}; + +class NS_DEV final : public ServiceFramework<NS_DEV> { +public: + explicit NS_DEV() : ServiceFramework{"ns:dev"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "LaunchProgram"}, + {1, nullptr, "TerminateProcess"}, + {2, nullptr, "TerminateProgram"}, + {3, nullptr, "GetShellEventHandle"}, + {4, nullptr, "GetShellEventInfo"}, + {5, nullptr, "TerminateApplication"}, + {6, nullptr, "PrepareLaunchProgramFromHost"}, + {7, nullptr, "LaunchApplication"}, + {8, nullptr, "LaunchApplicationWithStorageId"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> { +public: + explicit ISystemUpdateControl() : ServiceFramework{"ISystemUpdateControl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "HasDownloaded"}, + {1, nullptr, "RequestCheckLatestUpdate"}, + {2, nullptr, "RequestDownloadLatestUpdate"}, + {3, nullptr, "GetDownloadProgress"}, + {4, nullptr, "ApplyDownloadedUpdate"}, + {5, nullptr, "RequestPrepareCardUpdate"}, + {6, nullptr, "GetPrepareCardUpdateProgress"}, + {7, nullptr, "HasPreparedCardUpdate"}, + {8, nullptr, "ApplyCardUpdate"}, + {9, nullptr, "GetDownloadedEulaDataSize"}, + {10, nullptr, "GetDownloadedEulaData"}, + {11, nullptr, "SetupCardUpdate"}, + {12, nullptr, "GetPreparedCardUpdateEulaDataSize"}, + {13, nullptr, "GetPreparedCardUpdateEulaData"}, + {14, nullptr, "SetupCardUpdateViaSystemUpdater"}, + {15, nullptr, "HasReceived"}, + {16, nullptr, "RequestReceiveSystemUpdate"}, + {17, nullptr, "GetReceiveProgress"}, + {18, nullptr, "ApplyReceivedUpdate"}, + {19, nullptr, "GetReceivedEulaDataSize"}, + {20, nullptr, "GetReceivedEulaData"}, + {21, nullptr, "SetupToReceiveSystemUpdate"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NS_SU final : public ServiceFramework<NS_SU> { +public: + explicit NS_SU() : ServiceFramework{"ns:su"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetBackgroundNetworkUpdateState"}, + {1, &NS_SU::OpenSystemUpdateControl, "OpenSystemUpdateControl"}, + {2, nullptr, "NotifyExFatDriverRequired"}, + {3, nullptr, "ClearExFatDriverStatusForDebug"}, + {4, nullptr, "RequestBackgroundNetworkUpdate"}, + {5, nullptr, "NotifyBackgroundNetworkUpdate"}, + {6, nullptr, "NotifyExFatDriverDownloadedForDebug"}, + {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"}, + {10, nullptr, "NotifySystemUpdateForContentDelivery"}, + {11, nullptr, "PrepareShutdown"}, + {16, nullptr, "DestroySystemUpdateTask"}, + {17, nullptr, "RequestSendSystemUpdate"}, + {18, nullptr, "GetSendSystemUpdateProgress"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void OpenSystemUpdateControl(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemUpdateControl>(); + + LOG_DEBUG(Service_NS, "called"); + } +}; + +class NS_VM final : public ServiceFramework<NS_VM> { +public: + explicit NS_VM() : ServiceFramework{"ns:vm"} { + // clang-format off + static const FunctionInfo functions[] = { + {1200, nullptr, "NeedsUpdateVulnerability"}, + {1201, nullptr, "UpdateSafeSystemVersionForDebug"}, + {1202, nullptr, "GetSafeSystemVersion"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); + std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); + std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); + std::make_shared<NS>("ns:rt")->InstallAsService(service_manager); + std::make_shared<NS>("ns:web")->InstallAsService(service_manager); + + std::make_shared<NS_DEV>()->InstallAsService(service_manager); + std::make_shared<NS_SU>()->InstallAsService(service_manager); + std::make_shared<NS_VM>()->InstallAsService(service_manager); + std::make_shared<PL_U>()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index ed69a4325d..8bc49935a8 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -7,8 +7,8 @@ #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "video_core/gpu.h" #include "video_core/renderer_base.h" -#include "video_core/video_core.h" namespace Service::Nvidia::Devices { @@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform, crop_rect}; - Core::System::GetInstance().perf_stats.EndGameFrame(); - - VideoCore::g_renderer->SwapBuffers(framebuffer); + auto& instance = Core::System::GetInstance(); + instance.perf_stats.EndGameFrame(); + instance.Renderer().SwapBuffers(framebuffer); } } // namespace Service::Nvidia::Devices 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 57b128b40a..be2b792566 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -2,14 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "video_core/memory_manager.h" +#include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" -#include "video_core/video_core.h" namespace Service::Nvidia::Devices { @@ -150,15 +151,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); - auto& gpu = Core::System::GetInstance().GPU(); - - auto itr = buffer_mappings.find(params.offset); - + const auto itr = buffer_mappings.find(params.offset); ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); + auto& system_instance = Core::System::GetInstance(); + // Remove this memory region from the rasterizer cache. - VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size); + system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset, + itr->second.size); + auto& gpu = system_instance.GPU(); params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); buffer_mappings.erase(itr->second.offset); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 671b092e17..5685eb2be0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstdlib> +#include <cstring> + #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 090261a60e..6b496e9fe1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -5,8 +5,6 @@ #pragma once #include <array> -#include <cstdlib> -#include <cstring> #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" 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 44e062f50d..ae421247d6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" @@ -97,7 +97,9 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlActiveSlotMask params{}; - std::memcpy(¶ms, input.data(), input.size()); + if (input.size() > 0) { + std::memcpy(¶ms, input.data(), input.size()); + } params.slot = 0x07; params.mask = 0x01; std::memcpy(output.data(), ¶ms, output.size()); @@ -107,7 +109,9 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlZcullGetCtxSize params{}; - std::memcpy(¶ms, input.data(), input.size()); + if (input.size() > 0) { + std::memcpy(¶ms, input.data(), input.size()); + } params.size = 0x1; std::memcpy(output.data(), ¶ms, output.size()); return 0; @@ -116,7 +120,11 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlNvgpuGpuZcullGetInfoArgs params{}; - std::memcpy(¶ms, input.data(), input.size()); + + if (input.size() > 0) { + std::memcpy(¶ms, input.data(), input.size()); + } + params.width_align_pixels = 0x20; params.height_align_pixels = 0x20; params.pixel_squares_by_aliquots = 0x400; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 126782573a..116dabedbe 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -2,12 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> -#include <map> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/memory.h" +#include "video_core/gpu.h" +#include "video_core/memory_manager.h" namespace Service::Nvidia::Devices { @@ -132,9 +134,12 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, params.num_entries, params.flags); - auto entries = std::vector<IoctlGpfifoEntry>(); - entries.resize(params.num_entries); - std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)], + ASSERT_MSG(input.size() == + sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(IoctlGpfifoEntry), + "Incorrect input size"); + + std::vector<IoctlGpfifoEntry> entries(params.num_entries); + std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(IoctlGpfifoEntry)); for (auto entry : entries) { Tegra::GPUVAddr va_addr = entry.Address(); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index aa8df2e6eb..650ed8fbc0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -6,6 +6,7 @@ #include <memory> #include <vector> +#include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index b51c73ee89..364619e672 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0192aecdd8..6ad74421b4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -4,11 +4,9 @@ #pragma once -#include <array> -#include <cstdlib> -#include <cstring> #include <vector> #include "common/common_types.h" +#include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia::Devices { diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 724eeb1392..e9305bfb3d 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> -#include <cinttypes> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 959b5ba296..1c3529bb66 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -5,7 +5,6 @@ #pragma once #include <memory> -#include <string> #include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 1555ea806a..e8b30921af 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -54,7 +54,7 @@ u32 Module::Open(const std::string& device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp index 9ca6e55129..0e8e21badb 100644 --- a/src/core/hle/service/nvdrv/nvmemp.cpp +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -4,8 +4,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvmemp.h" namespace Service::Nvidia { diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 7132b18ad3..adf180509f 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -4,9 +4,8 @@ #include <algorithm> -#include "common/alignment.h" -#include "common/scope_exit.h" -#include "core/core_timing.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "core/hle/service/nvflinger/buffer_queue.h" namespace Service { diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 5344441e1d..570aa8493b 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -3,8 +3,11 @@ // Refer to the license.txt file included. #include <algorithm> +#include <boost/optional.hpp> #include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "common/microprofile.h" #include "common/scope_exit.h" #include "core/core.h" @@ -31,7 +34,7 @@ NVFlinger::NVFlinger() { // Schedule the screen composition events composition_event = - CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { + CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { Compose(); CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); }); @@ -43,7 +46,7 @@ NVFlinger::~NVFlinger() { CoreTiming::UnscheduleEvent(composition_event, 0); } -u64 NVFlinger::OpenDisplay(const std::string& name) { +u64 NVFlinger::OpenDisplay(std::string_view name) { LOG_WARNING(Service, "Opening display {}", name); // TODO(Subv): Currently we only support the Default display. @@ -127,9 +130,11 @@ void NVFlinger::Compose() { MicroProfileFlip(); if (buffer == boost::none) { + auto& system_instance = Core::System::GetInstance(); + // There was no queued buffer to draw, render previous frame - Core::System::GetInstance().perf_stats.EndGameFrame(); - VideoCore::g_renderer->SwapBuffers({}); + system_instance.perf_stats.EndGameFrame(); + system_instance.Renderer().SwapBuffers({}); continue; } diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 2c908297ba..5374df175a 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -5,7 +5,11 @@ #pragma once #include <memory> -#include <boost/optional.hpp> +#include <string> +#include <string_view> +#include <vector> + +#include "common/common_types.h" #include "core/hle/kernel/event.h" namespace CoreTiming { @@ -41,7 +45,7 @@ public: ~NVFlinger(); /// Opens the specified display and returns the id. - u64 OpenDisplay(const std::string& name); + u64 OpenDisplay(std::string_view name); /// Creates a layer on the specified display and returns the layer id. u64 CreateLayer(u64 display_id); diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp new file mode 100644 index 0000000000..39cf05eba2 --- /dev/null +++ b/src/core/hle/service/pcie/pcie.cpp @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/pcie/pcie.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PCIe { + +class ISession final : public ServiceFramework<ISession> { +public: + explicit ISession() : ServiceFramework{"ISession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "QueryFunctions"}, + {1, nullptr, "AcquireFunction"}, + {2, nullptr, "ReleaseFunction"}, + {3, nullptr, "GetFunctionState"}, + {4, nullptr, "GetBarProfile"}, + {5, nullptr, "ReadConfig"}, + {6, nullptr, "WriteConfig"}, + {7, nullptr, "ReadBarRegion"}, + {8, nullptr, "WriteBarRegion"}, + {9, nullptr, "FindCapability"}, + {10, nullptr, "FindExtendedCapability"}, + {11, nullptr, "MapDma"}, + {12, nullptr, "UnmapDma"}, + {13, nullptr, "UnmapDmaBusAddress"}, + {14, nullptr, "GetDmaBusAddress"}, + {15, nullptr, "GetDmaBusAddressRange"}, + {16, nullptr, "SetDmaEnable"}, + {17, nullptr, "AcquireIrq"}, + {18, nullptr, "ReleaseIrq"}, + {19, nullptr, "SetIrqEnable"}, + {20, nullptr, "SetAspmEnable"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PCIe final : public ServiceFramework<PCIe> { +public: + explicit PCIe() : ServiceFramework{"pcie"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterClassDriver"}, + {1, nullptr, "QueryFunctionsUnregistered"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PCIe>()->InstallAsService(sm); +} + +} // namespace Service::PCIe diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h new file mode 100644 index 0000000000..59c22ca45a --- /dev/null +++ b/src/core/hle/service/pcie/pcie.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PCIe { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PCIe diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp new file mode 100644 index 0000000000..d6891a6593 --- /dev/null +++ b/src/core/hle/service/pcv/pcv.cpp @@ -0,0 +1,84 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/pcv/pcv.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PCV { + +class PCV final : public ServiceFramework<PCV> { +public: + explicit PCV() : ServiceFramework{"pcv"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetPowerEnabled"}, + {1, nullptr, "SetClockEnabled"}, + {2, nullptr, "SetClockRate"}, + {3, nullptr, "GetClockRate"}, + {4, nullptr, "GetState"}, + {5, nullptr, "GetPossibleClockRates"}, + {6, nullptr, "SetMinVClockRate"}, + {7, nullptr, "SetReset"}, + {8, nullptr, "SetVoltageEnabled"}, + {9, nullptr, "GetVoltageEnabled"}, + {10, nullptr, "GetVoltageRange"}, + {11, nullptr, "SetVoltageValue"}, + {12, nullptr, "GetVoltageValue"}, + {13, nullptr, "GetTemperatureThresholds"}, + {14, nullptr, "SetTemperature"}, + {15, nullptr, "Initialize"}, + {16, nullptr, "IsInitialized"}, + {17, nullptr, "Finalize"}, + {18, nullptr, "PowerOn"}, + {19, nullptr, "PowerOff"}, + {20, nullptr, "ChangeVoltage"}, + {21, nullptr, "GetPowerClockInfoEvent"}, + {22, nullptr, "GetOscillatorClock"}, + {23, nullptr, "GetDvfsTable"}, + {24, nullptr, "GetModuleStateTable"}, + {25, nullptr, "GetPowerDomainStateTable"}, + {26, nullptr, "GetFuseInfo"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PCV_ARB final : public ServiceFramework<PCV_ARB> { +public: + explicit PCV_ARB() : ServiceFramework{"pcv:arb"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ReleaseControl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PCV_IMM final : public ServiceFramework<PCV_IMM> { +public: + explicit PCV_IMM() : ServiceFramework{"pcv:imm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetClockRate"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PCV>()->InstallAsService(sm); + std::make_shared<PCV_ARB>()->InstallAsService(sm); + std::make_shared<PCV_IMM>()->InstallAsService(sm); +} + +} // namespace Service::PCV diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h new file mode 100644 index 0000000000..219a893c30 --- /dev/null +++ b/src/core/hle/service/pcv/pcv.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PCV { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PCV diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp new file mode 100644 index 0000000000..bbad870a2a --- /dev/null +++ b/src/core/hle/service/psc/psc.cpp @@ -0,0 +1,77 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/psc/psc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PSC { + +class PSC_C final : public ServiceFramework<PSC_C> { +public: + explicit PSC_C() : ServiceFramework{"psc:c"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "Unknown7"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IPmModule final : public ServiceFramework<IPmModule> { +public: + explicit IPmModule() : ServiceFramework{"IPmModule"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "GetRequest"}, + {2, nullptr, "Acknowledge"}, + {3, nullptr, "Unknown1"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PSC_M final : public ServiceFramework<PSC_M> { +public: + explicit PSC_M() : ServiceFramework{"psc:m"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &PSC_M::GetPmModule, "GetPmModule"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetPmModule(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IPmModule>(); + + LOG_DEBUG(Service_PSC, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PSC_C>()->InstallAsService(sm); + std::make_shared<PSC_M>()->InstallAsService(sm); +} + +} // namespace Service::PSC diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h new file mode 100644 index 0000000000..5052eb02ce --- /dev/null +++ b/src/core/hle/service/psc/psc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PSC { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PSC diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 8b84fd3491..889cdd41ac 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -19,28 +19,42 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" +#include "core/hle/service/arp/arp.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/bcat/bcat.h" +#include "core/hle/service/bpc/bpc.h" +#include "core/hle/service/btdrv/btdrv.h" +#include "core/hle/service/btm/btm.h" +#include "core/hle/service/caps/caps.h" #include "core/hle/service/erpt/erpt.h" #include "core/hle/service/es/es.h" #include "core/hle/service/eupld/eupld.h" #include "core/hle/service/fatal/fatal.h" +#include "core/hle/service/fgm/fgm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/friend/friend.h" #include "core/hle/service/grc/grc.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/lbl/lbl.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldr/ldr.h" #include "core/hle/service/lm/lm.h" +#include "core/hle/service/mig/mig.h" +#include "core/hle/service/mii/mii.h" #include "core/hle/service/mm/mm_u.h" +#include "core/hle/service/ncm/ncm.h" +#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/pcie/pcie.h" #include "core/hle/service/pctl/pctl.h" +#include "core/hle/service/pcv/pcv.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" +#include "core/hle/service/psc/psc.h" #include "core/hle/service/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/controller.h" @@ -49,7 +63,9 @@ #include "core/hle/service/spl/module.h" #include "core/hle/service/ssl/ssl.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" +#include "core/hle/service/wlan/wlan.h" using Kernel::ClientPort; using Kernel::ServerPort; @@ -193,34 +209,50 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) { AM::InstallInterfaces(*sm, nv_flinger); AOC::InstallInterfaces(*sm); APM::InstallInterfaces(*sm); - BCAT::InstallInterfaces(*sm); + ARP::InstallInterfaces(*sm); Audio::InstallInterfaces(*sm); + BCAT::InstallInterfaces(*sm); + BPC::InstallInterfaces(*sm); + BtDrv::InstallInterfaces(*sm); + BTM::InstallInterfaces(*sm); + Capture::InstallInterfaces(*sm); ERPT::InstallInterfaces(*sm); ES::InstallInterfaces(*sm); EUPLD::InstallInterfaces(*sm); Fatal::InstallInterfaces(*sm); + FGM::InstallInterfaces(*sm); FileSystem::InstallInterfaces(*sm); Friend::InstallInterfaces(*sm); GRC::InstallInterfaces(*sm); HID::InstallInterfaces(*sm); + LBL::InstallInterfaces(*sm); LDN::InstallInterfaces(*sm); LDR::InstallInterfaces(*sm); LM::InstallInterfaces(*sm); + Migration::InstallInterfaces(*sm); + Mii::InstallInterfaces(*sm); MM::InstallInterfaces(*sm); + NCM::InstallInterfaces(*sm); + NFC::InstallInterfaces(*sm); NFP::InstallInterfaces(*sm); NIFM::InstallInterfaces(*sm); NIM::InstallInterfaces(*sm); NS::InstallInterfaces(*sm); Nvidia::InstallInterfaces(*sm); + PCIe::InstallInterfaces(*sm); PCTL::InstallInterfaces(*sm); + PCV::InstallInterfaces(*sm); PlayReport::InstallInterfaces(*sm); PM::InstallInterfaces(*sm); + PSC::InstallInterfaces(*sm); + Set::InstallInterfaces(*sm); Sockets::InstallInterfaces(*sm); SPL::InstallInterfaces(*sm); SSL::InstallInterfaces(*sm); Time::InstallInterfaces(*sm); + USB::InstallInterfaces(*sm); VI::InstallInterfaces(*sm, nv_flinger); - Set::InstallInterfaces(*sm); + WLAN::InstallInterfaces(*sm); LOG_DEBUG(Service, "initialized OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 180f22703a..046c5e18da 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -8,10 +8,9 @@ #include <string> #include <unordered_map> #include <boost/container/flat_map.hpp> -#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 1651f61229..a461e72ec4 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -8,6 +8,7 @@ #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/service/set/set.h" +#include "core/settings.h" namespace Service::Set { @@ -31,6 +32,10 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ LanguageCode::ZH_HANT, }}; +LanguageCode GetLanguageCodeFromIndex(size_t index) { + return available_language_codes.at(index); +} + void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { ctx.WriteBuffer(available_language_codes); @@ -49,9 +54,17 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); } +void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(available_language_codes[Settings::values.language_index])); + + LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); +} + SET::SET() : ServiceFramework("set") { static const FunctionInfo functions[] = { - {0, nullptr, "GetLanguageCode"}, + {0, &SET::GetLanguageCode, "GetLanguageCode"}, {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, {2, nullptr, "MakeLanguageCode"}, {3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index a2472ec4cf..4232b6162b 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -28,6 +28,7 @@ enum class LanguageCode : u64 { ZH_HANS = 0x00736E61482D687A, ZH_HANT = 0x00746E61482D687A, }; +LanguageCode GetLanguageCodeFromIndex(size_t idx); class SET final : public ServiceFramework<SET> { public: @@ -35,6 +36,7 @@ public: ~SET() = default; private: + void GetLanguageCode(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); }; diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index e2a00e4f68..e8ea62f084 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -4,9 +4,11 @@ #pragma once +#include <memory> #include <string> #include <unordered_map> -#include "core/hle/kernel/kernel.h" + +#include "core/hle/kernel/object.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -19,6 +21,8 @@ class SessionRequestHandler; namespace Service::SM { +class Controller; + /// Interface to "sm:" service class SM final : public ServiceFramework<SM> { public: @@ -32,8 +36,6 @@ private: std::shared_ptr<ServiceManager> service_manager; }; -class Controller; - constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1); constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); constexpr ResultCode ERR_INVALID_NAME_SIZE(-1); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 37b58bb772..2172c681b2 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -80,8 +80,8 @@ public: {5, nullptr, "GetTimeZoneRuleVersion"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {200, nullptr, "ToPosixTime"}, - {201, nullptr, "ToPosixTimeWithMyRule"}, + {201, nullptr, "ToPosixTime"}, + {202, nullptr, "ToPosixTimeWithMyRule"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp new file mode 100644 index 0000000000..e7fb5a419f --- /dev/null +++ b/src/core/hle/service/usb/usb.cpp @@ -0,0 +1,238 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/usb/usb.h" + +namespace Service::USB { + +class IDsInterface final : public ServiceFramework<IDsInterface> { +public: + explicit IDsInterface() : ServiceFramework{"IDsInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetDsEndpoint"}, + {1, nullptr, "GetSetupEvent"}, + {2, nullptr, "Unknown"}, + {3, nullptr, "EnableInterface"}, + {4, nullptr, "DisableInterface"}, + {5, nullptr, "CtrlInPostBufferAsync"}, + {6, nullptr, "CtrlOutPostBufferAsync"}, + {7, nullptr, "GetCtrlInCompletionEvent"}, + {8, nullptr, "GetCtrlInReportData"}, + {9, nullptr, "GetCtrlOutCompletionEvent"}, + {10, nullptr, "GetCtrlOutReportData"}, + {11, nullptr, "StallCtrl"}, + {12, nullptr, "AppendConfigurationData"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_DS final : public ServiceFramework<USB_DS> { +public: + explicit USB_DS() : ServiceFramework{"usb:ds"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "BindDevice"}, + {1, nullptr, "BindClientProcess"}, + {2, nullptr, "GetDsInterface"}, + {3, nullptr, "GetStateChangeEvent"}, + {4, nullptr, "GetState"}, + {5, nullptr, "ClearDeviceData"}, + {6, nullptr, "AddUsbStringDescriptor"}, + {7, nullptr, "DeleteUsbStringDescriptor"}, + {8, nullptr, "SetUsbDeviceDescriptor"}, + {9, nullptr, "SetBinaryObjectStore"}, + {10, nullptr, "Enable"}, + {11, nullptr, "Disable"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IClientEpSession final : public ServiceFramework<IClientEpSession> { +public: + explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "PostBufferAsync"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IClientIfSession final : public ServiceFramework<IClientIfSession> { +public: + explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "CtrlXferAsync"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "GetCtrlXferReport"}, + {8, nullptr, "Unknown7"}, + {9, nullptr, "GetClientEpSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_HS final : public ServiceFramework<USB_HS> { +public: + explicit USB_HS() : ServiceFramework{"usb:hs"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "BindClientProcess"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "GetInterfaceStateChangeEvent"}, + {7, nullptr, "GetClientIfSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IPdSession final : public ServiceFramework<IPdSession> { +public: + explicit IPdSession() : ServiceFramework{"IPdSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "BindNoticeEvent"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "GetStatus"}, + {3, nullptr, "GetNotice"}, + {4, nullptr, "Unknown2"}, + {5, nullptr, "Unknown3"}, + {6, nullptr, "ReplyPowerRequest"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_PD final : public ServiceFramework<USB_PD> { +public: + explicit USB_PD() : ServiceFramework{"usb:pd"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &USB_PD::GetPdSession, "GetPdSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetPdSession(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IPdSession>(); + + LOG_DEBUG(Service_USB, "called"); + } +}; + +class IPdCradleSession final : public ServiceFramework<IPdCradleSession> { +public: + explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "VdmUserWrite"}, + {1, nullptr, "VdmUserRead"}, + {2, nullptr, "Vdm20Init"}, + {3, nullptr, "GetFwType"}, + {4, nullptr, "GetFwRevision"}, + {5, nullptr, "GetManufacturerId"}, + {6, nullptr, "GetDeviceId"}, + {7, nullptr, "Unknown1"}, + {8, nullptr, "Unknown2"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_PD_C final : public ServiceFramework<USB_PD_C> { +public: + explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetPdCradleSession(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IPdCradleSession>(); + + LOG_DEBUG(Service_USB, "called"); + } +}; + +class USB_PM final : public ServiceFramework<USB_PM> { +public: + explicit USB_PM() : ServiceFramework{"usb:pm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<USB_DS>()->InstallAsService(sm); + std::make_shared<USB_HS>()->InstallAsService(sm); + std::make_shared<USB_PD>()->InstallAsService(sm); + std::make_shared<USB_PD_C>()->InstallAsService(sm); + std::make_shared<USB_PM>()->InstallAsService(sm); +} + +} // namespace Service::USB diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h new file mode 100644 index 0000000000..970a11fe89 --- /dev/null +++ b/src/core/hle/service/usb/usb.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::USB { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::USB diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp new file mode 100644 index 0000000000..2654594c16 --- /dev/null +++ b/src/core/hle/service/wlan/wlan.cpp @@ -0,0 +1,172 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/wlan/wlan.h" + +namespace Service::WLAN { + +class WLANInfra final : public ServiceFramework<WLANInfra> { +public: + explicit WLANInfra() : ServiceFramework{"wlan:inf"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "GetMacAddress"}, + {3, nullptr, "StartScan"}, + {4, nullptr, "StopScan"}, + {5, nullptr, "Connect"}, + {6, nullptr, "CancelConnect"}, + {7, nullptr, "Disconnect"}, + {8, nullptr, "Unknown3"}, + {9, nullptr, "Unknown4"}, + {10, nullptr, "GetState"}, + {11, nullptr, "GetScanResult"}, + {12, nullptr, "GetRssi"}, + {13, nullptr, "ChangeRxAntenna"}, + {14, nullptr, "Unknown5"}, + {15, nullptr, "Unknown6"}, + {16, nullptr, "RequestWakeUp"}, + {17, nullptr, "RequestIfUpDown"}, + {18, nullptr, "Unknown7"}, + {19, nullptr, "Unknown8"}, + {20, nullptr, "Unknown9"}, + {21, nullptr, "Unknown10"}, + {22, nullptr, "Unknown11"}, + {23, nullptr, "Unknown12"}, + {24, nullptr, "Unknown13"}, + {25, nullptr, "Unknown14"}, + {26, nullptr, "Unknown15"}, + {27, nullptr, "Unknown16"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANLocal final : public ServiceFramework<WLANLocal> { +public: + explicit WLANLocal() : ServiceFramework{"wlan:lcl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "GetMacAddress"}, + {7, nullptr, "CreateBss"}, + {8, nullptr, "DestroyBss"}, + {9, nullptr, "StartScan"}, + {10, nullptr, "StopScan"}, + {11, nullptr, "Connect"}, + {12, nullptr, "CancelConnect"}, + {13, nullptr, "Join"}, + {14, nullptr, "CancelJoin"}, + {15, nullptr, "Disconnect"}, + {16, nullptr, "SetBeaconLostCount"}, + {17, nullptr, "Unknown7"}, + {18, nullptr, "Unknown8"}, + {19, nullptr, "Unknown9"}, + {20, nullptr, "GetBssIndicationEvent"}, + {21, nullptr, "GetBssIndicationInfo"}, + {22, nullptr, "GetState"}, + {23, nullptr, "GetAllowedChannels"}, + {24, nullptr, "AddIe"}, + {25, nullptr, "DeleteIe"}, + {26, nullptr, "Unknown10"}, + {27, nullptr, "Unknown11"}, + {28, nullptr, "CreateRxEntry"}, + {29, nullptr, "DeleteRxEntry"}, + {30, nullptr, "Unknown12"}, + {31, nullptr, "Unknown13"}, + {32, nullptr, "AddMatchingDataToRxEntry"}, + {33, nullptr, "RemoveMatchingDataFromRxEntry"}, + {34, nullptr, "GetScanResult"}, + {35, nullptr, "Unknown14"}, + {36, nullptr, "SetActionFrameWithBeacon"}, + {37, nullptr, "CancelActionFrameWithBeacon"}, + {38, nullptr, "CreateRxEntryForActionFrame"}, + {39, nullptr, "DeleteRxEntryForActionFrame"}, + {40, nullptr, "Unknown15"}, + {41, nullptr, "Unknown16"}, + {42, nullptr, "CancelGetActionFrame"}, + {43, nullptr, "GetRssi"}, + {44, nullptr, "Unknown17"}, + {45, nullptr, "Unknown18"}, + {46, nullptr, "Unknown19"}, + {47, nullptr, "Unknown20"}, + {48, nullptr, "Unknown21"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANLocalGetFrame final : public ServiceFramework<WLANLocalGetFrame> { +public: + explicit WLANLocalGetFrame() : ServiceFramework{"wlan:lg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANSocketGetFrame final : public ServiceFramework<WLANSocketGetFrame> { +public: + explicit WLANSocketGetFrame() : ServiceFramework{"wlan:sg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANSocketManager final : public ServiceFramework<WLANSocketManager> { +public: + explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "GetMacAddress"}, + {7, nullptr, "SwitchTsfTimerFunction"}, + {8, nullptr, "Unknown7"}, + {9, nullptr, "Unknown8"}, + {10, nullptr, "Unknown9"}, + {11, nullptr, "Unknown10"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<WLANInfra>()->InstallAsService(sm); + std::make_shared<WLANLocal>()->InstallAsService(sm); + std::make_shared<WLANLocalGetFrame>()->InstallAsService(sm); + std::make_shared<WLANSocketGetFrame>()->InstallAsService(sm); + std::make_shared<WLANSocketManager>()->InstallAsService(sm); +} + +} // namespace Service::WLAN diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h new file mode 100644 index 0000000000..054ea928a1 --- /dev/null +++ b/src/core/hle/service/wlan/wlan.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::WLAN { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::WLAN diff --git a/src/core/hw/aes/ccm.cpp b/src/core/hw/aes/ccm.cpp deleted file mode 100644 index 1ee37aaa4d..0000000000 --- a/src/core/hw/aes/ccm.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include "common/alignment.h" -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/hw/aes/ccm.h" -#include "core/hw/aes/key.h" - -namespace HW { -namespace AES { - -std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce, - size_t slot_id) { - UNIMPLEMENTED(); - return {}; -} - -std::vector<u8> DecryptVerifyCCM(const std::vector<u8>& cipher, const CCMNonce& nonce, - size_t slot_id) { - UNIMPLEMENTED(); - return {}; -} - -} // namespace AES -} // namespace HW diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp deleted file mode 100644 index 2f48068c1e..0000000000 --- a/src/core/hw/hw.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hw/hw.h" -#include "core/hw/lcd.h" - -namespace HW { - -template <typename T> -inline void Read(T& var, const u32 addr) { - switch (addr & 0xFFFFF000) { - case VADDR_GPU: - case VADDR_GPU + 0x1000: - case VADDR_GPU + 0x2000: - case VADDR_GPU + 0x3000: - case VADDR_GPU + 0x4000: - case VADDR_GPU + 0x5000: - case VADDR_GPU + 0x6000: - case VADDR_GPU + 0x7000: - case VADDR_GPU + 0x8000: - case VADDR_GPU + 0x9000: - case VADDR_GPU + 0xA000: - case VADDR_GPU + 0xB000: - case VADDR_GPU + 0xC000: - case VADDR_GPU + 0xD000: - case VADDR_GPU + 0xE000: - case VADDR_GPU + 0xF000: - break; - case VADDR_LCD: - LCD::Read(var, addr); - break; - default: - LOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); - break; - } -} - -template <typename T> -inline void Write(u32 addr, const T data) { - switch (addr & 0xFFFFF000) { - case VADDR_GPU: - case VADDR_GPU + 0x1000: - case VADDR_GPU + 0x2000: - case VADDR_GPU + 0x3000: - case VADDR_GPU + 0x4000: - case VADDR_GPU + 0x5000: - case VADDR_GPU + 0x6000: - case VADDR_GPU + 0x7000: - case VADDR_GPU + 0x8000: - case VADDR_GPU + 0x9000: - case VADDR_GPU + 0xA000: - case VADDR_GPU + 0xB000: - case VADDR_GPU + 0xC000: - case VADDR_GPU + 0xD000: - case VADDR_GPU + 0xE000: - case VADDR_GPU + 0xF000: - break; - case VADDR_LCD: - LCD::Write(addr, data); - break; - default: - LOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); - break; - } -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64& var, const u32 addr); -template void Read<u32>(u32& var, const u32 addr); -template void Read<u16>(u16& var, const u32 addr); -template void Read<u8>(u8& var, const u32 addr); - -template void Write<u64>(u32 addr, const u64 data); -template void Write<u32>(u32 addr, const u32 data); -template void Write<u16>(u32 addr, const u16 data); -template void Write<u8>(u32 addr, const u8 data); - -/// Update hardware -void Update() {} - -/// Initialize hardware -void Init() { - LCD::Init(); - LOG_DEBUG(HW, "Initialized OK"); -} - -/// Shutdown hardware -void Shutdown() { - LCD::Shutdown(); - LOG_DEBUG(HW, "Shutdown OK"); -} -} // namespace HW diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h deleted file mode 100644 index 5890d2b5c0..0000000000 --- a/src/core/hw/hw.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -namespace HW { - -/// Beginnings of IO register regions, in the user VA space. -enum : u32 { - VADDR_HASH = 0x1EC01000, - VADDR_CSND = 0x1EC03000, - VADDR_DSP = 0x1EC40000, - VADDR_PDN = 0x1EC41000, - VADDR_CODEC = 0x1EC41000, - VADDR_SPI = 0x1EC42000, - VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM? - VADDR_I2C = 0x1EC44000, - VADDR_CODEC_2 = 0x1EC45000, - VADDR_HID = 0x1EC46000, - VADDR_GPIO = 0x1EC47000, - VADDR_I2C_2 = 0x1EC48000, - VADDR_SPI_3 = 0x1EC60000, - VADDR_I2C_3 = 0x1EC61000, - VADDR_MIC = 0x1EC62000, - VADDR_PXI = 0x1EC63000, - VADDR_LCD = 0x1ED02000, - VADDR_DSP_2 = 0x1ED03000, - VADDR_HASH_2 = 0x1EE01000, - VADDR_GPU = 0x1EF00000, -}; - -template <typename T> -void Read(T& var, const u32 addr); - -template <typename T> -void Write(u32 addr, const T data); - -/// Update hardware -void Update(); - -/// Initialize hardware -void Init(); - -/// Shutdown hardware -void Shutdown(); - -} // namespace HW diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp deleted file mode 100644 index 0b62174d58..0000000000 --- a/src/core/hw/lcd.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstring> -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hw/hw.h" -#include "core/hw/lcd.h" -#include "core/tracer/recorder.h" - -namespace LCD { - -Regs g_regs; - -template <typename T> -inline void Read(T& var, const u32 raw_addr) { - u32 addr = raw_addr - HW::VADDR_LCD; - u32 index = addr / 4; - - // Reads other than u32 are untested, so I'd rather have them abort than silently fail - if (index >= 0x400 || !std::is_same<T, u32>::value) { - LOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); - return; - } - - var = g_regs[index]; -} - -template <typename T> -inline void Write(u32 addr, const T data) { - addr -= HW::VADDR_LCD; - u32 index = addr / 4; - - // Writes other than u32 are untested, so I'd rather have them abort than silently fail - if (index >= 0x400 || !std::is_same<T, u32>::value) { - LOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); - return; - } - - g_regs[index] = static_cast<u32>(data); -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64& var, const u32 addr); -template void Read<u32>(u32& var, const u32 addr); -template void Read<u16>(u16& var, const u32 addr); -template void Read<u8>(u8& var, const u32 addr); - -template void Write<u64>(u32 addr, const u64 data); -template void Write<u32>(u32 addr, const u32 data); -template void Write<u16>(u32 addr, const u16 data); -template void Write<u8>(u32 addr, const u8 data); - -/// Initialize hardware -void Init() { - memset(&g_regs, 0, sizeof(g_regs)); - LOG_DEBUG(HW_LCD, "Initialized OK"); -} - -/// Shutdown hardware -void Shutdown() { - LOG_DEBUG(HW_LCD, "Shutdown OK"); -} - -} // namespace LCD diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h deleted file mode 100644 index d2db9700f4..0000000000 --- a/src/core/hw/lcd.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <cstddef> -#include <type_traits> -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" - -#define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32)) - -namespace LCD { - -struct Regs { - - union ColorFill { - u32 raw; - - BitField<0, 8, u32> color_r; - BitField<8, 8, u32> color_g; - BitField<16, 8, u32> color_b; - BitField<24, 1, u32> is_enabled; - }; - - INSERT_PADDING_WORDS(0x81); - ColorFill color_fill_top; - INSERT_PADDING_WORDS(0xE); - u32 backlight_top; - - INSERT_PADDING_WORDS(0x1F0); - - ColorFill color_fill_bottom; - INSERT_PADDING_WORDS(0xE); - u32 backlight_bottom; - INSERT_PADDING_WORDS(0x16F); - - static constexpr size_t NumIds() { - return sizeof(Regs) / sizeof(u32); - } - - const u32& operator[](int index) const { - const u32* content = reinterpret_cast<const u32*>(this); - return content[index]; - } - - u32& operator[](int index) { - u32* content = reinterpret_cast<u32*>(this); - return content[index]; - } -}; -static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout"); - -// TODO: MSVC does not support using offsetof() on non-static data members even though this -// is technically allowed since C++11. This macro should be enabled once MSVC adds -// support for that. -#ifndef _MSC_VER -#define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Regs, field_name) == position * 4, \ - "Field " #field_name " has invalid position") - -ASSERT_REG_POSITION(color_fill_top, 0x81); -ASSERT_REG_POSITION(backlight_top, 0x90); -ASSERT_REG_POSITION(color_fill_bottom, 0x281); -ASSERT_REG_POSITION(backlight_bottom, 0x290); - -#undef ASSERT_REG_POSITION -#endif // !defined(_MSC_VER) - -extern Regs g_regs; - -template <typename T> -void Read(T& var, const u32 addr); - -template <typename T> -void Write(u32 addr, const T data); - -/// Initialize hardware -void Init(); - -/// Shutdown hardware -void Shutdown(); - -} // namespace LCD diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index b0277a8750..9a8cdd0ff4 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -20,6 +20,10 @@ namespace Loader { AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} +AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( + FileSys::VirtualDir directory) + : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {} + FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { return FileType::DeconstructedRomDirectory; @@ -34,7 +38,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( return ResultStatus::ErrorAlreadyLoaded; } - const FileSys::VirtualDir dir = file->GetContainingDirectory(); + if (dir == nullptr) { + if (file == nullptr) + return ResultStatus::ErrorInvalidFormat; + dir = file->GetContainingDirectory(); + } + const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); if (npdm == nullptr) return ResultStatus::ErrorInvalidFormat; diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 982a037f78..7d54335633 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -7,7 +7,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/loader.h" namespace Loader { @@ -22,6 +22,9 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader { public: explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); + // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' + explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory); + /** * Returns the type of the file * @param file std::shared_ptr<VfsFile> open file @@ -40,6 +43,7 @@ public: private: FileSys::ProgramMetadata metadata; FileSys::VirtualFile romfs; + FileSys::VirtualDir dir; }; } // namespace Loader diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 352938dcb4..a7133f5a63 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -311,11 +311,11 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { CodeSet::Segment* codeset_segment; u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); if (permission_flags == (PF_R | PF_X)) { - codeset_segment = &codeset->code; + codeset_segment = &codeset->CodeSegment(); } else if (permission_flags == (PF_R)) { - codeset_segment = &codeset->rodata; + codeset_segment = &codeset->RODataSegment(); } else if (permission_flags == (PF_R | PF_W)) { - codeset_segment = &codeset->data; + codeset_segment = &codeset->DataSegment(); } else { LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, p->p_flags); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index cbc4177c61..57e6c03653 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -13,6 +13,7 @@ #include "core/loader/nca.h" #include "core/loader/nro.h" #include "core/loader/nso.h" +#include "core/loader/xci.h" namespace Loader { @@ -35,6 +36,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) { CHECK_TYPE(NSO) CHECK_TYPE(NRO) CHECK_TYPE(NCA) + CHECK_TYPE(XCI) #undef CHECK_TYPE @@ -60,6 +62,8 @@ FileType GuessFromFilename(const std::string& name) { return FileType::NSO; if (extension == "nca") return FileType::NCA; + if (extension == "xci") + return FileType::XCI; return FileType::Unknown; } @@ -74,6 +78,8 @@ const char* GetFileTypeString(FileType type) { return "NSO"; case FileType::NCA: return "NCA"; + case FileType::XCI: + return "XCI"; case FileType::DeconstructedRomDirectory: return "Directory"; case FileType::Error: @@ -111,6 +117,9 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT case FileType::NCA: return std::make_unique<AppLoader_NCA>(std::move(file)); + case FileType::XCI: + return std::make_unique<AppLoader_XCI>(std::move(file)); + // NX deconstructed ROM directory. case FileType::DeconstructedRomDirectory: return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index fbf11e5d0f..e69ab85ef9 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -14,7 +14,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "core/file_sys/vfs.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { struct AddressMapping; @@ -31,6 +31,7 @@ enum class FileType { NSO, NRO, NCA, + XCI, DeconstructedRomDirectory, }; @@ -72,7 +73,8 @@ enum class ResultStatus { ErrorNotUsed, ErrorAlreadyLoaded, ErrorMemoryAllocationFailed, - ErrorEncrypted, + ErrorMissingKeys, + ErrorDecrypting, ErrorUnsupportedArch, }; diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index c80df23be0..dbc67c0b52 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -22,15 +22,14 @@ namespace Loader { -AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} +AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_) + : AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {} FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { - // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. - FileSys::NCAHeader header{}; - if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header)) - return FileType::Error; + FileSys::NCA nca(file); - if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program) + if (nca.GetStatus() == ResultStatus::Success && + nca.GetType() == FileSys::NCAContentType::Program) return FileType::NCA; return FileType::Error; @@ -41,8 +40,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { return ResultStatus::ErrorAlreadyLoaded; } - nca = std::make_unique<FileSys::NCA>(file); - ResultStatus result = nca->GetStatus(); + const auto result = nca->GetStatus(); if (result != ResultStatus::Success) { return result; } @@ -50,44 +48,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { if (nca->GetType() != FileSys::NCAContentType::Program) return ResultStatus::ErrorInvalidFormat; - auto exefs = nca->GetExeFS(); + const auto exefs = nca->GetExeFS(); if (exefs == nullptr) return ResultStatus::ErrorInvalidFormat; - result = metadata.Load(exefs->GetFile("main.npdm")); - if (result != ResultStatus::Success) { - return result; - } - metadata.Print(); - - const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; - if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { - return ResultStatus::ErrorUnsupportedArch; - } + directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs); - VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; - for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", - "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { - const VAddr load_addr = next_load_addr; - - next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr); - if (next_load_addr) { - LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); - // Register module with GDBStub - GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); - } else { - next_load_addr = load_addr; - } - } - - process->program_id = metadata.GetTitleID(); - process->svc_access_mask.set(); - process->address_mappings = default_address_mappings; - process->resource_limit = - Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), - metadata.GetMainThreadStackSize()); + const auto load_result = directory_loader->Load(process); + if (load_result != ResultStatus::Success) + return load_result; if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); @@ -98,12 +68,21 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { } ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { - if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) + if (nca == nullptr) + return ResultStatus::ErrorNotLoaded; + if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) return ResultStatus::ErrorNotUsed; dir = nca->GetRomFS(); return ResultStatus::Success; } +ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { + if (nca == nullptr) + return ResultStatus::ErrorNotLoaded; + out_program_id = nca->GetTitleId(); + return ResultStatus::Success; +} + AppLoader_NCA::~AppLoader_NCA() = default; } // namespace Loader diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 52c95953ac..0fd2d04175 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -8,8 +8,9 @@ #include "common/common_types.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/loader.h" +#include "deconstructed_rom_directory.h" namespace Loader { @@ -33,12 +34,15 @@ public: ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + ~AppLoader_NCA(); private: FileSys::ProgramMetadata metadata; std::unique_ptr<FileSys::NCA> nca; + std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; }; } // namespace Loader diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 7d3ec2a76d..dc053cdad5 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -159,7 +159,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { // Resize program image to include .bss section and page align each section bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); } - codeset->data.size += bss_size; + codeset->DataSegment().size += bss_size; program_image.resize(static_cast<u32>(program_image.size()) + bss_size); // Load codeset for current process diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 04a0f497e1..bb01c9e25b 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -6,7 +6,7 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/linker.h" #include "core/loader/loader.h" diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 06b1b33f48..fee7d58c60 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -127,7 +127,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { // Resize program image to include .bss section and page align each section bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); } - codeset->data.size += bss_size; + codeset->DataSegment().size += bss_size; const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; program_image.resize(image_size); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 3f7567500a..aaeb1f2a9e 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -6,7 +6,7 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/linker.h" #include "core/loader/loader.h" diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp new file mode 100644 index 0000000000..eb4dee2c25 --- /dev/null +++ b/src/core/loader/xci.cpp @@ -0,0 +1,74 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/file_util.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/swap.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/program_metadata.h" +#include "core/file_sys/romfs.h" +#include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/nso.h" +#include "core/loader/xci.h" +#include "core/memory.h" + +namespace Loader { + +AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) + : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), + nca_loader(std::make_unique<AppLoader_NCA>( + xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} + +AppLoader_XCI::~AppLoader_XCI() = default; + +FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { + FileSys::XCI xci(file); + + if (xci.GetStatus() == ResultStatus::Success && + xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr && + AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) == + FileType::NCA) { + return FileType::XCI; + } + + return FileType::Error; +} + +ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { + if (is_loaded) { + return ResultStatus::ErrorAlreadyLoaded; + } + + if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) { + if (!Core::Crypto::KeyManager::KeyFileExists(false)) + return ResultStatus::ErrorMissingKeys; + return ResultStatus::ErrorDecrypting; + } + + auto result = nca_loader->Load(process); + if (result != ResultStatus::Success) + return result; + + is_loaded = true; + + return ResultStatus::Success; +} + +ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) { + return nca_loader->ReadRomFS(dir); +} + +ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { + return nca_loader->ReadProgramId(out_program_id); +} + +} // namespace Loader diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h new file mode 100644 index 0000000000..0dbcfbdf83 --- /dev/null +++ b/src/core/loader/xci.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "common/common_types.h" +#include "core/file_sys/card_image.h" +#include "core/loader/loader.h" +#include "core/loader/nca.h" + +namespace Loader { + +/// Loads an XCI file +class AppLoader_XCI final : public AppLoader { +public: + explicit AppLoader_XCI(FileSys::VirtualFile file); + ~AppLoader_XCI(); + + /** + * Returns the type of the file + * @param file std::shared_ptr<VfsFile> open file + * @return FileType found, or FileType::Error if this loader doesn't know it + */ + static FileType IdentifyType(const FileSys::VirtualFile& file); + + FileType GetFileType() override { + return IdentifyType(file); + } + + ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + + ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + +private: + FileSys::ProgramMetadata metadata; + + std::unique_ptr<FileSys::XCI> xci; + std::unique_ptr<AppLoader_NCA> nca_loader; +}; + +} // namespace Loader diff --git a/src/core/memory.cpp b/src/core/memory.cpp index e753e34361..1133bcbaf0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -14,7 +14,6 @@ #include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/lock.h" #include "core/memory.h" @@ -24,8 +23,6 @@ namespace Memory { -static std::array<u8, Memory::VRAM_SIZE> vram; - static PageTable* current_page_table = nullptr; void SetCurrentPageTable(PageTable* page_table) { @@ -102,22 +99,6 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin } /** - * This function should only be called for virtual addreses with attribute `PageType::Special`. - */ -static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_table, VAddr vaddr, - u64 size) { - std::set<MemoryHookPointer> result; - auto interval = boost::icl::discrete_interval<VAddr>::closed(vaddr, vaddr + size - 1); - auto interval_list = page_table.special_regions.equal_range(interval); - for (auto it = interval_list.first; it != interval_list.second; ++it) { - for (const auto& region : it->second) { - result.insert(region.handler); - } - } - return result; -} - -/** * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) * using a VMA from the current process */ @@ -242,10 +223,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } -bool IsValidPhysicalAddress(const PAddr paddr) { - return GetPhysicalPointer(paddr) != nullptr; -} - u8* GetPointer(const VAddr vaddr) { u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { @@ -274,61 +251,6 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) { return string; } -u8* GetPhysicalPointer(PAddr address) { - struct MemoryArea { - PAddr paddr_base; - u32 size; - }; - - static constexpr MemoryArea memory_areas[] = { - {VRAM_PADDR, VRAM_SIZE}, - {IO_AREA_PADDR, IO_AREA_SIZE}, - {DSP_RAM_PADDR, DSP_RAM_SIZE}, - {FCRAM_PADDR, FCRAM_N3DS_SIZE}, - }; - - const auto area = - std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { - return address >= area.paddr_base && address < area.paddr_base + area.size; - }); - - if (area == std::end(memory_areas)) { - LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address); - return nullptr; - } - - if (area->paddr_base == IO_AREA_PADDR) { - LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address); - return nullptr; - } - - u64 offset_into_region = address - area->paddr_base; - - u8* target_pointer = nullptr; - switch (area->paddr_base) { - case VRAM_PADDR: - target_pointer = vram.data() + offset_into_region; - break; - case DSP_RAM_PADDR: - break; - case FCRAM_PADDR: - for (const auto& region : Kernel::memory_regions) { - if (offset_into_region >= region.base && - offset_into_region < region.base + region.size) { - target_pointer = - region.linear_heap_memory->data() + offset_into_region - region.base; - break; - } - } - ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address"); - break; - default: - UNREACHABLE(); - } - - return target_pointer; -} - void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) { if (gpu_addr == 0) { return; @@ -404,43 +326,45 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) } void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { + auto& system_instance = Core::System::GetInstance(); + // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be // null here - if (VideoCore::g_renderer == nullptr) { + if (!system_instance.IsPoweredOn()) { return; } VAddr end = start + size; - auto CheckRegion = [&](VAddr region_start, VAddr region_end) { + const auto CheckRegion = [&](VAddr region_start, VAddr region_end) { if (start >= region_end || end <= region_start) { // No overlap with region return; } - VAddr overlap_start = std::max(start, region_start); - VAddr overlap_end = std::min(end, region_end); + const VAddr overlap_start = std::max(start, region_start); + const VAddr overlap_end = std::min(end, region_end); - std::vector<Tegra::GPUVAddr> gpu_addresses = - Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start); + const std::vector<Tegra::GPUVAddr> gpu_addresses = + system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start); if (gpu_addresses.empty()) { return; } - u64 overlap_size = overlap_end - overlap_start; + const u64 overlap_size = overlap_end - overlap_start; for (const auto& gpu_address : gpu_addresses) { - auto* rasterizer = VideoCore::g_renderer->Rasterizer(); + auto& rasterizer = system_instance.Renderer().Rasterizer(); switch (mode) { case FlushMode::Flush: - rasterizer->FlushRegion(gpu_address, overlap_size); + rasterizer.FlushRegion(gpu_address, overlap_size); break; case FlushMode::Invalidate: - rasterizer->InvalidateRegion(gpu_address, overlap_size); + rasterizer.InvalidateRegion(gpu_address, overlap_size); break; case FlushMode::FlushAndInvalidate: - rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size); + rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size); break; } } @@ -666,48 +590,4 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) { CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); } -boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) { - if (addr == 0) { - return 0; - } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { - return addr - VRAM_VADDR + VRAM_PADDR; - } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { - return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR; - } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { - return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR; - } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) { - return addr - DSP_RAM_VADDR + DSP_RAM_PADDR; - } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) { - return addr - IO_AREA_VADDR + IO_AREA_PADDR; - } - - return boost::none; -} - -PAddr VirtualToPhysicalAddress(const VAddr addr) { - auto paddr = TryVirtualToPhysicalAddress(addr); - if (!paddr) { - LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr); - // To help with debugging, set bit on address so that it's obviously invalid. - return addr | 0x80000000; - } - return *paddr; -} - -boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) { - if (addr == 0) { - return 0; - } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { - return addr - VRAM_PADDR + VRAM_VADDR; - } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return addr - FCRAM_PADDR + Core::CurrentProcess()->GetLinearHeapAreaAddress(); - } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { - return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; - } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { - return addr - IO_AREA_PADDR + IO_AREA_VADDR; - } - - return boost::none; -} - } // namespace Memory diff --git a/src/core/memory.h b/src/core/memory.h index 8d5d017a46..b7fb3b9ede 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -6,12 +6,9 @@ #include <array> #include <cstddef> -#include <map> #include <string> #include <tuple> -#include <vector> #include <boost/icl/interval_map.hpp> -#include <boost/optional.hpp> #include "common/common_types.h" #include "core/memory_hook.h" #include "video_core/memory_manager.h" @@ -85,40 +82,6 @@ struct PageTable { std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; }; -/// Physical memory regions as seen from the ARM11 -enum : PAddr { - /// IO register area - IO_AREA_PADDR = 0x10100000, - IO_AREA_SIZE = 0x01000000, ///< IO area size (16MB) - IO_AREA_PADDR_END = IO_AREA_PADDR + IO_AREA_SIZE, - - /// MPCore internal memory region - MPCORE_RAM_PADDR = 0x17E00000, - MPCORE_RAM_SIZE = 0x00002000, ///< MPCore internal memory size (8KB) - MPCORE_RAM_PADDR_END = MPCORE_RAM_PADDR + MPCORE_RAM_SIZE, - - /// Video memory - VRAM_PADDR = 0x18000000, - VRAM_SIZE = 0x00600000, ///< VRAM size (6MB) - VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE, - - /// DSP memory - DSP_RAM_PADDR = 0x1FF00000, - DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB) - DSP_RAM_PADDR_END = DSP_RAM_PADDR + DSP_RAM_SIZE, - - /// AXI WRAM - AXI_WRAM_PADDR = 0x1FF80000, - AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size (512KB) - AXI_WRAM_PADDR_END = AXI_WRAM_PADDR + AXI_WRAM_SIZE, - - /// Main FCRAM - FCRAM_PADDR = 0x20000000, - FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB) - FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB) - FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, -}; - /// Virtual user-space memory regions enum : VAddr { /// Where the application text, data and bss reside. @@ -126,24 +89,6 @@ enum : VAddr { PROCESS_IMAGE_MAX_SIZE = 0x08000000, PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, - /// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical - /// memory. - LINEAR_HEAP_VADDR = 0x14000000, - LINEAR_HEAP_SIZE = 0x08000000, - LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE, - - /// Maps 1:1 to the IO register area. - IO_AREA_VADDR = 0x1EC00000, - IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE, - - /// Maps 1:1 to VRAM. - VRAM_VADDR = 0x1F000000, - VRAM_VADDR_END = VRAM_VADDR + VRAM_SIZE, - - /// Maps 1:1 to DSP memory. - DSP_RAM_VADDR = 0x1FF00000, - DSP_RAM_VADDR_END = DSP_RAM_VADDR + DSP_RAM_SIZE, - /// Read-only page containing kernel and system configuration values. CONFIG_MEMORY_VADDR = 0x1FF80000, CONFIG_MEMORY_SIZE = 0x00001000, @@ -154,13 +99,8 @@ enum : VAddr { SHARED_PAGE_SIZE = 0x00001000, SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, - /// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS. - NEW_LINEAR_HEAP_VADDR = 0x30000000, - NEW_LINEAR_HEAP_SIZE = 0x10000000, - NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, - /// Area where TLS (Thread-Local Storage) buffers are allocated. - TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END, + TLS_AREA_VADDR = 0x40000000, TLS_ENTRY_SIZE = 0x200, TLS_AREA_SIZE = 0x10000000, TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, @@ -200,12 +140,10 @@ void SetCurrentPageTable(PageTable* page_table); PageTable* GetCurrentPageTable(); /// Determines if the given VAddr is valid for the specified process. -bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); -bool IsValidVirtualAddress(const VAddr addr); +bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); +bool IsValidVirtualAddress(VAddr vaddr); /// Determines if the given VAddr is a kernel address -bool IsKernelVirtualAddress(const VAddr addr); - -bool IsValidPhysicalAddress(const PAddr addr); +bool IsKernelVirtualAddress(VAddr vaddr); u8 Read8(VAddr addr); u16 Read16(VAddr addr); @@ -217,42 +155,17 @@ void Write16(VAddr addr, u16 data); void Write32(VAddr addr, u32 data); void Write64(VAddr addr, u64 data); -void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, - size_t size); -void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); -void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, +void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size); +void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size); +void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, size_t size); -void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); -void ZeroBlock(const VAddr dest_addr, const size_t size); +void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size); +void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); -u8* GetPointer(VAddr virtual_address); - -std::string ReadCString(VAddr virtual_address, std::size_t max_length); - -/** - * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical - * address. This should be used by services to translate addresses for use by the hardware. - */ -boost::optional<PAddr> TryVirtualToPhysicalAddress(VAddr addr); - -/** - * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical - * address. This should be used by services to translate addresses for use by the hardware. - * - * @deprecated Use TryVirtualToPhysicalAddress(), which reports failure. - */ -PAddr VirtualToPhysicalAddress(VAddr addr); +u8* GetPointer(VAddr vaddr); -/** - * Undoes a mapping performed by VirtualToPhysicalAddress(). - */ -boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr); - -/** - * Gets a pointer to the memory region beginning at the specified physical address. - */ -u8* GetPhysicalPointer(PAddr address); +std::string ReadCString(VAddr vaddr, std::size_t max_length); enum class FlushMode { /// Write back modified surfaces to RAM @@ -266,7 +179,7 @@ enum class FlushMode { /** * Mark each page touching the region as cached. */ -void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached); +void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached); /** * Flushes and invalidates any externally cached rasterizer resources touching the given virtual diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 5f53b16d34..8e09b9b63d 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -40,22 +40,21 @@ void PerfStats::EndGameFrame() { game_frames += 1; } -PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { +PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) { std::lock_guard<std::mutex> lock(object_mutex); - auto now = Clock::now(); + const auto now = Clock::now(); // Walltime elapsed since stats were reset - auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); + const auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); - auto system_us_per_second = - static_cast<double>(current_system_time_us - reset_point_system_us) / interval; + const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; Results results{}; results.system_fps = static_cast<double>(system_frames) / interval; results.game_fps = static_cast<double>(game_frames) / interval; results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / static_cast<double>(system_frames); - results.emulation_speed = system_us_per_second / 1'000'000.0; + results.emulation_speed = system_us_per_second.count() / 1'000'000.0; // Reset counters reset_point = now; @@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() { return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; } -void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { +void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher // values increase the time needed to recover and limit framerate again after spikes. - constexpr microseconds MAX_LAG_TIME_US = 25ms; + constexpr microseconds MAX_LAG_TIME_US = 25us; if (!Settings::values.toggle_framelimit) { return; @@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { auto now = Clock::now(); - frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); + frame_limiting_delta_err += current_system_time_us - previous_system_time_us; frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); frame_limiting_delta_err = std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 362b205c86..6e46197013 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -33,7 +33,7 @@ public: void EndSystemFrame(); void EndGameFrame(); - Results GetAndResetStats(u64 current_system_time_us); + Results GetAndResetStats(std::chrono::microseconds current_system_time_us); /** * Gets the ratio between walltime and the emulated time of the previous system frame. This is @@ -47,7 +47,7 @@ private: /// Point when the cumulative counters were reset Clock::time_point reset_point = Clock::now(); /// System time when the cumulative counters were reset - u64 reset_point_system_us = 0; + std::chrono::microseconds reset_point_system_us{0}; /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset Clock::duration accumulated_frametime = Clock::duration::zero(); @@ -68,11 +68,11 @@ class FrameLimiter { public: using Clock = std::chrono::high_resolution_clock; - void DoFrameLimiting(u64 current_system_time_us); + void DoFrameLimiting(std::chrono::microseconds current_system_time_us); private: /// Emulated system time (in microseconds) at the last limiter invocation - u64 previous_system_time_us = 0; + std::chrono::microseconds previous_system_time_us{0}; /// Walltime at the last limiter invocation Clock::time_point previous_walltime = Clock::now(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 444bcc387d..a4623223d1 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -2,13 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/hid/hid.h" #include "core/settings.h" +#include "video_core/renderer_base.h" #include "video_core/video_core.h" -#include "core/frontend/emu_window.h" - namespace Settings { Values values = {}; @@ -20,9 +20,9 @@ void Apply() { VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; - if (VideoCore::g_emu_window) { - auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); - VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); + auto& system_instance = Core::System::GetInstance(); + if (system_instance.IsPoweredOn()) { + system_instance.Renderer().UpdateCurrentFramebufferLayout(); } Service::HID::ReloadInputDevices(); diff --git a/src/core/settings.h b/src/core/settings.h index 7150d97555..73dc3061fe 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -112,6 +112,8 @@ static const std::array<const char*, NumAnalogs> mapping = {{ struct Values { // System bool use_docked_mode; + std::string username; + int language_index; // Controls std::array<std::string, NativeButton::NumButtons> buttons; @@ -137,6 +139,13 @@ struct Values { std::string log_filter; + bool use_dev_keys; + + // Audio + std::string sink_id; + std::string audio_device_id; + float volume; + // Debugging bool use_gdbstub; u16 gdbstub_port; diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index 0f0d10f237..525fe6abcf 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp @@ -5,6 +5,7 @@ #include <atomic> #include <list> #include <mutex> +#include <utility> #include "input_common/keyboard.h" namespace InputCommon { @@ -12,9 +13,9 @@ namespace InputCommon { class KeyButton final : public Input::ButtonDevice { public: explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) - : key_button_list(key_button_list_) {} + : key_button_list(std::move(key_button_list_)) {} - ~KeyButton(); + ~KeyButton() override; bool GetStatus() const override { return status.load(); diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index caffe48cbd..9570c060eb 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp @@ -131,7 +131,7 @@ public: device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); } - std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const { + std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const override { return device->GetStatus(); } diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp index 8d117c2d4f..d1b960fd77 100644 --- a/src/input_common/sdl/sdl.cpp +++ b/src/input_common/sdl/sdl.cpp @@ -82,7 +82,7 @@ private: class SDLButton final : public Input::ButtonDevice { public: explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) - : joystick(joystick_), button(button_) {} + : joystick(std::move(joystick_)), button(button_) {} bool GetStatus() const override { return joystick->GetButton(button); @@ -96,7 +96,7 @@ private: class SDLDirectionButton final : public Input::ButtonDevice { public: explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) - : joystick(joystick_), hat(hat_), direction(direction_) {} + : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {} bool GetStatus() const override { return joystick->GetHatDirection(hat, direction); @@ -112,7 +112,7 @@ class SDLAxisButton final : public Input::ButtonDevice { public: explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_, bool trigger_if_greater_) - : joystick(joystick_), axis(axis_), threshold(threshold_), + : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_) {} bool GetStatus() const override { @@ -132,7 +132,7 @@ private: class SDLAnalog final : public Input::AnalogDevice { public: SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_) - : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {} + : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_) {} std::tuple<float, float> GetStatus() const override { return joystick->GetAnalog(axis_x, axis_y); @@ -314,10 +314,6 @@ namespace Polling { class SDLPoller : public InputCommon::Polling::DevicePoller { public: - SDLPoller() = default; - - ~SDLPoller() = default; - void Start() override { // SDL joysticks must be opened, otherwise they don't generate events SDL_JoystickUpdate(); @@ -341,10 +337,6 @@ private: class SDLButtonPoller final : public SDLPoller { public: - SDLButtonPoller() = default; - - ~SDLButtonPoller() = default; - Common::ParamPackage GetNextInput() override { SDL_Event event; while (SDL_PollEvent(&event)) { @@ -364,10 +356,6 @@ public: class SDLAnalogPoller final : public SDLPoller { public: - SDLAnalogPoller() = default; - - ~SDLAnalogPoller() = default; - void Start() override { SDLPoller::Start(); diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 6a0a62ecc8..4d74bb3955 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -3,7 +3,6 @@ add_executable(tests core/arm/arm_test_common.cpp core/arm/arm_test_common.h core/core_timing.cpp - core/memory/memory.cpp glad.cpp tests.cpp ) diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp deleted file mode 100644 index 165496a548..0000000000 --- a/src/tests/core/memory/memory.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <catch.hpp> -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/process.h" -#include "core/memory.h" - -TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory][!hide]") { - SECTION("these regions should not be mapped on an empty process") { - auto process = Kernel::Process::Create(""); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == false); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::TLS_AREA_VADDR) == false); - } - - SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") { - auto process = Kernel::Process::Create(""); - Kernel::MapSharedPages(process->vm_manager); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); - } - - SECTION("special regions should be valid after mapping them") { - auto process = Kernel::Process::Create(""); - SECTION("VRAM") { - Kernel::HandleSpecialMapping(process->vm_manager, - {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false}); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == true); - } - - SECTION("IO (Not yet implemented)") { - Kernel::HandleSpecialMapping( - process->vm_manager, {Memory::IO_AREA_VADDR, Memory::IO_AREA_SIZE, false, false}); - CHECK_FALSE(Memory::IsValidVirtualAddress(*process, Memory::IO_AREA_VADDR) == true); - } - - SECTION("DSP") { - Kernel::HandleSpecialMapping( - process->vm_manager, {Memory::DSP_RAM_VADDR, Memory::DSP_RAM_SIZE, false, false}); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::DSP_RAM_VADDR) == true); - } - } - - SECTION("Unmapping a VAddr should make it invalid") { - auto process = Kernel::Process::Create(""); - Kernel::MapSharedPages(process->vm_manager); - process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE); - CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); - } -} diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 0e205ed72e..5c0ae8009e 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -19,8 +19,8 @@ namespace Engines { /// First register id that is actually a Macro call. constexpr u32 MacroRegistersStart = 0xE00; -Maxwell3D::Maxwell3D(MemoryManager& memory_manager) - : memory_manager(memory_manager), macro_interpreter(*this) {} +Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) + : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { auto macro_code = uploaded_macros.find(method); @@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { break; } - VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method); + rasterizer.NotifyMaxwellRegisterChanged(method); if (debug_context) { debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); @@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() { } const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; - VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed); + rasterizer.AccelerateDrawBatch(is_indexed); // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - @@ -285,8 +285,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { // TODO(Subv): Different data types for separate components are not supported ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); - // TODO(Subv): Only UNORM formats are supported for now. - ASSERT(r_type == Texture::ComponentType::UNORM); return tic_entry; } @@ -393,7 +391,7 @@ void Maxwell3D::ProcessClearBuffers() { regs.clear_buffers.R == regs.clear_buffers.B && regs.clear_buffers.R == regs.clear_buffers.A); - VideoCore::g_renderer->Rasterizer()->Clear(); + rasterizer.Clear(); } } // namespace Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 3c32f10671..4d0ff96a57 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -17,6 +17,10 @@ #include "video_core/memory_manager.h" #include "video_core/textures/texture.h" +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra::Engines { #define MAXWELL3D_REG_INDEX(field_name) \ @@ -24,7 +28,7 @@ namespace Tegra::Engines { class Maxwell3D final { public: - explicit Maxwell3D(MemoryManager& memory_manager); + explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); ~Maxwell3D() = default; /// Register structure of the Maxwell3D engine. @@ -818,6 +822,8 @@ public: Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; private: + VideoCore::RasterizerInterface& rasterizer; + std::unordered_map<u32, std::vector<u32>> uploaded_macros; /// Macro method that is currently being executed / being fed parameters. diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 60c49d6723..b2a83ce0b0 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -7,12 +7,13 @@ #include "video_core/engines/maxwell_compute.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/gpu.h" +#include "video_core/rasterizer_interface.h" namespace Tegra { -GPU::GPU() { +GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { memory_manager = std::make_unique<MemoryManager>(); - maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager); + maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); @@ -40,6 +41,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { case RenderTargetFormat::RGBA8_UNORM: case RenderTargetFormat::RGB10_A2_UNORM: case RenderTargetFormat::BGRA8_UNORM: + case RenderTargetFormat::R32_FLOAT: return 4; default: UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index c464fc6d15..440505c9d1 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -11,6 +11,10 @@ #include "core/hle/service/nvflinger/buffer_queue.h" #include "video_core/memory_manager.h" +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra { enum class RenderTargetFormat : u32 { @@ -29,6 +33,7 @@ enum class RenderTargetFormat : u32 { RG16_UINT = 0xDD, RG16_FLOAT = 0xDE, R11G11B10_FLOAT = 0xE0, + R32_FLOAT = 0xE5, R16_FLOAT = 0xF2, R8_UNORM = 0xF3, }; @@ -97,7 +102,7 @@ enum class EngineID { class GPU final { public: - GPU(); + explicit GPU(VideoCore::RasterizerInterface& rasterizer); ~GPU(); /// Processes a command list stored at the specified address in GPU memory. diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 44ece01c14..377bd66ab0 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -102,11 +102,11 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) { if (taken) { // Ignore the delay slot if the branch has the annul bit. if (opcode.branch_annul) { - pc = base_address + (opcode.immediate << 2); + pc = base_address + opcode.GetBranchTarget(); return true; } - delayed_pc = base_address + (opcode.immediate << 2); + delayed_pc = base_address + opcode.GetBranchTarget(); // Execute one more instruction due to the delay slot. return Step(code, true); } diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index a71e359d89..7d836b8160 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h @@ -91,6 +91,10 @@ private: u32 GetBitfieldMask() const { return (1 << bf_size) - 1; } + + s32 GetBranchTarget() const { + return static_cast<s32>(immediate * sizeof(u32)); + } }; union MethodAddress { diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 30075b23c1..3ca3502433 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -2,14 +2,26 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <atomic> #include <memory> +#include "core/frontend/emu_window.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_rasterizer.h" -#include "video_core/video_core.h" + +namespace VideoCore { + +RendererBase::RendererBase(EmuWindow& window) : render_window{window} {} +RendererBase::~RendererBase() = default; + +void RendererBase::UpdateCurrentFramebufferLayout() { + const Layout::FramebufferLayout& layout = render_window.GetFramebufferLayout(); + + render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); +} void RendererBase::RefreshRasterizerSetting() { if (rasterizer == nullptr) { - rasterizer = std::make_unique<RasterizerOpenGL>(); + rasterizer = std::make_unique<RasterizerOpenGL>(render_window); } } + +} // namespace VideoCore diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 89a960eaf8..235de23a1b 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -13,28 +13,28 @@ class EmuWindow; +namespace VideoCore { + class RendererBase : NonCopyable { public: /// Used to reference a framebuffer enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture }; - virtual ~RendererBase() {} + explicit RendererBase(EmuWindow& window); + virtual ~RendererBase(); /// Swap buffers (render frame) virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0; - /** - * Set the emulator window to use for renderer - * @param window EmuWindow handle to emulator window to use for rendering - */ - virtual void SetWindow(EmuWindow* window) = 0; - /// Initialize the renderer virtual bool Init() = 0; /// Shutdown the renderer virtual void ShutDown() = 0; + /// Updates the framebuffer layout of the contained render window handle. + void UpdateCurrentFramebufferLayout(); + // Getter/setter functions: // ------------------------ @@ -46,16 +46,21 @@ public: return m_current_frame; } - VideoCore::RasterizerInterface* Rasterizer() const { - return rasterizer.get(); + RasterizerInterface& Rasterizer() { + return *rasterizer; + } + + const RasterizerInterface& Rasterizer() const { + return *rasterizer; } void RefreshRasterizerSetting(); protected: - std::unique_ptr<VideoCore::RasterizerInterface> rasterizer; + EmuWindow& render_window; ///< Reference to the render window handle. + std::unique_ptr<RasterizerInterface> rasterizer; f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer int m_current_frame = 0; ///< Current frame, should be set by the renderer - -private: }; + +} // namespace VideoCore diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a1c47bae99..c2a9314695 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -14,7 +14,6 @@ #include "common/logging/log.h" #include "common/math_util.h" #include "common/microprofile.h" -#include "common/scope_exit.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/hle/kernel/process.h" @@ -37,7 +36,7 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); -RasterizerOpenGL::RasterizerOpenGL() { +RasterizerOpenGL::RasterizerOpenGL(EmuWindow& window) : emu_window{window} { // Create sampler objects for (size_t i = 0; i < texture_samplers.size(); ++i) { texture_samplers[i].Create(); @@ -170,8 +169,14 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, ASSERT(buffer.IsEnabled()); glEnableVertexAttribArray(index); - glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), - attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); + if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt || + attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) { + glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), + attrib.offset); + } else { + glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), + attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); + } glVertexAttribBinding(index, attrib.buffer); } @@ -395,7 +400,7 @@ void RasterizerOpenGL::Clear() { if (clear_mask == 0) return; - ScopeAcquireGLContext acquire_context; + ScopeAcquireGLContext acquire_context{emu_window}; auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers(use_color_fb, use_depth_fb); @@ -425,7 +430,7 @@ void RasterizerOpenGL::DrawArrays() { MICROPROFILE_SCOPE(OpenGL_Drawing); const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - ScopeAcquireGLContext acquire_context; + ScopeAcquireGLContext acquire_context{emu_window}; auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index e150be58f6..6d6d85cc10 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -21,11 +21,12 @@ #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" +class EmuWindow; struct ScreenInfo; class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: - RasterizerOpenGL(); + explicit RasterizerOpenGL(EmuWindow& renderer); ~RasterizerOpenGL() override; void DrawArrays() override; @@ -144,6 +145,8 @@ private: RasterizerCacheOpenGL res_cache; + EmuWindow& emu_window; + std::unique_ptr<GLShader::ProgramManager> shader_program_manager; OGLVertexArray sw_vao; OGLVertexArray hw_vao; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index a4d9707cb2..257aa95714 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -46,6 +46,8 @@ struct FormatTuple { params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); params.unaligned_height = config.tic.Height(); params.size_in_bytes = params.SizeInBytes(); + params.cache_width = Common::AlignUp(params.width, 16); + params.cache_height = Common::AlignUp(params.height, 16); return params; } @@ -63,6 +65,8 @@ struct FormatTuple { params.height = config.height; params.unaligned_height = config.height; params.size_in_bytes = params.SizeInBytes(); + params.cache_width = Common::AlignUp(params.width, 16); + params.cache_height = Common::AlignUp(params.height, 16); return params; } @@ -82,6 +86,8 @@ struct FormatTuple { params.height = zeta_height; params.unaligned_height = zeta_height; params.size_in_bytes = params.SizeInBytes(); + params.cache_width = Common::AlignUp(params.width, 16); + params.cache_height = Common::AlignUp(params.height, 16); return params; } @@ -118,6 +124,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RG16UI {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S + {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 // DepthStencil formats @@ -218,9 +225,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::RG16>, MortonCopy<true, PixelFormat::RG16F>, MortonCopy<true, PixelFormat::RG16UI>, MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>, - MortonCopy<true, PixelFormat::SRGBA8>, MortonCopy<true, PixelFormat::Z24S8>, - MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>, - MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z32FS8>, + MortonCopy<true, PixelFormat::RGB32F>, MortonCopy<true, PixelFormat::SRGBA8>, + MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, + MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>, + MortonCopy<true, PixelFormat::Z32FS8>, }; static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), @@ -253,6 +261,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), MortonCopy<false, PixelFormat::RG16UI>, MortonCopy<false, PixelFormat::RG16I>, MortonCopy<false, PixelFormat::RG16S>, + MortonCopy<false, PixelFormat::RGB32F>, MortonCopy<false, PixelFormat::SRGBA8>, MortonCopy<false, PixelFormat::Z24S8>, MortonCopy<false, PixelFormat::S8Z24>, @@ -677,12 +686,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) { // If use_accurate_framebuffers is enabled, always load from memory FlushSurface(surface); UnregisterSurface(surface); - } else if (surface->GetSurfaceParams() != params) { - // If surface parameters changed, recreate the surface from the old one - return RecreateSurface(surface, params); - } else { + } else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) { // Use the cached surface as-is return surface; + } else { + // If surface parameters changed, recreate the surface from the old one + return RecreateSurface(surface, params); } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index bf0458b941..0c6652c7a9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -9,6 +9,7 @@ #include <memory> #include <vector> #include <boost/icl/interval_map.hpp> + #include "common/common_types.h" #include "common/math_util.h" #include "video_core/engines/maxwell_3d.h" @@ -48,16 +49,17 @@ struct SurfaceParams { RG16UI = 23, RG16I = 24, RG16S = 25, - SRGBA8 = 26, + RGB32F = 26, + SRGBA8 = 27, MaxColorFormat, // DepthStencil formats - Z24S8 = 27, - S8Z24 = 28, - Z32F = 29, - Z16 = 30, - Z32FS8 = 31, + Z24S8 = 28, + S8Z24 = 29, + Z32F = 30, + Z16 = 31, + Z32FS8 = 32, MaxDepthStencilFormat, @@ -121,6 +123,7 @@ struct SurfaceParams { 1, // RG16UI 1, // RG16I 1, // RG16S + 1, // RGB32F 1, // SRGBA8 1, // Z24S8 1, // S8Z24 @@ -164,6 +167,7 @@ struct SurfaceParams { 32, // RG16UI 32, // RG16I 32, // RG16S + 96, // RGB32F 32, // SRGBA8 32, // Z24S8 32, // S8Z24 @@ -200,8 +204,9 @@ struct SurfaceParams { static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { switch (format) { + // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the + // gamma. case Tegra::RenderTargetFormat::RGBA8_SRGB: - return PixelFormat::SRGBA8; case Tegra::RenderTargetFormat::RGBA8_UNORM: return PixelFormat::ABGR8; case Tegra::RenderTargetFormat::BGRA8_UNORM: @@ -232,6 +237,8 @@ struct SurfaceParams { return PixelFormat::RG16S; case Tegra::RenderTargetFormat::R16_FLOAT: return PixelFormat::R16F; + case Tegra::RenderTargetFormat::R32_FLOAT: + return PixelFormat::R32F; default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); @@ -270,6 +277,8 @@ struct SurfaceParams { UNREACHABLE(); case Tegra::Texture::TextureFormat::R32_G32: return PixelFormat::RG32F; + case Tegra::Texture::TextureFormat::R32_G32_B32: + return PixelFormat::RGB32F; case Tegra::Texture::TextureFormat::R16: switch (component_type) { case Tegra::Texture::ComponentType::FLOAT: @@ -361,6 +370,8 @@ struct SurfaceParams { return Tegra::Texture::TextureFormat::A8R8G8B8; case PixelFormat::RGBA32F: return Tegra::Texture::TextureFormat::R32_G32_B32_A32; + case PixelFormat::RGB32F: + return Tegra::Texture::TextureFormat::R32_G32_B32; case PixelFormat::RG32F: return Tegra::Texture::TextureFormat::R32_G32; case PixelFormat::R32F: @@ -439,6 +450,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RG32_FLOAT: case Tegra::RenderTargetFormat::RG16_FLOAT: case Tegra::RenderTargetFormat::R16_FLOAT: + case Tegra::RenderTargetFormat::R32_FLOAT: return ComponentType::Float; case Tegra::RenderTargetFormat::RGBA32_UINT: case Tegra::RenderTargetFormat::RG16_UINT: @@ -536,6 +548,12 @@ struct SurfaceParams { return !operator==(other); } + /// Checks if surfaces are compatible for caching + bool IsCompatibleSurface(const SurfaceParams& other) const { + return std::tie(pixel_format, type, cache_width, cache_height) == + std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height); + } + Tegra::GPUVAddr addr; bool is_tiled; u32 block_height; @@ -546,6 +564,10 @@ struct SurfaceParams { u32 height; u32 unaligned_height; size_t size_in_bytes; + + // Parameters used for caching only + u32 cache_width; + u32 cache_height; }; class CachedSurface final { diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index acf067050a..e3217db819 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -412,7 +412,6 @@ public: } declarations.AddNewLine(); - unsigned const_buffer_layout = 0; for (const auto& entry : GetConstBuffersDeclarations()) { declarations.AddLine("layout(std140) uniform " + entry.GetName()); declarations.AddLine('{'); @@ -420,7 +419,6 @@ public: "[MAX_CONSTBUFFER_ELEMENTS];"); declarations.AddLine("};"); declarations.AddNewLine(); - ++const_buffer_layout; } declarations.AddNewLine(); @@ -768,13 +766,16 @@ private: // goes into gpr28+0 and gpr28+1 size_t texs_offset{}; + size_t src_elem{}; for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) { + size_t dest_elem{}; for (unsigned elem = 0; elem < 2; ++elem) { - if (!instr.texs.IsComponentEnabled(elem)) { + if (!instr.texs.IsComponentEnabled(src_elem++)) { // Skip disabled components continue; } - regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem); + regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, + dest_elem++); } if (!instr.texs.HasTwoDestinations()) { diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index e81fcbbc49..415d42fdac 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -13,15 +13,16 @@ namespace Impl { static void SetShaderUniformBlockBinding(GLuint shader, const char* name, Maxwell3D::Regs::ShaderStage binding, size_t expected_size) { - GLuint ub_index = glGetUniformBlockIndex(shader, name); - if (ub_index != GL_INVALID_INDEX) { - GLint ub_size = 0; - glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); - ASSERT_MSG(ub_size == expected_size, - "Uniform block size did not match! Got {}, expected {}", - static_cast<int>(ub_size), expected_size); - glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); + const GLuint ub_index = glGetUniformBlockIndex(shader, name); + if (ub_index == GL_INVALID_INDEX) { + return; } + + GLint ub_size = 0; + glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); + ASSERT_MSG(static_cast<size_t>(ub_size) == expected_size, + "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size); + glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); } void SetShaderUniformBlockBindings(GLuint shader) { diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index e29d551e16..716933a0b5 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -105,20 +105,20 @@ public: } ShaderEntries UseProgrammableVertexShader(const MaxwellVSConfig& config, - const ShaderSetup setup) { + const ShaderSetup& setup) { ShaderEntries result; std::tie(current.vs, result) = vertex_shaders.Get(config, setup); return result; } ShaderEntries UseProgrammableFragmentShader(const MaxwellFSConfig& config, - const ShaderSetup setup) { + const ShaderSetup& setup) { ShaderEntries result; std::tie(current.fs, result) = fragment_shaders.Get(config, setup); return result; } - GLuint GetCurrentProgramStage(Maxwell3D::Regs::ShaderStage stage) { + GLuint GetCurrentProgramStage(Maxwell3D::Regs::ShaderStage stage) const { switch (stage) { case Maxwell3D::Regs::ShaderStage::Vertex: return current.vs; diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 3398d7c04e..24b1d956b3 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -82,7 +82,7 @@ public: GLenum logic_op; // GL_LOGIC_OP_MODE // 3 texture units - one for each that is used in PICA fragment shader emulation - struct { + struct TextureUnit { GLuint texture_2d; // GL_TEXTURE_BINDING_2D GLuint sampler; // GL_SAMPLER_BINDING struct { @@ -104,7 +104,8 @@ public: Unbind(); sampler = 0; } - } texture_units[32]; + }; + std::array<TextureUnit, 32> texture_units; struct { GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 7810b9147b..bf9131193f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -92,23 +92,23 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons return matrix; } -ScopeAcquireGLContext::ScopeAcquireGLContext() { +ScopeAcquireGLContext::ScopeAcquireGLContext(EmuWindow& emu_window_) : emu_window{emu_window_} { if (Settings::values.use_multi_core) { - VideoCore::g_emu_window->MakeCurrent(); + emu_window.MakeCurrent(); } } ScopeAcquireGLContext::~ScopeAcquireGLContext() { if (Settings::values.use_multi_core) { - VideoCore::g_emu_window->DoneCurrent(); + emu_window.DoneCurrent(); } } -RendererOpenGL::RendererOpenGL() = default; +RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {} RendererOpenGL::~RendererOpenGL() = default; /// Swap buffers (render frame) void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { - ScopeAcquireGLContext acquire_context; + ScopeAcquireGLContext acquire_context{render_window}; Core::System::GetInstance().perf_stats.EndSystemFrame(); @@ -130,10 +130,10 @@ void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig& // Load the framebuffer from memory, draw it to the screen, and swap buffers LoadFBToScreenInfo(*framebuffer, screen_info); DrawScreen(); - render_window->SwapBuffers(); + render_window.SwapBuffers(); } - render_window->PollEvents(); + render_window.PollEvents(); Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs()); Core::System::GetInstance().perf_stats.BeginSystemFrame(); @@ -160,8 +160,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf // only allows rows to have a memory alignement of 4. ASSERT(framebuffer.stride % 4 == 0); - if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride, - screen_info)) { + if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride, + screen_info)) { // Reset the screen info's display texture to its own permanent texture screen_info.display_texture = screen_info.texture.resource.handle; @@ -356,7 +356,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, * Draws the emulated screens to the emulator window. */ void RendererOpenGL::DrawScreen() { - const auto& layout = render_window->GetFramebufferLayout(); + const auto& layout = render_window.GetFramebufferLayout(); const auto& screen = layout.screen; glViewport(0, 0, layout.width, layout.height); @@ -380,14 +380,6 @@ void RendererOpenGL::DrawScreen() { /// Updates the framerate void RendererOpenGL::UpdateFramerate() {} -/** - * Set the emulator window to use for renderer - * @param window EmuWindow handle to emulator window to use for rendering - */ -void RendererOpenGL::SetWindow(EmuWindow* window) { - render_window = window; -} - static const char* GetSource(GLenum source) { #define RET(s) \ case GL_DEBUG_SOURCE_##s: \ @@ -445,7 +437,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum /// Initialize the renderer bool RendererOpenGL::Init() { - ScopeAcquireGLContext acquire_context; + ScopeAcquireGLContext acquire_context{render_window}; if (GLAD_GL_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 59d92a3dc8..428afa3b71 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -34,24 +34,21 @@ struct ScreenInfo { /// Helper class to acquire/release OpenGL context within a given scope class ScopeAcquireGLContext : NonCopyable { public: - ScopeAcquireGLContext(); + explicit ScopeAcquireGLContext(EmuWindow& window); ~ScopeAcquireGLContext(); + +private: + EmuWindow& emu_window; }; -class RendererOpenGL : public RendererBase { +class RendererOpenGL : public VideoCore::RendererBase { public: - RendererOpenGL(); + explicit RendererOpenGL(EmuWindow& window); ~RendererOpenGL() override; /// Swap buffers (render frame) void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override; - /** - * Set the emulator window to use for renderer - * @param window EmuWindow handle to emulator window to use for rendering - */ - void SetWindow(EmuWindow* window) override; - /// Initialize the renderer bool Init() override; @@ -72,8 +69,6 @@ private: void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, const TextureInfo& texture); - EmuWindow* render_window; ///< Handle to render window - OpenGLState state; // OpenGL object IDs diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index d794f8402a..65db84ad33 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -57,6 +57,8 @@ u32 BytesPerPixel(TextureFormat format) { case TextureFormat::BC7U: // In this case a 'pixel' actually refers to a 4x4 tile. return 16; + case TextureFormat::R32_G32_B32: + return 12; case TextureFormat::ASTC_2D_4X4: case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: @@ -131,6 +133,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, case TextureFormat::R16_G16: case TextureFormat::BF10GF11RF11: case TextureFormat::ASTC_2D_4X4: + case TextureFormat::R32_G32_B32: CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, unswizzled_data.data(), true, block_height); break; @@ -190,6 +193,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat case TextureFormat::R32: case TextureFormat::R16: case TextureFormat::R16_G16: + case TextureFormat::R32_G32_B32: // TODO(Subv): For the time being just forward the same data without any decoding. rgba_data = texture_data; break; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 289140f31a..5085ef96bd 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -3,40 +3,16 @@ // Refer to the license.txt file included. #include <memory> -#include "common/logging/log.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/video_core.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Video Core namespace - namespace VideoCore { -EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window -std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin - std::atomic<bool> g_toggle_framelimit_enabled; -/// Initialize the video core -bool Init(EmuWindow* emu_window) { - g_emu_window = emu_window; - g_renderer = std::make_unique<RendererOpenGL>(); - g_renderer->SetWindow(g_emu_window); - if (g_renderer->Init()) { - LOG_DEBUG(Render, "initialized OK"); - } else { - LOG_CRITICAL(Render, "initialization failed !"); - return false; - } - return true; -} - -/// Shutdown the video core -void Shutdown() { - g_renderer.reset(); - - LOG_DEBUG(Render, "shutdown OK"); +std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) { + return std::make_unique<RendererOpenGL>(emu_window); } } // namespace VideoCore diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 37da624369..7c01c0b8da 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -8,29 +8,23 @@ #include <memory> class EmuWindow; -class RendererBase; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Video Core namespace namespace VideoCore { -enum class Renderer { Software, OpenGL }; +class RendererBase; -extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin -extern EmuWindow* g_emu_window; ///< Emu window +enum class Renderer { Software, OpenGL }; // TODO: Wrap these in a user settings struct along with any other graphics settings (often set from // qt ui) extern std::atomic<bool> g_toggle_framelimit_enabled; -/// Start the video core -void Start(); - -/// Initialize the video core -bool Init(EmuWindow* emu_window); - -/// Shutdown the video core -void Shutdown(); +/** + * Creates a renderer instance. + * + * @note The returned renderer instance is simply allocated. Its Init() + * function still needs to be called to fully complete its setup. + */ +std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window); } // namespace VideoCore diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 7de919a8eb..4755568066 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -11,6 +11,8 @@ add_executable(yuzu bootmanager.h configuration/config.cpp configuration/config.h + configuration/configure_audio.cpp + configuration/configure_audio.h configuration/configure_debug.cpp configuration/configure_debug.h configuration/configure_dialog.cpp @@ -55,6 +57,7 @@ add_executable(yuzu set(UIS aboutdialog.ui configuration/configure.ui + configuration/configure_audio.ui configuration/configure_debug.ui configuration/configure_general.ui configuration/configure_graphics.ui diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index d6647eeeae..a81ad28889 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp @@ -10,8 +10,9 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200)); - ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( - Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); + ui->labelBuildInfo->setText( + ui->labelBuildInfo->text().arg(Common::g_build_name, Common::g_scm_branch, + Common::g_scm_desc, QString(Common::g_build_date).left(10))); } -AboutDialog::~AboutDialog() {} +AboutDialog::~AboutDialog() = default; diff --git a/src/yuzu/about_dialog.h b/src/yuzu/about_dialog.h index 2eb6e28f50..18e8c11a73 100644 --- a/src/yuzu/about_dialog.h +++ b/src/yuzu/about_dialog.h @@ -16,7 +16,7 @@ class AboutDialog : public QDialog { public: explicit AboutDialog(QWidget* parent); - ~AboutDialog(); + ~AboutDialog() override; private: std::unique_ptr<Ui::AboutDialog> ui; diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui index 2680480cc3..f122ba39db 100644 --- a/src/yuzu/aboutdialog.ui +++ b/src/yuzu/aboutdialog.ui @@ -70,7 +70,7 @@ </sizepolicy> </property> <property name="text"> - <string><html><head/><body><p>%1 | %2-%3</p></body></html></string> + <string><html><head/><body><p>%1 | %2-%3 (%4)</p></body></html></string> </property> </widget> </item> @@ -115,7 +115,7 @@ p, li { white-space: pre-wrap; } <item> <widget class="QLabel" name="labelLinks"> <property name="text"> - <string><html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#0000ff;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#0000ff;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/license.txt"><span style=" text-decoration: underline; color:#0000ff;">License</span></a></p></body></html></string> + <string><html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/license.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html></string> </property> <property name="openExternalLinks"> <bool>true</bool> diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 130bc613b2..d0f990c646 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -106,7 +106,7 @@ class GRenderWindow : public QWidget, public EmuWindow { public: GRenderWindow(QWidget* parent, EmuThread* emu_thread); - ~GRenderWindow(); + ~GRenderWindow() override; // EmuWindow implementation void SwapBuffers() override; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 98969fe10b..bf469ee738 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -92,16 +92,26 @@ void Config::ReadValues() { Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat(); qt_config->endGroup(); + qt_config->beginGroup("Audio"); + Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); + Settings::values.audio_device_id = + qt_config->value("output_device", "auto").toString().toStdString(); + Settings::values.volume = qt_config->value("volume", 1).toFloat(); + qt_config->endGroup(); + qt_config->beginGroup("Data Storage"); Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); qt_config->endGroup(); qt_config->beginGroup("System"); Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); + Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); + Settings::values.language_index = qt_config->value("language_index", 1).toInt(); qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); + Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("Debugging"); @@ -195,16 +205,25 @@ void Config::SaveValues() { qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); qt_config->endGroup(); + qt_config->beginGroup("Audio"); + qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); + qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); + qt_config->setValue("volume", Settings::values.volume); + qt_config->endGroup(); + qt_config->beginGroup("Data Storage"); qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); qt_config->endGroup(); qt_config->beginGroup("System"); qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); + qt_config->setValue("username", QString::fromStdString(Settings::values.username)); + qt_config->setValue("language_index", Settings::values.language_index); qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); + qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys); qt_config->endGroup(); qt_config->beginGroup("Debugging"); diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index c5303851c7..c8e0b88af9 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -34,11 +34,16 @@ <string>Input</string> </attribute> </widget> - <widget class="ConfigureGraphics" name="graphicsTab"> - <attribute name="title"> - <string>Graphics</string> - </attribute> - </widget> + <widget class="ConfigureGraphics" name="graphicsTab"> + <attribute name="title"> + <string>Graphics</string> + </attribute> + </widget> + <widget class="ConfigureAudio" name="audioTab"> + <attribute name="title"> + <string>Audio</string> + </attribute> + </widget> <widget class="ConfigureDebug" name="debugTab"> <attribute name="title"> <string>Debug</string> @@ -69,6 +74,12 @@ <container>1</container> </customwidget> <customwidget> + <class>ConfigureAudio</class> + <extends>QWidget</extends> + <header>configuration/configure_audio.h</header> + <container>1</container> + </customwidget> + <customwidget> <class>ConfigureDebug</class> <extends>QWidget</extends> <header>configuration/configure_debug.h</header> diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp new file mode 100644 index 0000000000..fbb813f6c6 --- /dev/null +++ b/src/yuzu/configuration/configure_audio.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "audio_core/sink.h" +#include "audio_core/sink_details.h" +#include "core/core.h" +#include "core/settings.h" +#include "ui_configure_audio.h" +#include "yuzu/configuration/configure_audio.h" + +ConfigureAudio::ConfigureAudio(QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) { + ui->setupUi(this); + + ui->output_sink_combo_box->clear(); + ui->output_sink_combo_box->addItem("auto"); + for (const auto& sink_detail : AudioCore::g_sink_details) { + ui->output_sink_combo_box->addItem(sink_detail.id); + } + + connect(ui->volume_slider, &QSlider::valueChanged, [this] { + ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition())); + }); + + this->setConfiguration(); + connect(ui->output_sink_combo_box, + static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + &ConfigureAudio::updateAudioDevices); + + ui->output_sink_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->audio_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); +} + +ConfigureAudio::~ConfigureAudio() = default; + +void ConfigureAudio::setConfiguration() { + int new_sink_index = 0; + for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { + if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) { + new_sink_index = index; + break; + } + } + ui->output_sink_combo_box->setCurrentIndex(new_sink_index); + + // The device list cannot be pre-populated (nor listed) until the output sink is known. + updateAudioDevices(new_sink_index); + + int new_device_index = -1; + for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { + if (ui->audio_device_combo_box->itemText(index).toStdString() == + Settings::values.audio_device_id) { + new_device_index = index; + break; + } + } + ui->audio_device_combo_box->setCurrentIndex(new_device_index); + + ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); + ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition())); +} + +void ConfigureAudio::applyConfiguration() { + Settings::values.sink_id = + ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) + .toStdString(); + Settings::values.audio_device_id = + ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) + .toStdString(); + Settings::values.volume = + static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); +} + +void ConfigureAudio::updateAudioDevices(int sink_index) { + ui->audio_device_combo_box->clear(); + ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); + + std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); + std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); + for (const auto& device : device_list) { + ui->audio_device_combo_box->addItem(device.c_str()); + } +} + +void ConfigureAudio::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h new file mode 100644 index 0000000000..4f0af41630 --- /dev/null +++ b/src/yuzu/configuration/configure_audio.h @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureAudio; +} + +class ConfigureAudio : public QWidget { + Q_OBJECT + +public: + explicit ConfigureAudio(QWidget* parent = nullptr); + ~ConfigureAudio(); + + void applyConfiguration(); + void retranslateUi(); + +public slots: + void updateAudioDevices(int sink_index); + +private: + void setConfiguration(); + + std::unique_ptr<Ui::ConfigureAudio> ui; +}; diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui new file mode 100644 index 0000000000..ef67890dc8 --- /dev/null +++ b/src/yuzu/configuration/configure_audio.ui @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureAudio</class> + <widget class="QWidget" name="ConfigureAudio"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>188</width> + <height>246</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Audio</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Output Engine:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="output_sink_combo_box"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Audio Device:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="audio_device_combo_box"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Volume:</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QSlider" name="volume_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="pageStep"> + <number>10</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="volume_indicator"> + <property name="minimumSize"> + <size> + <width>32</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>0 %</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>167</width> + <height>55</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 5e66239ff0..45d84f19a2 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co }); } -ConfigureDebug::~ConfigureDebug() {} +ConfigureDebug::~ConfigureDebug() = default; void ConfigureDebug::setConfiguration() { ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); @@ -44,5 +44,4 @@ void ConfigureDebug::applyConfiguration() { Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter); Log::SetGlobalFilter(filter); - Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 118e91cf1b..5ae7276bd2 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -23,13 +23,6 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="QLabel" name="label_1"> - <property name="text"> - <string>The GDB Stub only works correctly when the CPU JIT is off.</string> - </property> - </widget> - </item> - <item> <layout class="QHBoxLayout" name="horizontalLayout_1"> <item> <widget class="QCheckBox" name="toggle_gdbstub"> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 358f33005c..cc4b326aef 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -6,13 +6,16 @@ #include "ui_configure.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" +#include "yuzu/hotkeys.h" -ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { +ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry) + : QDialog(parent), ui(new Ui::ConfigureDialog) { ui->setupUi(this); + ui->generalTab->PopulateHotkeyList(registry); this->setConfiguration(); } -ConfigureDialog::~ConfigureDialog() {} +ConfigureDialog::~ConfigureDialog() = default; void ConfigureDialog::setConfiguration() {} @@ -21,6 +24,7 @@ void ConfigureDialog::applyConfiguration() { ui->systemTab->applyConfiguration(); ui->inputTab->applyConfiguration(); ui->graphicsTab->applyConfiguration(); + ui->audioTab->applyConfiguration(); ui->debugTab->applyConfiguration(); Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 21fa1f5019..bbbdacc29c 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -7,6 +7,8 @@ #include <memory> #include <QDialog> +class HotkeyRegistry; + namespace Ui { class ConfigureDialog; } @@ -15,7 +17,7 @@ class ConfigureDialog : public QDialog { Q_OBJECT public: - explicit ConfigureDialog(QWidget* parent); + explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); ~ConfigureDialog(); void applyConfiguration(); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index baa5586673..d8caee1e8e 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); } -ConfigureGeneral::~ConfigureGeneral() {} +ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::setConfiguration() { ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); @@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() { ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); } +void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { + ui->widget->Populate(registry); +} + void ConfigureGeneral::applyConfiguration() { UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); @@ -44,5 +48,4 @@ void ConfigureGeneral::applyConfiguration() { Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); Settings::values.use_multi_core = ui->use_multi_core->isChecked(); Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); - Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 447552d8c6..4770034cc0 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -7,6 +7,8 @@ #include <memory> #include <QWidget> +class HotkeyRegistry; + namespace Ui { class ConfigureGeneral; } @@ -18,11 +20,11 @@ public: explicit ConfigureGeneral(QWidget* parent = nullptr); ~ConfigureGeneral(); + void PopulateHotkeyList(const HotkeyRegistry& registry); void applyConfiguration(); private: void setConfiguration(); -private: std::unique_ptr<Ui::ConfigureGeneral> ui; }; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 7664880d53..4afe0f81b6 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) this->setConfiguration(); } -ConfigureGraphics::~ConfigureGraphics() {} +ConfigureGraphics::~ConfigureGraphics() = default; enum class Resolution : int { Auto, @@ -67,5 +67,4 @@ void ConfigureGraphics::applyConfiguration() { ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); - Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 78559e2bbe..5e7badedf4 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -191,8 +191,6 @@ void ConfigureInput::applyConfiguration() { [](const Common::ParamPackage& param) { return param.Serialize(); }); std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); - - Settings::Apply(); } void ConfigureInput::loadConfiguration() { diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index d09505a0f7..e9ed9c38f7 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -4,9 +4,10 @@ #include <QMessageBox> #include "core/core.h" +#include "core/settings.h" #include "ui_configure_system.h" #include "yuzu/configuration/configure_system.h" -#include "yuzu/ui_settings.h" +#include "yuzu/main.h" static const std::array<int, 12> days_in_month = {{ 31, @@ -34,10 +35,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: this->setConfiguration(); } -ConfigureSystem::~ConfigureSystem() {} +ConfigureSystem::~ConfigureSystem() = default; void ConfigureSystem::setConfiguration() { enabled = !Core::System::GetInstance().IsPoweredOn(); + ui->edit_username->setText(QString::fromStdString(Settings::values.username)); + ui->combo_language->setCurrentIndex(Settings::values.language_index); } void ConfigureSystem::ReadSystemSettings() {} @@ -45,6 +48,9 @@ void ConfigureSystem::ReadSystemSettings() {} void ConfigureSystem::applyConfiguration() { if (!enabled) return; + Settings::values.username = ui->edit_username->text().toStdString(); + Settings::values.language_index = ui->combo_language->currentIndex(); + Settings::Apply(); } void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 8caf496239..f3f8db038d 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -38,7 +38,7 @@ </sizepolicy> </property> <property name="maxLength"> - <number>10</number> + <number>32</number> </property> </widget> </item> @@ -164,7 +164,7 @@ </item> <item> <property name="text"> - <string>Simplified Chinese (简体中文)</string> + <string>Chinese</string> </property> </item> <item> @@ -187,6 +187,31 @@ <string>Russian (Русский)</string> </property> </item> + <item> + <property name="text"> + <string>Taiwanese</string> + </property> + </item> + <item> + <property name="text"> + <string>British English</string> + </property> + </item> + <item> + <property name="text"> + <string>Canadian French</string> + </property> + </item> + <item> + <property name="text"> + <string>Latin American Spanish</string> + </property> + </item> + <item> + <property name="text"> + <string>Simplified Chinese</string> + </property> + </item> <item> <property name="text"> <string>Traditional Chinese (正體中文)</string> diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp index d6d61a7391..5f459ccfb8 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp @@ -10,12 +10,12 @@ BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Tegra::DebugConte : QDockWidget(title, parent), BreakPointObserver(debug_context) { qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event"); - connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); + connect(this, &BreakPointObserverDock::Resumed, this, &BreakPointObserverDock::OnResumed); // NOTE: This signal is emitted from a non-GUI thread, but connect() takes // care of delaying its handling to the GUI thread. - connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this, - SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); + connect(this, &BreakPointObserverDock::BreakPointHit, this, + &BreakPointObserverDock::OnBreakPointHit, Qt::BlockingQueuedConnection); } void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) { diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h index 9d05493cf9..ab32f0115a 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h @@ -23,11 +23,11 @@ public: void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override; void OnMaxwellResume() override; -private slots: - virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0; - virtual void OnResumed() = 0; - signals: void Resumed(); void BreakPointHit(Tegra::DebugContext::Event event, void* data); + +private: + virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0; + virtual void OnResumed() = 0; }; diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index f98cc8152c..eb16a38a09 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp @@ -144,21 +144,25 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event"); - connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this, - SLOT(OnItemDoubleClicked(const QModelIndex&))); + connect(breakpoint_list, &QTreeView::doubleClicked, this, + &GraphicsBreakPointsWidget::OnItemDoubleClicked); - connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); + connect(resume_button, &QPushButton::clicked, this, + &GraphicsBreakPointsWidget::OnResumeRequested); - connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this, - SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); - connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); + connect(this, &GraphicsBreakPointsWidget::BreakPointHit, this, + &GraphicsBreakPointsWidget::OnBreakPointHit, Qt::BlockingQueuedConnection); + connect(this, &GraphicsBreakPointsWidget::Resumed, this, &GraphicsBreakPointsWidget::OnResumed); - connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), breakpoint_model, - SLOT(OnBreakPointHit(Tegra::DebugContext::Event)), Qt::BlockingQueuedConnection); - connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); + connect(this, &GraphicsBreakPointsWidget::BreakPointHit, breakpoint_model, + &BreakPointModel::OnBreakPointHit, Qt::BlockingQueuedConnection); + connect(this, &GraphicsBreakPointsWidget::Resumed, breakpoint_model, + &BreakPointModel::OnResumed); - connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)), - breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&))); + connect(this, &GraphicsBreakPointsWidget::BreakPointsChanged, + [this](const QModelIndex& top_left, const QModelIndex& bottom_right) { + breakpoint_model->dataChanged(top_left, bottom_right); + }); QWidget* main_widget = new QWidget; auto main_layout = new QVBoxLayout; diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.h b/src/yuzu/debugger/graphics/graphics_breakpoints.h index ae0ede2e8c..a920a2ae59 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.h @@ -26,18 +26,17 @@ public: void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override; void OnMaxwellResume() override; -public slots: - void OnBreakPointHit(Tegra::DebugContext::Event event, void* data); - void OnItemDoubleClicked(const QModelIndex&); - void OnResumeRequested(); - void OnResumed(); - signals: void Resumed(); void BreakPointHit(Tegra::DebugContext::Event event, void* data); void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: + void OnBreakPointHit(Tegra::DebugContext::Event event, void* data); + void OnItemDoubleClicked(const QModelIndex&); + void OnResumeRequested(); + void OnResumed(); + QLabel* status_text; QPushButton* resume_button; diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h index 35a6876ae3..7112b87e65 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h @@ -25,7 +25,6 @@ public: bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; -public slots: void OnBreakPointHit(Tegra::DebugContext::Event event); void OnResumed(); diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index c41ff693b8..3f7103ab99 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -34,7 +34,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat( SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {} -SurfacePicture::~SurfacePicture() {} + +SurfacePicture::~SurfacePicture() = default; void SurfacePicture::mousePressEvent(QMouseEvent* event) { // Only do something while the left mouse button is held down @@ -153,22 +154,24 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); // Connections - connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); - connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, - SLOT(OnSurfaceSourceChanged(int))); - connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, - SLOT(OnSurfaceAddressChanged(qint64))); - connect(surface_width_control, SIGNAL(valueChanged(int)), this, - SLOT(OnSurfaceWidthChanged(int))); - connect(surface_height_control, SIGNAL(valueChanged(int)), this, - SLOT(OnSurfaceHeightChanged(int))); - connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, - SLOT(OnSurfaceFormatChanged(int))); - connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, - SLOT(OnSurfacePickerXChanged(int))); - connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, - SLOT(OnSurfacePickerYChanged(int))); - connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface())); + connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); + connect(surface_source_list, + static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + &GraphicsSurfaceWidget::OnSurfaceSourceChanged); + connect(surface_address_control, &CSpinBox::ValueChanged, this, + &GraphicsSurfaceWidget::OnSurfaceAddressChanged); + connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged); + connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged); + connect(surface_format_control, + static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + &GraphicsSurfaceWidget::OnSurfaceFormatChanged); + connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged); + connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged); + connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); auto main_widget = new QWidget; auto main_layout = new QVBoxLayout; diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h index 6a344bdfce..323e39d94e 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ b/src/yuzu/debugger/graphics/graphics_surface.h @@ -22,11 +22,11 @@ class SurfacePicture : public QLabel { public: explicit SurfacePicture(QWidget* parent = nullptr, GraphicsSurfaceWidget* surface_widget = nullptr); - ~SurfacePicture(); + ~SurfacePicture() override; protected slots: - virtual void mouseMoveEvent(QMouseEvent* event); - virtual void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; private: GraphicsSurfaceWidget* surface_widget; @@ -65,16 +65,15 @@ public slots: void OnSurfacePickerYChanged(int new_value); void OnUpdate(); -private slots: +signals: + void Update(); + +private: void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; void OnResumed() override; void SaveSurface(); -signals: - void Update(); - -private: QComboBox* surface_source_list; CSpinBox* surface_address_control; QSpinBox* surface_width_control; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index f5a5697a0a..d0926d723d 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -14,7 +14,7 @@ #include "core/hle/kernel/timer.h" #include "core/hle/kernel/wait_object.h" -WaitTreeItem::~WaitTreeItem() {} +WaitTreeItem::~WaitTreeItem() = default; QColor WaitTreeItem::GetColor() const { return QColor(Qt::GlobalColor::black); @@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const { list.push_back(std::make_unique<WaitTreeText>( tr("reset type = %1") - .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type)))); + .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType())))); return list; } diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 10fc9e9680..513b3c45d6 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -9,7 +9,7 @@ #include <QTreeView> #include <boost/container/flat_set.hpp> #include "core/core.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" class EmuThread; @@ -25,11 +25,13 @@ class WaitTreeThread; class WaitTreeItem : public QObject { Q_OBJECT public: + ~WaitTreeItem() override; + virtual bool IsExpandable() const; virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const; virtual QString GetText() const = 0; virtual QColor GetColor() const; - virtual ~WaitTreeItem(); + void Expand(); WaitTreeItem* Parent() const; const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 99e6634a15..24f38a3c72 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -162,15 +162,15 @@ void GameList::onTextChanged(const QString& newText) { } search_field->setFilterResult(rowCount, rowCount); } else { - QStandardItem* child_file; - QString file_path, file_name, file_title, file_programmid; int result_count = 0; for (int i = 0; i < rowCount; ++i) { - child_file = item_model->item(i, 0); - file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower(); - file_name = file_path.mid(file_path.lastIndexOf("/") + 1); - file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower(); - file_programmid = + const QStandardItem* child_file = item_model->item(i, 0); + const QString file_path = + child_file->data(GameListItemPath::FullPathRole).toString().toLower(); + QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1); + const QString file_title = + child_file->data(GameListItemPath::TitleRole).toString().toLower(); + const QString file_programmid = child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); // Only items which filename in combination with its title contains all words @@ -258,18 +258,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) { void GameList::ValidateEntry(const QModelIndex& item) { // We don't care about the individual QStandardItem that was selected, but its row. - int row = item_model->itemFromIndex(item)->row(); - QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); - QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); + const int row = item_model->itemFromIndex(item)->row(); + const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); + const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); if (file_path.isEmpty()) return; - std::string std_file_path(file_path.toStdString()); - if (!FileUtil::Exists(std_file_path)) + + if (!QFileInfo::exists(file_path)) return; - if (FileUtil::IsDirectory(std_file_path)) { - QDir dir(std_file_path.c_str()); - QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); + + const QFileInfo file_info{file_path}; + if (file_info.isDir()) { + const QDir dir{file_path}; + const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); if (matching_main.size() == 1) { emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); } @@ -365,24 +367,26 @@ void GameList::LoadInterfaceLayout() { item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); } -const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca"}; +const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"}; static bool HasSupportedFileExtension(const std::string& file_name) { - QFileInfo file = QFileInfo(file_name.c_str()); + const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); } static bool IsExtractedNCAMain(const std::string& file_name) { - return QFileInfo(file_name.c_str()).fileName() == "main"; + return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; } static QString FormatGameName(const std::string& physical_name) { - QFileInfo file_info(physical_name.c_str()); + const QString physical_name_as_qstring = QString::fromStdString(physical_name); + const QFileInfo file_info(physical_name_as_qstring); + if (IsExtractedNCAMain(physical_name)) { return file_info.dir().path(); - } else { - return QString::fromStdString(physical_name); } + + return physical_name_as_qstring; } void GameList::RefreshGameDirectory() { diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index a758b77aaf..aa69a098f0 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -5,6 +5,7 @@ #pragma once #include <atomic> +#include <utility> #include <QImage> #include <QRunnable> #include <QStandardItem> @@ -27,9 +28,8 @@ static QPixmap GetDefaultIcon(bool large) { class GameListItem : public QStandardItem { public: - GameListItem() : QStandardItem() {} - GameListItem(const QString& string) : QStandardItem(string) {} - virtual ~GameListItem() override {} + GameListItem() = default; + explicit GameListItem(const QString& string) : QStandardItem(string) {} }; /** @@ -45,9 +45,8 @@ public: static const int TitleRole = Qt::UserRole + 2; static const int ProgramIdRole = Qt::UserRole + 3; - GameListItemPath() : GameListItem() {} - GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) - : GameListItem() { + GameListItemPath() = default; + GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) { setData(game_path, FullPathRole); setData(qulonglong(program_id), ProgramIdRole); } @@ -75,8 +74,8 @@ class GameListItemSize : public GameListItem { public: static const int SizeRole = Qt::UserRole + 1; - GameListItemSize() : GameListItem() {} - GameListItemSize(const qulonglong size_bytes) : GameListItem() { + GameListItemSize() = default; + explicit GameListItemSize(const qulonglong size_bytes) { setData(size_bytes, SizeRole); } @@ -111,7 +110,7 @@ class GameListWorker : public QObject, public QRunnable { public: GameListWorker(QString dir_path, bool deep_scan) - : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} + : dir_path(std::move(dir_path)), deep_scan(deep_scan) {} public slots: /// Starts the processing of directory tree information. diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index 61acb38eea..dce3997746 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp @@ -10,58 +10,53 @@ #include "yuzu/hotkeys.h" #include "yuzu/ui_settings.h" -struct Hotkey { - Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} +HotkeyRegistry::HotkeyRegistry() = default; +HotkeyRegistry::~HotkeyRegistry() = default; - QKeySequence keyseq; - QShortcut* shortcut; - Qt::ShortcutContext context; -}; - -typedef std::map<QString, Hotkey> HotkeyMap; -typedef std::map<QString, HotkeyMap> HotkeyGroupMap; - -HotkeyGroupMap hotkey_groups; - -void SaveHotkeys() { - UISettings::values.shortcuts.clear(); - for (auto group : hotkey_groups) { - for (auto hotkey : group.second) { - UISettings::values.shortcuts.emplace_back( - UISettings::Shortcut(group.first + "/" + hotkey.first, - UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), - hotkey.second.context))); - } - } -} - -void LoadHotkeys() { +void HotkeyRegistry::LoadHotkeys() { // Make sure NOT to use a reference here because it would become invalid once we call // beginGroup() for (auto shortcut : UISettings::values.shortcuts) { - QStringList cat = shortcut.first.split("/"); + const QStringList cat = shortcut.first.split('/'); Q_ASSERT(cat.size() >= 2); // RegisterHotkey assigns default keybindings, so use old values as default parameters Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; if (!shortcut.second.first.isEmpty()) { hk.keyseq = QKeySequence::fromString(shortcut.second.first); - hk.context = (Qt::ShortcutContext)shortcut.second.second; + hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second); } if (hk.shortcut) hk.shortcut->setKey(hk.keyseq); } } -void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, - Qt::ShortcutContext default_context) { - if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) { - hotkey_groups[group][action].keyseq = default_keyseq; - hotkey_groups[group][action].context = default_context; +void HotkeyRegistry::SaveHotkeys() { + UISettings::values.shortcuts.clear(); + for (const auto& group : hotkey_groups) { + for (const auto& hotkey : group.second) { + UISettings::values.shortcuts.emplace_back( + UISettings::Shortcut(group.first + '/' + hotkey.first, + UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), + hotkey.second.context))); + } } } -QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) { +void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action, + const QKeySequence& default_keyseq, + Qt::ShortcutContext default_context) { + auto& hotkey_group = hotkey_groups[group]; + if (hotkey_group.find(action) != hotkey_group.end()) { + return; + } + + auto& hotkey_action = hotkey_groups[group][action]; + hotkey_action.keyseq = default_keyseq; + hotkey_action.context = default_context; +} + +QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { Hotkey& hk = hotkey_groups[group][action]; if (!hk.shortcut) @@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { ui.setupUi(this); +} - for (auto group : hotkey_groups) { +void GHotkeysDialog::Populate(const HotkeyRegistry& registry) { + for (const auto& group : registry.hotkey_groups) { QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); - for (auto hotkey : group.second) { + for (const auto& hotkey : group.second) { QStringList columns; columns << hotkey.first << hotkey.second.keyseq.toString(); QTreeWidgetItem* item = new QTreeWidgetItem(columns); diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index a4ccc193ba..f38e6c0028 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h @@ -4,6 +4,7 @@ #pragma once +#include <map> #include "ui_hotkeys.h" class QDialog; @@ -11,47 +12,69 @@ class QKeySequence; class QSettings; class QShortcut; -/** - * Register a hotkey. - * - * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") - * @param action Name of the action (e.g. "Start Emulation", "Load Image") - * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings - * file before - * @param default_context Default context to assign if the hotkey wasn't present in the settings - * file before - * @warning Both the group and action strings will be displayed in the hotkey settings dialog - */ -void RegisterHotkey(const QString& group, const QString& action, - const QKeySequence& default_keyseq = QKeySequence(), - Qt::ShortcutContext default_context = Qt::WindowShortcut); - -/** - * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. - * - * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). - * @param action Name of the action (e.g. "Start Emulation", "Load Image"). - * @param widget Parent widget of the returned QShortcut. - * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut - * will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. - */ -QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); - -/** - * Saves all registered hotkeys to the settings file. - * - * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a - * settings group will be created to store the key sequence and the hotkey context. - */ -void SaveHotkeys(); - -/** - * Loads hotkeys from the settings file. - * - * @note Yet unregistered hotkeys which are present in the settings will automatically be - * registered. - */ -void LoadHotkeys(); +class HotkeyRegistry final { +public: + friend class GHotkeysDialog; + + explicit HotkeyRegistry(); + ~HotkeyRegistry(); + + /** + * Loads hotkeys from the settings file. + * + * @note Yet unregistered hotkeys which are present in the settings will automatically be + * registered. + */ + void LoadHotkeys(); + + /** + * Saves all registered hotkeys to the settings file. + * + * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a + * settings group will be created to store the key sequence and the hotkey context. + */ + void SaveHotkeys(); + + /** + * Returns a QShortcut object whose activated() signal can be connected to other QObjects' + * slots. + * + * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). + * @param action Name of the action (e.g. "Start Emulation", "Load Image"). + * @param widget Parent widget of the returned QShortcut. + * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut + * will be the same. Thus, you shouldn't rely on the caller really being the + * QShortcut's parent. + */ + QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); + + /** + * Register a hotkey. + * + * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") + * @param action Name of the action (e.g. "Start Emulation", "Load Image") + * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the + * settings file before + * @param default_context Default context to assign if the hotkey wasn't present in the settings + * file before + * @warning Both the group and action strings will be displayed in the hotkey settings dialog + */ + void RegisterHotkey(const QString& group, const QString& action, + const QKeySequence& default_keyseq = {}, + Qt::ShortcutContext default_context = Qt::WindowShortcut); + +private: + struct Hotkey { + QKeySequence keyseq; + QShortcut* shortcut = nullptr; + Qt::ShortcutContext context = Qt::WindowShortcut; + }; + + using HotkeyMap = std::map<QString, Hotkey>; + using HotkeyGroupMap = std::map<QString, HotkeyMap>; + + HotkeyGroupMap hotkey_groups; +}; class GHotkeysDialog : public QWidget { Q_OBJECT @@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget { public: explicit GHotkeysDialog(QWidget* parent = nullptr); + void Populate(const HotkeyRegistry& registry); + private: Ui::hotkeys ui; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 97273f9673..17ed62c727 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -23,6 +23,7 @@ #include "common/scope_exit.h" #include "common/string_util.h" #include "core/core.h" +#include "core/crypto/key_manager.h" #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" #include "core/settings.h" @@ -80,6 +81,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) { void GMainWindow::ShowCallouts() {} +const int GMainWindow::max_recent_files_item; + GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { debug_context = Tegra::DebugContext::Construct(); @@ -205,27 +208,46 @@ void GMainWindow::InitializeRecentFileMenuActions() { } void GMainWindow::InitializeHotkeys() { - RegisterHotkey("Main Window", "Load File", QKeySequence::Open); - RegisterHotkey("Main Window", "Start Emulation"); - RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); - RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), - Qt::ApplicationShortcut); - LoadHotkeys(); - - connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this, - &GMainWindow::OnMenuLoadFile); - connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this, - &GMainWindow::OnStartGame); - connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated, - ui.action_Fullscreen, &QAction::trigger); - connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously, - ui.action_Fullscreen, &QAction::trigger); - connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] { - if (emulation_running) { - ui.action_Fullscreen->setChecked(false); - ToggleFullscreen(); - } - }); + hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open); + hotkey_registry.RegisterHotkey("Main Window", "Start Emulation"); + hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); + hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); + hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), + Qt::ApplicationShortcut); + hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"), + Qt::ApplicationShortcut); + hotkey_registry.LoadHotkeys(); + + connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, + this, &GMainWindow::OnMenuLoadFile); + connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this), + &QShortcut::activated, this, &GMainWindow::OnStartGame); + connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, + this, [&] { + if (emulation_running) { + if (emu_thread->IsRunning()) { + OnPauseGame(); + } else { + OnStartGame(); + } + } + }); + connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window), + &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger); + connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window), + &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger); + connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this), + &QShortcut::activated, this, [&] { + if (emulation_running) { + ui.action_Fullscreen->setChecked(false); + ToggleFullscreen(); + } + }); + connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this), + &QShortcut::activated, this, [&] { + Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit; + UpdateStatusBar(); + }); } void GMainWindow::SetDefaultUIGeometry() { @@ -304,7 +326,8 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); // Fullscreen - ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); + ui.action_Fullscreen->setShortcut( + hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key()); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); // Help @@ -386,7 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) { system.SetGPUDebugContext(debug_context); - const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())}; + const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; render_window->DoneCurrent(); @@ -408,18 +431,49 @@ bool GMainWindow::LoadROM(const QString& filename) { tr("Could not determine the system mode.")); break; - case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: { + case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: { + const auto reg_found = Core::Crypto::KeyManager::KeyFileExists(false); + const auto title_found = Core::Crypto::KeyManager::KeyFileExists(true); + + std::string file_text; + + if (!reg_found && !title_found) { + file_text = "A proper key file (prod.keys, dev.keys, or title.keys) could not be " + "found. You will need to dump your keys from your switch to continue."; + } else if (reg_found && title_found) { + file_text = + "Both key files were found in your config directory, but the correct key could" + "not be found. You may be missing a titlekey or general key, depending on " + "the game."; + } else if (reg_found) { + file_text = + "The regular keys file (prod.keys/dev.keys) was found in your config, but the " + "titlekeys file (title.keys) was not. You are either missing the correct " + "titlekey or missing a general key required to decrypt the game."; + } else { + file_text = "The title keys file (title.keys) was found in your config, but " + "the regular keys file (prod.keys/dev.keys) was not. Unfortunately, " + "having the titlekey is not enough, you need additional general keys " + "to properly decrypt the game. You should double-check to make sure " + "your keys are correct."; + } + QMessageBox::critical( this, tr("Error while loading ROM!"), - tr("The game that you are trying to load must be decrypted before being used with " - "yuzu. A real Switch is required.<br/><br/>" - "For more information on dumping and decrypting games, please see the following " - "wiki pages: <ul>" - "<li><a href='https://yuzu-emu.org/wiki/dumping-game-cartridges/'>Dumping Game " - "Cartridges</a></li>" - "<li><a href='https://yuzu-emu.org/wiki/dumping-installed-titles/'>Dumping " - "Installed Titles</a></li>" - "</ul>")); + tr(("The game you are trying to load is encrypted and the required keys to load " + "the game could not be found in your configuration. " + + file_text + " Please refer to the yuzu wiki for help.") + .c_str())); + break; + } + case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: { + QMessageBox::critical( + this, tr("Error while loading ROM!"), + tr("There was a general error while decrypting the game. This means that the keys " + "necessary were found, but were either incorrect, the game itself was not a " + "valid game or the game uses an unhandled cryptographic scheme. Please double " + "check that you have the correct " + "keys.")); break; } case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: @@ -429,7 +483,7 @@ bool GMainWindow::LoadROM(const QString& filename) { case Core::System::ResultStatus::ErrorVideoCore: QMessageBox::critical( - this, tr("An error occured in the video core."), + this, tr("An error occurred initializing the video core."), tr("yuzu has encountered an error while running the video core, please see the " "log for more details." "For more information on accessing the log, please see the following page: " @@ -443,7 +497,7 @@ bool GMainWindow::LoadROM(const QString& filename) { default: QMessageBox::critical( this, tr("Error while loading ROM!"), - tr("An unknown error occured. Please see the log for more details.")); + tr("An unknown error occurred. Please see the log for more details.")); break; } return false; @@ -531,11 +585,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) { } void GMainWindow::UpdateRecentFiles() { - unsigned int num_recent_files = - std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); + const int num_recent_files = + std::min(UISettings::values.recent_files.size(), max_recent_files_item); - for (unsigned int i = 0; i < num_recent_files; i++) { - QString text = QString("&%1. %2").arg(i + 1).arg( + for (int i = 0; i < num_recent_files; i++) { + const QString text = QString("&%1. %2").arg(i + 1).arg( QFileInfo(UISettings::values.recent_files[i]).fileName()); actions_recent_files[i]->setText(text); actions_recent_files[i]->setData(UISettings::values.recent_files[i]); @@ -547,12 +601,8 @@ void GMainWindow::UpdateRecentFiles() { actions_recent_files[j]->setVisible(false); } - // Grey out the recent files menu if the list is empty - if (num_recent_files == 0) { - ui.menu_recent_files->setEnabled(false); - } else { - ui.menu_recent_files->setEnabled(true); - } + // Enable the recent files menu if the list isn't empty + ui.menu_recent_files->setEnabled(num_recent_files != 0); } void GMainWindow::OnGameListLoadFile(QString game_path) { @@ -583,9 +633,15 @@ void GMainWindow::OnMenuLoadFile() { } void GMainWindow::OnMenuLoadFolder() { - QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory")); + const QString dir_path = + QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory")); + + if (dir_path.isNull()) { + return; + } - QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); + const QDir dir{dir_path}; + const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); if (matching_main.size() == 1) { BootGame(dir.path() + DIR_SEP + matching_main[0]); } else { @@ -606,9 +662,8 @@ void GMainWindow::OnMenuRecentFile() { QAction* action = qobject_cast<QAction*>(sender()); assert(action); - QString filename = action->data().toString(); - QFileInfo file_info(filename); - if (file_info.exists()) { + const QString filename = action->data().toString(); + if (QFileInfo::exists(filename)) { BootGame(filename); } else { // Display an error message and remove the file from the list. @@ -706,11 +761,13 @@ void GMainWindow::ToggleWindowMode() { } void GMainWindow::OnConfigure() { - ConfigureDialog configureDialog(this); + ConfigureDialog configureDialog(this, hotkey_registry); + auto old_theme = UISettings::values.theme; auto result = configureDialog.exec(); if (result == QDialog::Accepted) { configureDialog.applyConfiguration(); - UpdateUITheme(); + if (UISettings::values.theme != old_theme) + UpdateUITheme(); config->Save(); } } @@ -843,7 +900,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { UISettings::values.first_start = false; game_list->SaveInterfaceLayout(); - SaveHotkeys(); + hotkey_registry.SaveHotkeys(); // Shutdown session if the emu thread is active... if (emu_thread != nullptr) @@ -897,15 +954,14 @@ void GMainWindow::UpdateUITheme() { QStringList theme_paths(default_theme_paths); if (UISettings::values.theme != UISettings::themes[0].second && !UISettings::values.theme.isEmpty()) { - QString theme_uri(":" + UISettings::values.theme + "/style.qss"); + const QString theme_uri(":" + UISettings::values.theme + "/style.qss"); QFile f(theme_uri); - if (!f.exists()) { - LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); - } else { - f.open(QFile::ReadOnly | QFile::Text); + if (f.open(QFile::ReadOnly | QFile::Text)) { QTextStream ts(&f); qApp->setStyleSheet(ts.readAll()); GMainWindow::setStyleSheet(ts.readAll()); + } else { + LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); } theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme}); QIcon::setThemeName(":/icons/" + UISettings::values.theme); @@ -941,7 +997,6 @@ int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName("yuzu team"); QCoreApplication::setApplicationName("yuzu"); - QApplication::setAttribute(Qt::AA_X11InitThreads); QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QApplication app(argc, argv); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 074bba3f9c..6e335b8f80 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -9,6 +9,7 @@ #include <QTimer> #include "core/core.h" #include "ui_main.h" +#include "yuzu/hotkeys.h" class Config; class EmuThread; @@ -43,7 +44,7 @@ public: void filterBarSetChecked(bool state); void UpdateUITheme(); GMainWindow(); - ~GMainWindow(); + ~GMainWindow() override; signals: @@ -172,6 +173,8 @@ private: // stores default icon theme search paths for the platform QStringList default_theme_paths; + HotkeyRegistry hotkey_registry; + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index cea1a5e623..9bf26717fe 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -105,6 +105,11 @@ void Config::ReadValues() { Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0); + // Audio + Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); + Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); + Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); + // Data Storage Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); @@ -114,6 +119,7 @@ void Config::ReadValues() { // Miscellaneous Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); + Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); // Debugging Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 567f23417e..9a935a0d58 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -143,19 +143,17 @@ swap_screen = [Audio] # Which audio output engine to use. -# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available) +# auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available) output_engine = -# Whether or not to enable the audio-stretching post-processing effect. -# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter, -# at the cost of increasing audio latency. -# 0: No, 1 (default): Yes -enable_audio_stretching = - # Which audio device to use. # auto (default): Auto-select output_device = +# Output volume. +# 1.0 (default): 100%, 0.0; mute +volume = + [Data Storage] # Whether to create a virtual SD card. # 1 (default): Yes, 0: No @@ -166,6 +164,16 @@ use_virtual_sd = # 1: Yes, 0 (default): No use_docked_mode = +# Sets the account username, max length is 32 characters +# yuzu (default) +username = + +# Sets the systems language index +# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, +# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, +# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese +language_index = + # The system region that yuzu will use during emulation # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan region_value = diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index b5392c499b..d637dbd0c5 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -23,6 +23,7 @@ #include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include <getopt.h> +#include "core/crypto/key_manager.h" #ifndef _MSC_VER #include <unistd.h> #endif @@ -71,6 +72,7 @@ static void InitializeLogging() { /// Application entry point int main(int argc, char** argv) { Config config; + int option_index = 0; bool use_gdbstub = Settings::values.use_gdbstub; u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); @@ -162,7 +164,7 @@ int main(int argc, char** argv) { SCOPE_EXIT({ system.Shutdown(); }); - const Core::System::ResultStatus load_result{system.Load(emu_window.get(), filepath)}; + const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)}; switch (load_result) { case Core::System::ResultStatus::ErrorGetLoader: @@ -171,11 +173,15 @@ int main(int argc, char** argv) { case Core::System::ResultStatus::ErrorLoader: LOG_CRITICAL(Frontend, "Failed to load ROM!"); return -1; - case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: - LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " - "being used with yuzu. \n\n For more information on dumping and " - "decrypting games, please refer to: " - "https://yuzu-emu.org/wiki/dumping-game-cartridges/"); + case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: + LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and the keys required " + "could not be found. Please refer to the yuzu wiki for help"); + return -1; + case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: + LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and there was a " + "general error while decrypting. This could mean that the keys are " + "incorrect, game is invalid or game uses an unsupported method of " + "crypto. Please double-check your keys"); return -1; case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); @@ -187,7 +193,7 @@ int main(int argc, char** argv) { LOG_CRITICAL(Frontend, "Failed to determine system mode!"); return -1; case Core::System::ResultStatus::ErrorVideoCore: - LOG_CRITICAL(Frontend, "VideoCore not initialized"); + LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!"); return -1; case Core::System::ResultStatus::Success: break; // Expected case |