Compare commits
131 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc9d7bbf01 | ||
|
|
d224eb7c39 | ||
|
|
fd9da4232b | ||
|
|
507e6ae100 | ||
|
|
6a2f415298 | ||
|
|
448264e719 | ||
|
|
f156a45c01 | ||
|
|
6a5d032809 | ||
|
|
4941e3d412 | ||
|
|
9fde7d739a | ||
|
|
3c498189b6 | ||
|
|
934a2b9604 | ||
|
|
2bc296801a | ||
|
|
80cfd88e4e | ||
|
|
d378d98e26 | ||
|
|
766c1a2d50 | ||
|
|
5c323d96e0 | ||
|
|
4e3bc37791 | ||
|
|
a7d6efc520 | ||
|
|
cd1a96f389 | ||
|
|
62b0b83fd8 | ||
|
|
825e8cb925 | ||
|
|
1fbb7aff1e | ||
|
|
4fa3511a63 | ||
|
|
7139f05fc5 | ||
|
|
e530f82dd3 | ||
|
|
1abfd4166e | ||
|
|
b9829a05be | ||
|
|
7ed8565978 | ||
|
|
6576bc8927 | ||
|
|
2d57cbaec1 | ||
|
|
3242e7c164 | ||
|
|
c707240685 | ||
|
|
573a66c23d | ||
|
|
97c6f984dc | ||
|
|
07595987ac | ||
|
|
b09c4f45c7 | ||
|
|
0735a0c8a1 | ||
|
|
bc6d91a103 | ||
|
|
c392650e21 | ||
|
|
8f73f41824 | ||
|
|
c4397ec77e | ||
|
|
438d9aa407 | ||
|
|
3e81c09094 | ||
|
|
12ab5a0547 | ||
|
|
d3f64785d1 | ||
|
|
300ab211e8 | ||
|
|
fa8017295b | ||
|
|
e40b0cf437 | ||
|
|
7e49881b7f | ||
|
|
0f5c4615ae | ||
|
|
890e543304 | ||
|
|
20c976ff2a | ||
|
|
45bc449ff9 | ||
|
|
c8f6754417 | ||
|
|
0db0e4c8f3 | ||
|
|
91cfe70301 | ||
|
|
e4422b09b6 | ||
|
|
5927cf0e17 | ||
|
|
9e88f03e75 | ||
|
|
da2f00ab7d | ||
|
|
826b1394e8 | ||
|
|
0c3c91e41c | ||
|
|
e2b74f6354 | ||
|
|
e218d79cc2 | ||
|
|
75df8a3969 | ||
|
|
645d35ac32 | ||
|
|
168958f8e2 | ||
|
|
e3321c2e00 | ||
|
|
f179e87864 | ||
|
|
cf82358ee6 | ||
|
|
83ef37ca37 | ||
|
|
bf51bbffcb | ||
|
|
96b6ad11c1 | ||
|
|
10d693b9c2 | ||
|
|
a5ac53dd4c | ||
|
|
251e92513a | ||
|
|
cf983888cc | ||
|
|
2b2dc00bfd | ||
|
|
d33f641912 | ||
|
|
9764b4ec0e | ||
|
|
7846295a8f | ||
|
|
00a68c5eea | ||
|
|
2feb1a8ba6 | ||
|
|
1ac45342dd | ||
|
|
37adf04dcd | ||
|
|
2ea0f0fd16 | ||
|
|
904d7eaa94 | ||
|
|
57eb936200 | ||
|
|
0b80bbeb95 | ||
|
|
f1b93d63d1 | ||
|
|
03b7ebbc08 | ||
|
|
bb21c2198a | ||
|
|
6c56754322 | ||
|
|
a0c3a46aa9 | ||
|
|
2a7a2b739b | ||
|
|
4aa31b0618 | ||
|
|
2fc5c783ed | ||
|
|
6edd828101 | ||
|
|
d9815b523b | ||
|
|
7f9430f7ae | ||
|
|
c8e5c74092 | ||
|
|
00f7e584ce | ||
|
|
89c076b4b1 | ||
|
|
c0af42d6eb | ||
|
|
7a77d0a71e | ||
|
|
ca96f8db4e | ||
|
|
e06953626c | ||
|
|
e816745b19 | ||
|
|
fd715e54a1 | ||
|
|
ce46fb27ca | ||
|
|
b46df98e93 | ||
|
|
b96010bfa9 | ||
|
|
6d1e30e041 | ||
|
|
a0a605df06 | ||
|
|
cd96c04339 | ||
|
|
3f4fcd582e | ||
|
|
1dee8ceda1 | ||
|
|
f1cb3903ac | ||
|
|
de72956181 | ||
|
|
df51207ed2 | ||
|
|
0d04ee97dc | ||
|
|
64c8212ae1 | ||
|
|
b25468b498 | ||
|
|
8da651ac4d | ||
|
|
c1f76abfaf | ||
|
|
02fccc0940 | ||
|
|
34b3f83498 | ||
|
|
9f846d3aa4 | ||
|
|
2665457f4a | ||
|
|
6030c5ce41 |
@@ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
|
||||
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||
set(PLATFORMS ${DLL_DEST}platforms/)
|
||||
set(STYLES ${DLL_DEST}styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
icudt*.dll
|
||||
icuin*.dll
|
||||
@@ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
)
|
||||
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
||||
@@ -117,6 +117,7 @@ after_build:
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $RELEASE_DIST/platforms
|
||||
mkdir $RELEASE_DIST/styles
|
||||
mkdir $RELEASE_DIST/imageformats
|
||||
|
||||
# copy the compiled binaries and other release files to the release folder
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
@@ -140,6 +141,9 @@ after_build:
|
||||
# copy the qt windows vista style dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
|
||||
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
|
||||
6
externals/CMakeLists.txt
vendored
6
externals/CMakeLists.txt
vendored
@@ -32,11 +32,11 @@ add_subdirectory(inih)
|
||||
|
||||
# lz4
|
||||
set(LZ4_BUNDLED_MODE ON)
|
||||
add_subdirectory(lz4/contrib/cmake_unofficial)
|
||||
add_subdirectory(lz4/contrib/cmake_unofficial EXCLUDE_FROM_ALL)
|
||||
target_include_directories(lz4_static INTERFACE ./lz4/lib)
|
||||
|
||||
# mbedtls
|
||||
add_subdirectory(mbedtls)
|
||||
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
|
||||
# MicroProfile
|
||||
@@ -62,5 +62,5 @@ target_include_directories(opus INTERFACE ./opus/include)
|
||||
# Cubeb
|
||||
if(ENABLE_CUBEB)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
add_subdirectory(cubeb)
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
2
externals/catch
vendored
2
externals/catch
vendored
Submodule externals/catch updated: d2a130f243...15cf3caace
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 73d3efc3e0...4f96c63025
23
externals/glad/include/glad/glad.h
vendored
23
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
6385
externals/glad/src/glad.c
vendored
6385
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
2
externals/mbedtls
vendored
2
externals/mbedtls
vendored
Submodule externals/mbedtls updated: 06442b840e...d409b75a4c
@@ -1,9 +1,11 @@
|
||||
add_library(audio_core STATIC
|
||||
audio_out.cpp
|
||||
audio_out.h
|
||||
audio_renderer.cpp
|
||||
audio_renderer.h
|
||||
buffer.h
|
||||
cubeb_sink.cpp
|
||||
cubeb_sink.h
|
||||
codec.cpp
|
||||
codec.h
|
||||
null_sink.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
@@ -11,6 +13,8 @@ add_library(audio_core STATIC
|
||||
sink_details.cpp
|
||||
sink_details.h
|
||||
sink_stream.h
|
||||
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||
)
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
@@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
return {};
|
||||
}
|
||||
|
||||
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
|
||||
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));
|
||||
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) {
|
||||
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
|
||||
stream->Stop();
|
||||
}
|
||||
|
||||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
|
||||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
|
||||
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/buffer.h"
|
||||
@@ -20,7 +21,7 @@ namespace AudioCore {
|
||||
class AudioOut {
|
||||
public:
|
||||
/// Opens a new audio stream
|
||||
StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
|
||||
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
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
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<u8>&& data);
|
||||
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
|
||||
|
||||
private:
|
||||
SinkPtr sink;
|
||||
|
||||
234
src/audio_core/audio_renderer.cpp
Normal file
234
src/audio_core/audio_renderer.cpp
Normal file
@@ -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
|
||||
206
src/audio_core/audio_renderer.h
Normal file
206
src/audio_core/audio_renderer.h
Normal file
@@ -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
|
||||
@@ -18,11 +18,16 @@ class Buffer {
|
||||
public:
|
||||
using Tag = u64;
|
||||
|
||||
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
|
||||
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
|
||||
|
||||
/// Returns the raw audio data for the buffer
|
||||
const std::vector<u8>& GetData() const {
|
||||
return data;
|
||||
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
|
||||
@@ -32,7 +37,7 @@ public:
|
||||
|
||||
private:
|
||||
Tag tag;
|
||||
std::vector<u8> data;
|
||||
std::vector<s16> samples;
|
||||
};
|
||||
|
||||
using BufferPtr = std::shared_ptr<Buffer>;
|
||||
|
||||
77
src/audio_core/codec.cpp
Normal file
77
src/audio_core/codec.cpp
Normal file
@@ -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
|
||||
44
src/audio_core/codec.h
Normal file
44
src/audio_core/codec.h
Normal file
@@ -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
|
||||
@@ -13,20 +13,30 @@ namespace AudioCore {
|
||||
|
||||
class SinkStreamImpl final : public SinkStream {
|
||||
public:
|
||||
SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
|
||||
cubeb_stream_params params;
|
||||
params.rate = 48000;
|
||||
params.channels = GetNumChannels();
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
const std::string& name)
|
||||
: ctx{ctx}, num_channels{num_channels_} {
|
||||
|
||||
u32 minimum_latency = 0;
|
||||
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, "yuzu Audio Output", nullptr, nullptr,
|
||||
output_device, ¶ms, std::max(512u, 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");
|
||||
@@ -51,33 +61,29 @@ public:
|
||||
cubeb_stream_destroy(stream_backend);
|
||||
}
|
||||
|
||||
void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
|
||||
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue.reserve(queue.size() + sample_count * GetNumChannels());
|
||||
queue.reserve(queue.size() + samples.size() * GetNumChannels());
|
||||
|
||||
if (num_channels == 2) {
|
||||
// Copy as-is
|
||||
std::copy(samples, samples + sample_count * GetNumChannels(),
|
||||
std::back_inserter(queue));
|
||||
} else if (num_channels == 6) {
|
||||
if (is_6_channel) {
|
||||
// Downsample 6 channels to 2
|
||||
const size_t sample_count_copy_size = sample_count * num_channels * 2;
|
||||
const size_t sample_count_copy_size = samples.size() * 2;
|
||||
queue.reserve(sample_count_copy_size);
|
||||
for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
|
||||
for (size_t i = 0; i < samples.size(); i += num_channels) {
|
||||
queue.push_back(samples[i]);
|
||||
queue.push_back(samples[i + 1]);
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
// Copy as-is
|
||||
std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetNumChannels() const {
|
||||
// Only support 2-channel stereo output for now
|
||||
return 2;
|
||||
return num_channels;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -85,6 +91,8 @@ private:
|
||||
|
||||
cubeb* ctx{};
|
||||
cubeb_stream* stream_backend{};
|
||||
u32 num_channels{};
|
||||
bool is_6_channel{};
|
||||
|
||||
std::vector<s16> queue;
|
||||
|
||||
@@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
|
||||
sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ public:
|
||||
explicit CubebSink(std::string device_id);
|
||||
~CubebSink() override;
|
||||
|
||||
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
|
||||
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) override;
|
||||
|
||||
private:
|
||||
cubeb* ctx{};
|
||||
|
||||
@@ -13,14 +13,14 @@ public:
|
||||
explicit NullSink(std::string){};
|
||||
~NullSink() override = default;
|
||||
|
||||
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
|
||||
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 s16* /*samples*/,
|
||||
size_t /*sample_count*/) override {}
|
||||
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
|
||||
} null_sink_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
|
||||
class Sink {
|
||||
public:
|
||||
virtual ~Sink() = default;
|
||||
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
|
||||
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) = 0;
|
||||
};
|
||||
|
||||
using SinkPtr = std::unique_ptr<Sink>;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -22,9 +23,8 @@ public:
|
||||
* Feed stereo samples to sink.
|
||||
* @param num_channels Number of channels used.
|
||||
* @param samples Samples in interleaved stereo PCM16 format.
|
||||
* @param sample_count Number of samples.
|
||||
*/
|
||||
virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
|
||||
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
|
||||
};
|
||||
|
||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||
|
||||
@@ -32,17 +32,13 @@ u32 Stream::GetNumChannels() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
u32 Stream::GetSampleSize() const {
|
||||
return GetNumChannels() * 2;
|
||||
}
|
||||
|
||||
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
|
||||
SinkStream& sink_stream)
|
||||
SinkStream& sink_stream, std::string&& name_)
|
||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||
sink_stream{sink_stream} {
|
||||
sink_stream{sink_stream}, name{std::move(name_)} {
|
||||
|
||||
release_event = CoreTiming::RegisterEvent(
|
||||
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||
}
|
||||
|
||||
void Stream::Play() {
|
||||
@@ -55,17 +51,15 @@ void Stream::Stop() {
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
|
||||
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
}
|
||||
|
||||
static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
|
||||
std::vector<s16> samples(data.size() / sizeof(s16));
|
||||
std::memcpy(samples.data(), data.data(), data.size());
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
|
||||
|
||||
if (volume == 1.0f) {
|
||||
return samples;
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementation of a volume slider with a dynamic range of 60 dB
|
||||
@@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
|
||||
for (auto& sample : samples) {
|
||||
sample = static_cast<s16>(sample * volume_scale_factor);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
void Stream::PlayNextBuffer() {
|
||||
@@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() {
|
||||
active_buffer = queued_buffers.front();
|
||||
queued_buffers.pop();
|
||||
|
||||
const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
|
||||
sink_stream.EnqueueSamples(
|
||||
GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
|
||||
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();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
using ReleaseCallback = std::function<void()>;
|
||||
|
||||
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
|
||||
SinkStream& sink_stream);
|
||||
SinkStream& sink_stream, std::string&& name_);
|
||||
|
||||
/// Plays the audio stream
|
||||
void Play();
|
||||
@@ -68,9 +69,6 @@ public:
|
||||
/// Gets the number of channels
|
||||
u32 GetNumChannels() const;
|
||||
|
||||
/// Gets the sample size in bytes
|
||||
u32 GetSampleSize() const;
|
||||
|
||||
private:
|
||||
/// Current state of the stream
|
||||
enum class State {
|
||||
@@ -96,6 +94,7 @@ private:
|
||||
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>;
|
||||
|
||||
@@ -9,13 +9,13 @@ namespace Common {
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, size_t size) {
|
||||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignDown(T value, size_t size) {
|
||||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) {
|
||||
|
||||
template <typename IntTy>
|
||||
class BitSet {
|
||||
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
|
||||
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
|
||||
|
||||
public:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -207,39 +208,42 @@ public:
|
||||
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
|
||||
if (!IsOpen())
|
||||
return -1;
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
return std::fread(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteArray(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
if (!IsOpen())
|
||||
return -1;
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
return std::fwrite(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteBytes(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
|
||||
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) {
|
||||
*/
|
||||
template <typename T>
|
||||
static inline u64 ComputeStructHash64(const T& data) {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Type passed to ComputeStructHash64 must be trivially copyable");
|
||||
return ComputeHash64(&data, sizeof(data));
|
||||
}
|
||||
@@ -38,7 +38,7 @@ template <typename T>
|
||||
struct HashableStruct {
|
||||
// In addition to being trivially copyable, T must also have a trivial default constructor,
|
||||
// because any member initialization would be overridden by memset
|
||||
static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial");
|
||||
static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
|
||||
/*
|
||||
* We use a union because "implicitly-defined copy/move constructor for a union X copies the
|
||||
* object representation of X." and "implicitly-defined copy assignment operator for a union X
|
||||
|
||||
@@ -168,6 +168,7 @@ 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) \
|
||||
@@ -199,6 +200,7 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
SUB(Service, WLAN) \
|
||||
CLS(HW) \
|
||||
|
||||
@@ -54,6 +54,7 @@ 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
|
||||
@@ -86,6 +87,7 @@ enum class Class : ClassType {
|
||||
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
|
||||
|
||||
@@ -42,140 +42,136 @@ class Vec3;
|
||||
template <typename T>
|
||||
class Vec4;
|
||||
|
||||
template <typename T>
|
||||
static inline Vec2<T> MakeVec(const T& x, const T& y);
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z);
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w);
|
||||
|
||||
template <typename T>
|
||||
class Vec2 {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
|
||||
Vec2() = default;
|
||||
Vec2(const T& _x, const T& _y) : x(_x), y(_y) {}
|
||||
constexpr Vec2() = default;
|
||||
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
||||
|
||||
template <typename T2>
|
||||
Vec2<T2> Cast() const {
|
||||
return Vec2<T2>((T2)x, (T2)y);
|
||||
constexpr Vec2<T2> Cast() const {
|
||||
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
||||
}
|
||||
|
||||
static Vec2 AssignToAll(const T& f) {
|
||||
return Vec2<T>(f, f);
|
||||
static constexpr Vec2 AssignToAll(const T& f) {
|
||||
return Vec2{f, f};
|
||||
}
|
||||
|
||||
Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||
return MakeVec(x + other.x, y + other.y);
|
||||
constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||
return {x + other.x, y + other.y};
|
||||
}
|
||||
void operator+=(const Vec2& other) {
|
||||
constexpr Vec2& operator+=(const Vec2& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||
return MakeVec(x - other.x, y - other.y);
|
||||
constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||
return {x - other.x, y - other.y};
|
||||
}
|
||||
void operator-=(const Vec2& other) {
|
||||
constexpr Vec2& operator-=(const Vec2& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
|
||||
return MakeVec(-x, -y);
|
||||
constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y};
|
||||
}
|
||||
Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||
return MakeVec(x * other.x, y * other.y);
|
||||
}
|
||||
template <typename V>
|
||||
Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return MakeVec(x * f, y * f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
}
|
||||
template <typename V>
|
||||
Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return MakeVec(x / f, y / f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||
return {x * other.x, y * other.y};
|
||||
}
|
||||
|
||||
T Length2() const {
|
||||
template <typename V>
|
||||
constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return {x * f, y * f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return {x / f, y / f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Length2() const {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
float Length() const;
|
||||
void SetLength(const float l);
|
||||
Vec2 WithLength(const float l) const;
|
||||
float Distance2To(Vec2& other);
|
||||
Vec2 Normalized() const;
|
||||
float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
T& operator[](int i) // allow vector[1] = 3 (vector.y=3)
|
||||
{
|
||||
constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
T operator[](const int i) const {
|
||||
constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
void SetZero() {
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UV (texel coordinates), ST (texture coordinates)
|
||||
T& u() {
|
||||
constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
T& v() {
|
||||
constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
T& s() {
|
||||
constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
T& t() {
|
||||
constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
|
||||
const T& u() const {
|
||||
constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
const T& v() const {
|
||||
constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
const T& s() const {
|
||||
constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
const T& t() const {
|
||||
constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
|
||||
// swizzlers - create a subvector of specific components
|
||||
const Vec2 yx() const {
|
||||
constexpr Vec2 yx() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
const Vec2 vu() const {
|
||||
constexpr Vec2 vu() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
const Vec2 ts() const {
|
||||
constexpr Vec2 ts() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||
constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||
return Vec2<T>(f * vec.x, f * vec.y);
|
||||
}
|
||||
|
||||
typedef Vec2<float> Vec2f;
|
||||
using Vec2f = Vec2<float>;
|
||||
|
||||
template <>
|
||||
inline float Vec2<float>::Length() const {
|
||||
@@ -196,147 +192,151 @@ public:
|
||||
T y{};
|
||||
T z{};
|
||||
|
||||
Vec3() = default;
|
||||
Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {}
|
||||
constexpr Vec3() = default;
|
||||
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
template <typename T2>
|
||||
Vec3<T2> Cast() const {
|
||||
return MakeVec<T2>((T2)x, (T2)y, (T2)z);
|
||||
constexpr Vec3<T2> Cast() const {
|
||||
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
|
||||
}
|
||||
|
||||
// Only implemented for T=int and T=float
|
||||
static Vec3 FromRGB(unsigned int rgb);
|
||||
unsigned int ToRGB() const; // alpha bits set to zero
|
||||
|
||||
static Vec3 AssignToAll(const T& f) {
|
||||
return MakeVec(f, f, f);
|
||||
static constexpr Vec3 AssignToAll(const T& f) {
|
||||
return Vec3(f, f, f);
|
||||
}
|
||||
|
||||
Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||
return MakeVec(x + other.x, y + other.y, z + other.z);
|
||||
constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z};
|
||||
}
|
||||
void operator+=(const Vec3& other) {
|
||||
|
||||
constexpr Vec3& operator+=(const Vec3& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||
return MakeVec(x - other.x, y - other.y, z - other.z);
|
||||
|
||||
constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z};
|
||||
}
|
||||
void operator-=(const Vec3& other) {
|
||||
|
||||
constexpr Vec3& operator-=(const Vec3& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
|
||||
return MakeVec(-x, -y, -z);
|
||||
}
|
||||
Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||
return MakeVec(x * other.x, y * other.y, z * other.z);
|
||||
}
|
||||
template <typename V>
|
||||
Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return MakeVec(x * f, y * f, z * f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
}
|
||||
template <typename V>
|
||||
Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return MakeVec(x / f, y / f, z / f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z};
|
||||
}
|
||||
|
||||
T Length2() const {
|
||||
constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return {x * f, y * f, z * f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
template <typename V>
|
||||
constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return {x / f, y / f, z / f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Length2() const {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
float Length() const;
|
||||
void SetLength(const float l);
|
||||
Vec3 WithLength(const float l) const;
|
||||
float Distance2To(Vec3& other);
|
||||
Vec3 Normalized() const;
|
||||
float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
|
||||
{
|
||||
return *((&x) + i);
|
||||
}
|
||||
T operator[](const int i) const {
|
||||
constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
void SetZero() {
|
||||
constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
|
||||
T& u() {
|
||||
constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
T& v() {
|
||||
constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
T& w() {
|
||||
constexpr T& w() {
|
||||
return z;
|
||||
}
|
||||
|
||||
T& r() {
|
||||
constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
T& g() {
|
||||
constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
T& b() {
|
||||
constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
|
||||
T& s() {
|
||||
constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
T& t() {
|
||||
constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
T& q() {
|
||||
constexpr T& q() {
|
||||
return z;
|
||||
}
|
||||
|
||||
const T& u() const {
|
||||
constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
const T& v() const {
|
||||
constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
const T& w() const {
|
||||
constexpr const T& w() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
const T& r() const {
|
||||
constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
const T& g() const {
|
||||
constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
const T& b() const {
|
||||
constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
const T& s() const {
|
||||
constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
const T& t() const {
|
||||
constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
const T& q() const {
|
||||
constexpr const T& q() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ public:
|
||||
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
||||
// component names (x<->r) and permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
const Vec2<T> name() const { \
|
||||
constexpr Vec2<T> name() const { \
|
||||
return Vec2<T>(a, b); \
|
||||
}
|
||||
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
||||
@@ -366,7 +366,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||
constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||
return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ inline float Vec3<float>::Normalize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
typedef Vec3<float> Vec3f;
|
||||
using Vec3f = Vec3<float>;
|
||||
|
||||
template <typename T>
|
||||
class Vec4 {
|
||||
@@ -397,86 +397,88 @@ public:
|
||||
T z{};
|
||||
T w{};
|
||||
|
||||
Vec4() = default;
|
||||
Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {}
|
||||
constexpr Vec4() = default;
|
||||
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
|
||||
: x(x_), y(y_), z(z_), w(w_) {}
|
||||
|
||||
template <typename T2>
|
||||
Vec4<T2> Cast() const {
|
||||
return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w);
|
||||
constexpr Vec4<T2> Cast() const {
|
||||
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
|
||||
static_cast<T2>(w));
|
||||
}
|
||||
|
||||
// Only implemented for T=int and T=float
|
||||
static Vec4 FromRGBA(unsigned int rgba);
|
||||
unsigned int ToRGBA() const;
|
||||
|
||||
static Vec4 AssignToAll(const T& f) {
|
||||
return Vec4<T>(f, f, f, f);
|
||||
static constexpr Vec4 AssignToAll(const T& f) {
|
||||
return Vec4(f, f, f, f);
|
||||
}
|
||||
|
||||
Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||
return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w);
|
||||
constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z, w + other.w};
|
||||
}
|
||||
void operator+=(const Vec4& other) {
|
||||
|
||||
constexpr Vec4& operator+=(const Vec4& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
w += other.w;
|
||||
return *this;
|
||||
}
|
||||
Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||
return MakeVec(x - other.x, y - other.y, z - other.z, w - other.w);
|
||||
|
||||
constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z, w - other.w};
|
||||
}
|
||||
void operator-=(const Vec4& other) {
|
||||
|
||||
constexpr Vec4& operator-=(const Vec4& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
w -= other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
|
||||
return MakeVec(-x, -y, -z, -w);
|
||||
}
|
||||
Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||
return MakeVec(x * other.x, y * other.y, z * other.z, w * other.w);
|
||||
}
|
||||
template <typename V>
|
||||
Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return MakeVec(x * f, y * f, z * f, w * f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
}
|
||||
template <typename V>
|
||||
Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return MakeVec(x / f, y / f, z / f, w / f);
|
||||
}
|
||||
template <typename V>
|
||||
void operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z, -w};
|
||||
}
|
||||
|
||||
T Length2() const {
|
||||
constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z, w * other.w};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
return {x * f, y * f, z * f, w * f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
return {x / f, y / f, z / f, w / f};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Length2() const {
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
float Length() const;
|
||||
void SetLength(const float l);
|
||||
Vec4 WithLength(const float l) const;
|
||||
float Distance2To(Vec4& other);
|
||||
Vec4 Normalized() const;
|
||||
float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
|
||||
{
|
||||
return *((&x) + i);
|
||||
}
|
||||
T operator[](const int i) const {
|
||||
constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
void SetZero() {
|
||||
constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
@@ -484,29 +486,29 @@ public:
|
||||
}
|
||||
|
||||
// Common alias: RGBA (colors)
|
||||
T& r() {
|
||||
constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
T& g() {
|
||||
constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
T& b() {
|
||||
constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
T& a() {
|
||||
constexpr T& a() {
|
||||
return w;
|
||||
}
|
||||
|
||||
const T& r() const {
|
||||
constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
const T& g() const {
|
||||
constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
const T& b() const {
|
||||
constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
const T& a() const {
|
||||
constexpr const T& a() const {
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -518,7 +520,7 @@ public:
|
||||
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
||||
// permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
const Vec2<T> name() const { \
|
||||
constexpr Vec2<T> name() const { \
|
||||
return Vec2<T>(a, b); \
|
||||
}
|
||||
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
||||
@@ -545,7 +547,7 @@ public:
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
|
||||
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
||||
const Vec3<T> name() const { \
|
||||
constexpr Vec3<T> name() const { \
|
||||
return Vec3<T>(a, b, c); \
|
||||
}
|
||||
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
||||
@@ -579,51 +581,51 @@ public:
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||
return MakeVec(f * vec.x, f * vec.y, f * vec.z, f * vec.w);
|
||||
constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||
return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
|
||||
}
|
||||
|
||||
typedef Vec4<float> Vec4f;
|
||||
using Vec4f = Vec4<float>;
|
||||
|
||||
template <typename T>
|
||||
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return MakeVec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
|
||||
constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||
}
|
||||
|
||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||
template <typename X>
|
||||
static inline decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
return begin * (1.f - t) + end * t;
|
||||
}
|
||||
|
||||
// linear interpolation via int: 0=begin, base=end
|
||||
template <typename X, int base>
|
||||
static inline decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
|
||||
const int t) {
|
||||
constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
|
||||
const int t) {
|
||||
return (begin * (base - t) + end * t) / base;
|
||||
}
|
||||
|
||||
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
|
||||
// interpolation.
|
||||
template <typename X>
|
||||
inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
|
||||
const float t) {
|
||||
constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
|
||||
const float t) {
|
||||
auto y0 = Lerp(x00, x01, s);
|
||||
auto y1 = Lerp(x10, x11, s);
|
||||
return Lerp(y0, y1, t);
|
||||
@@ -631,42 +633,42 @@ inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x1
|
||||
|
||||
// Utility vector factories
|
||||
template <typename T>
|
||||
static inline Vec2<T> MakeVec(const T& x, const T& y) {
|
||||
constexpr Vec2<T> MakeVec(const T& x, const T& y) {
|
||||
return Vec2<T>{x, y};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||
constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||
return Vec3<T>{x, y, z};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||
return MakeVec(x, y, zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||
constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||
return MakeVec(xy[0], xy[1], z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||
constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||
return MakeVec(x, yz[0], yz[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||
return Vec4<T>{x, y, z, w};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||
return MakeVec(xy[0], xy[1], z, w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
return MakeVec(x, yz[0], yz[1], w);
|
||||
}
|
||||
|
||||
@@ -674,17 +676,17 @@ static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
|
||||
// out soon enough due to misuse of the returned structure.
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||
constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||
return MakeVec(xyz[0], xyz[1], xyz[2], w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||
constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer<T>(), "Argument must be a (function) pointer.");
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
|
||||
@@ -104,8 +104,6 @@ add_library(core STATIC
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/romfs.cpp
|
||||
hle/romfs.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
hle/service/acc/acc_aa.cpp
|
||||
@@ -134,6 +132,8 @@ add_library(core STATIC
|
||||
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
|
||||
@@ -313,6 +313,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/loader/loader.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 {
|
||||
@@ -61,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;
|
||||
@@ -77,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
}
|
||||
}
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -178,7 +182,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
|
||||
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>();
|
||||
|
||||
@@ -186,10 +189,13 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
|
||||
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];
|
||||
@@ -221,7 +227,7 @@ void System::Shutdown() {
|
||||
perf_results.frametime * 1000.0);
|
||||
|
||||
// Shutdown emulation session
|
||||
VideoCore::Shutdown();
|
||||
renderer.reset();
|
||||
GDBStub::Shutdown();
|
||||
Service::Shutdown();
|
||||
Kernel::Shutdown();
|
||||
|
||||
@@ -27,6 +27,10 @@ namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System {
|
||||
@@ -78,6 +82,17 @@ 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();
|
||||
|
||||
@@ -129,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();
|
||||
@@ -197,6 +227,7 @@ private:
|
||||
|
||||
/// 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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -3,10 +3,22 @@
|
||||
// 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.");
|
||||
@@ -56,27 +68,28 @@ void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
|
||||
}
|
||||
|
||||
template <typename Key, size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) {
|
||||
size_t written = 0;
|
||||
|
||||
const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
|
||||
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)
|
||||
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)
|
||||
if (written != length) {
|
||||
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
|
||||
length, written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,16 +110,6 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, size_t KeySize>
|
||||
std::vector<u8> AESCipher<Key, KeySize>::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;
|
||||
}
|
||||
|
||||
template class AESCipher<Key128>;
|
||||
template class AESCipher<Key256>;
|
||||
} // namespace Core::Crypto
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
@@ -38,15 +38,19 @@ public:
|
||||
void SetIV(std::vector<u8> iv);
|
||||
|
||||
template <typename Source, typename Dest>
|
||||
void Transcode(const Source* src, size_t size, Dest* dest, Op op) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
@@ -56,7 +60,5 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<CipherContext> ctx;
|
||||
|
||||
static std::vector<u8> CalculateNintendoTweak(size_t sector_id);
|
||||
};
|
||||
} // namespace Core::Crypto
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
@@ -2,19 +2,16 @@
|
||||
// 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 <mbedtls/sha256.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/settings.h"
|
||||
#include "key_manager.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
@@ -66,8 +63,7 @@ KeyManager::KeyManager() {
|
||||
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
|
||||
}
|
||||
|
||||
void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
|
||||
const auto filename = std::string(filename_);
|
||||
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
|
||||
std::ifstream file(filename);
|
||||
if (!file.is_open())
|
||||
return;
|
||||
@@ -107,11 +103,8 @@ void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
|
||||
}
|
||||
}
|
||||
|
||||
void KeyManager::AttemptLoadKeyFile(std::string_view dir1_, std::string_view dir2_,
|
||||
std::string_view filename_, bool title) {
|
||||
const std::string dir1(dir1_);
|
||||
const std::string dir2(dir2_);
|
||||
const std::string filename(filename_);
|
||||
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))
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -109,9 +110,9 @@ private:
|
||||
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
|
||||
|
||||
bool dev_mode;
|
||||
void LoadFromFile(std::string_view filename, bool is_title_keys);
|
||||
void AttemptLoadKeyFile(std::string_view dir1, std::string_view dir2, std::string_view filename,
|
||||
bool title);
|
||||
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;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <core/loader/loader.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
|
||||
@@ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
return;
|
||||
}
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header))
|
||||
LOG_ERROR(Loader, "File reader errored out during header read.");
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "control_metadata.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -62,6 +62,13 @@ enum class Language : u8 {
|
||||
Chinese = 14,
|
||||
};
|
||||
|
||||
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
|
||||
"AmericanEnglish", "BritishEnglish", "Japanese",
|
||||
"French", "German", "LatinAmericanSpanish",
|
||||
"Spanish", "Italian", "Dutch",
|
||||
"CanadianFrench", "Portugese", "Russian",
|
||||
"Korean", "Taiwanese", "Chinese"};
|
||||
|
||||
// A class representing the format used by NX metadata files, typically named Control.nacp.
|
||||
// These store application name, dev name, title id, and other miscellaneous data.
|
||||
class NACP {
|
||||
|
||||
@@ -41,40 +41,42 @@
|
||||
#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">
|
||||
@@ -140,30 +142,28 @@ static const char* target_xml =
|
||||
</target>
|
||||
)";
|
||||
|
||||
namespace GDBStub {
|
||||
int gdbserver_socket = -1;
|
||||
|
||||
static int gdbserver_socket = -1;
|
||||
u8 command_buffer[GDB_BUFFER_SIZE];
|
||||
u32 command_length;
|
||||
|
||||
static u8 command_buffer[GDB_BUFFER_SIZE];
|
||||
static u32 command_length;
|
||||
u32 latest_signal = 0;
|
||||
bool memory_break = false;
|
||||
|
||||
static u32 latest_signal = 0;
|
||||
static 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;
|
||||
@@ -171,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);
|
||||
@@ -418,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;
|
||||
@@ -441,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()) {
|
||||
@@ -468,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;
|
||||
@@ -931,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");
|
||||
}
|
||||
|
||||
@@ -950,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.
|
||||
@@ -966,6 +977,7 @@ static void Continue() {
|
||||
memory_break = false;
|
||||
step_loop = false;
|
||||
halt_loop = false;
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -975,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}",
|
||||
@@ -1015,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 =
|
||||
@@ -1064,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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -32,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();
|
||||
@@ -45,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.
|
||||
@@ -76,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;
|
||||
@@ -110,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;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
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
|
||||
@@ -40,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,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.
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/romfs.h"
|
||||
|
||||
namespace RomFS {
|
||||
|
||||
struct Header {
|
||||
u32_le header_length;
|
||||
u32_le dir_hash_table_offset;
|
||||
u32_le dir_hash_table_length;
|
||||
u32_le dir_table_offset;
|
||||
u32_le dir_table_length;
|
||||
u32_le file_hash_table_offset;
|
||||
u32_le file_hash_table_length;
|
||||
u32_le file_table_offset;
|
||||
u32_le file_table_length;
|
||||
u32_le data_offset;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x28, "Header has incorrect size");
|
||||
|
||||
struct DirectoryMetadata {
|
||||
u32_le parent_dir_offset;
|
||||
u32_le next_dir_offset;
|
||||
u32_le first_child_dir_offset;
|
||||
u32_le first_file_offset;
|
||||
u32_le same_hash_next_dir_offset;
|
||||
u32_le name_length; // in bytes
|
||||
// followed by directory name
|
||||
};
|
||||
|
||||
static_assert(sizeof(DirectoryMetadata) == 0x18, "DirectoryMetadata has incorrect size");
|
||||
|
||||
struct FileMetadata {
|
||||
u32_le parent_dir_offset;
|
||||
u32_le next_file_offset;
|
||||
u64_le data_offset;
|
||||
u64_le data_length;
|
||||
u32_le same_hash_next_file_offset;
|
||||
u32_le name_length; // in bytes
|
||||
// followed by file name
|
||||
};
|
||||
|
||||
static_assert(sizeof(FileMetadata) == 0x20, "FileMetadata has incorrect size");
|
||||
|
||||
static bool MatchName(const u8* buffer, u32 name_length, const std::u16string& name) {
|
||||
std::vector<char16_t> name_buffer(name_length / sizeof(char16_t));
|
||||
std::memcpy(name_buffer.data(), buffer, name_length);
|
||||
return name == std::u16string(name_buffer.begin(), name_buffer.end());
|
||||
}
|
||||
|
||||
const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path) {
|
||||
constexpr u32 INVALID_FIELD = 0xFFFFFFFF;
|
||||
|
||||
// Split path into directory names and file name
|
||||
std::vector<std::u16string> dir_names = path;
|
||||
dir_names.pop_back();
|
||||
const std::u16string& file_name = path.back();
|
||||
|
||||
Header header;
|
||||
std::memcpy(&header, romfs, sizeof(header));
|
||||
|
||||
// Find directories of each level
|
||||
DirectoryMetadata dir;
|
||||
const u8* current_dir = romfs + header.dir_table_offset;
|
||||
std::memcpy(&dir, current_dir, sizeof(dir));
|
||||
for (const std::u16string& dir_name : dir_names) {
|
||||
u32 child_dir_offset;
|
||||
child_dir_offset = dir.first_child_dir_offset;
|
||||
while (true) {
|
||||
if (child_dir_offset == INVALID_FIELD) {
|
||||
return nullptr;
|
||||
}
|
||||
const u8* current_child_dir = romfs + header.dir_table_offset + child_dir_offset;
|
||||
std::memcpy(&dir, current_child_dir, sizeof(dir));
|
||||
if (MatchName(current_child_dir + sizeof(dir), dir.name_length, dir_name)) {
|
||||
current_dir = current_child_dir;
|
||||
break;
|
||||
}
|
||||
child_dir_offset = dir.next_dir_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the file
|
||||
FileMetadata file;
|
||||
u32 file_offset = dir.first_file_offset;
|
||||
while (file_offset != INVALID_FIELD) {
|
||||
const u8* current_file = romfs + header.file_table_offset + file_offset;
|
||||
std::memcpy(&file, current_file, sizeof(file));
|
||||
if (MatchName(current_file + sizeof(file), file.name_length, file_name)) {
|
||||
return romfs + header.data_offset + file.data_offset;
|
||||
}
|
||||
file_offset = file.next_file_offset;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace RomFS
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace RomFS {
|
||||
|
||||
/**
|
||||
* Gets the pointer to a file in a RomFS image.
|
||||
* @param romfs The pointer to the RomFS image
|
||||
* @param path A vector containing the directory names and file name of the path to the file
|
||||
* @return the pointer to the file
|
||||
* @todo reimplement this with a full RomFS manager
|
||||
*/
|
||||
const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path);
|
||||
|
||||
} // namespace RomFS
|
||||
@@ -119,6 +119,13 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void GetUserCount(Kernel::HLERequestContext& ctx);
|
||||
void GetUserExistence(Kernel::HLERequestContext& ctx);
|
||||
void ListAllUsers(Kernel::HLERequestContext& ctx);
|
||||
void ListOpenUsers(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Service::Account {
|
||||
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetUserCount"},
|
||||
{0, &ACC_SU::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_SU::GetUserExistence, "GetUserExistence"},
|
||||
{2, &ACC_SU::ListAllUsers, "ListAllUsers"},
|
||||
{3, &ACC_SU::ListOpenUsers, "ListOpenUsers"},
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Service::Account {
|
||||
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetUserCount"},
|
||||
{0, &ACC_U0::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
|
||||
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
|
||||
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Service::Account {
|
||||
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetUserCount"},
|
||||
{0, &ACC_U1::GetUserCount, "GetUserCount"},
|
||||
{1, &ACC_U1::GetUserExistence, "GetUserExistence"},
|
||||
{2, &ACC_U1::ListAllUsers, "ListAllUsers"},
|
||||
{3, &ACC_U1::ListOpenUsers, "ListOpenUsers"},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
75
src/core/hle/service/arp/arp.cpp
Normal file
75
src/core/hle/service/arp/arp.cpp
Normal file
@@ -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
|
||||
16
src/core/hle/service/arp/arp.h
Normal file
16
src/core/hle/service/arp/arp.h
Normal file
@@ -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
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/audio/audin_a.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/audio/audout_a.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/codec.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
|
||||
[=]() { buffer_event->Signal(); });
|
||||
"IAudioOut", [=]() { buffer_event->Signal(); });
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -111,10 +113,10 @@ private:
|
||||
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
|
||||
std::vector<u8> data(audio_buffer.buffer_size);
|
||||
Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
|
||||
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(data))) {
|
||||
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
|
||||
}
|
||||
@@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(DefaultSampleRate);
|
||||
rb.Push<u32>(params.channel_count);
|
||||
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -38,16 +38,6 @@ private:
|
||||
|
||||
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
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/audio/audrec_a.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/audio/audren_a.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/filesystem/fsp_pr.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
|
||||
@@ -337,6 +337,7 @@ public:
|
||||
"AcquireNpadStyleSetUpdateEventHandle"},
|
||||
{107, nullptr, "DisconnectNpad"},
|
||||
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
|
||||
{109, nullptr, "ActivateNpadWithRevision"},
|
||||
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
|
||||
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
|
||||
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
|
||||
@@ -456,7 +457,7 @@ private:
|
||||
}
|
||||
|
||||
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
// TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
|
||||
rb.Push(true);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
// Remove this memory region from the rasterizer cache.
|
||||
VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
|
||||
auto& system_instance = Core::System::GetInstance();
|
||||
|
||||
// Remove this memory region from the rasterizer cache.
|
||||
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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvdrv/nvmemp.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
std::weak_ptr<Module> nvdrv;
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) {
|
||||
auto module_ = std::make_shared<Module>();
|
||||
std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
|
||||
std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager);
|
||||
std::make_shared<NVMEMP>()->InstallAsService(service_manager);
|
||||
nvdrv = module_;
|
||||
nvflinger.SetNVDrvInstance(module_);
|
||||
}
|
||||
|
||||
Module::Module() {
|
||||
@@ -54,7 +53,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");
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
namespace Devices {
|
||||
@@ -56,8 +60,6 @@ private:
|
||||
};
|
||||
|
||||
/// Registers all NVDRV services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
extern std::weak_ptr<Module> nvdrv;
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger);
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,11 @@ NVFlinger::~NVFlinger() {
|
||||
CoreTiming::UnscheduleEvent(composition_event, 0);
|
||||
}
|
||||
|
||||
u64 NVFlinger::OpenDisplay(const std::string& name) {
|
||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
nvdrv = std::move(instance);
|
||||
}
|
||||
|
||||
u64 NVFlinger::OpenDisplay(std::string_view name) {
|
||||
LOG_WARNING(Service, "Opening display {}", name);
|
||||
|
||||
// TODO(Subv): Currently we only support the Default display.
|
||||
@@ -127,18 +134,17 @@ 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;
|
||||
}
|
||||
|
||||
auto& igbp_buffer = buffer->igbp_buffer;
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
auto nvdrv = Nvidia::nvdrv.lock();
|
||||
ASSERT(nvdrv);
|
||||
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
|
||||
|
||||
@@ -5,13 +5,21 @@
|
||||
#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 {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class Module;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
class BufferQueue;
|
||||
@@ -40,8 +48,11 @@ public:
|
||||
NVFlinger();
|
||||
~NVFlinger();
|
||||
|
||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
|
||||
|
||||
/// 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);
|
||||
@@ -66,6 +77,8 @@ private:
|
||||
/// Returns the layer identified by the specified id in the desired display.
|
||||
Layer& GetLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||
|
||||
std::vector<Display> displays;
|
||||
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#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"
|
||||
@@ -62,6 +63,7 @@
|
||||
#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"
|
||||
|
||||
@@ -207,6 +209,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
AM::InstallInterfaces(*sm, nv_flinger);
|
||||
AOC::InstallInterfaces(*sm);
|
||||
APM::InstallInterfaces(*sm);
|
||||
ARP::InstallInterfaces(*sm);
|
||||
Audio::InstallInterfaces(*sm);
|
||||
BCAT::InstallInterfaces(*sm);
|
||||
BPC::InstallInterfaces(*sm);
|
||||
@@ -235,7 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
NIFM::InstallInterfaces(*sm);
|
||||
NIM::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm);
|
||||
Nvidia::InstallInterfaces(*sm);
|
||||
Nvidia::InstallInterfaces(*sm, *nv_flinger);
|
||||
PCIe::InstallInterfaces(*sm);
|
||||
PCTL::InstallInterfaces(*sm);
|
||||
PCV::InstallInterfaces(*sm);
|
||||
@@ -247,6 +250,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
SPL::InstallInterfaces(*sm);
|
||||
SSL::InstallInterfaces(*sm);
|
||||
Time::InstallInterfaces(*sm);
|
||||
USB::InstallInterfaces(*sm);
|
||||
VI::InstallInterfaces(*sm, nv_flinger);
|
||||
WLAN::InstallInterfaces(*sm);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
238
src/core/hle/service/usb/usb.cpp
Normal file
238
src/core/hle/service/usb/usb.cpp
Normal file
@@ -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
|
||||
15
src/core/hle/service/usb/usb.h
Normal file
15
src/core/hle/service/usb/usb.h
Normal file
@@ -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
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -17,8 +18,54 @@
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
|
||||
: AppLoader(std::move(file)) {}
|
||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_)
|
||||
: AppLoader(std::move(file_)) {
|
||||
const auto dir = file->GetContainingDirectory();
|
||||
|
||||
// Icon
|
||||
FileSys::VirtualFile icon_file = nullptr;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = dir->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file != nullptr) {
|
||||
icon_data = icon_file->ReadAllBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (icon_data.empty()) {
|
||||
// Any png, jpeg, or bmp file
|
||||
const auto& files = dir->GetFiles();
|
||||
const auto icon_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
|
||||
return file->GetExtension() == "png" || file->GetExtension() == "jpg" ||
|
||||
file->GetExtension() == "bmp" || file->GetExtension() == "jpeg";
|
||||
});
|
||||
if (icon_iter != files.end())
|
||||
icon_data = (*icon_iter)->ReadAllBytes();
|
||||
}
|
||||
|
||||
// Metadata
|
||||
FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp");
|
||||
if (nacp_file == nullptr) {
|
||||
const auto& files = dir->GetFiles();
|
||||
const auto nacp_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
|
||||
return file->GetExtension() == "nacp";
|
||||
});
|
||||
if (nacp_iter != files.end())
|
||||
nacp_file = *nacp_iter;
|
||||
}
|
||||
|
||||
if (nacp_file != nullptr) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
title_id = nacp.GetTitleId();
|
||||
name = nacp.GetApplicationName();
|
||||
}
|
||||
}
|
||||
|
||||
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())) {
|
||||
@@ -34,7 +81,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;
|
||||
@@ -96,4 +148,25 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
title = name;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // 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
|
||||
@@ -36,10 +39,18 @@ public:
|
||||
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
|
||||
private:
|
||||
FileSys::ProgramMetadata metadata;
|
||||
FileSys::VirtualFile romfs;
|
||||
FileSys::VirtualDir dir;
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::string name;
|
||||
u64 title_id{};
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -68,7 +68,7 @@ FileType GuessFromFilename(const std::string& name) {
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
const char* GetFileTypeString(FileType type) {
|
||||
std::string GetFileTypeString(FileType type) {
|
||||
switch (type) {
|
||||
case FileType::ELF:
|
||||
return "ELF";
|
||||
|
||||
@@ -61,7 +61,7 @@ FileType GuessFromFilename(const std::string& name);
|
||||
/**
|
||||
* Convert a FileType into a string which can be displayed to the user.
|
||||
*/
|
||||
const char* GetFileTypeString(FileType type);
|
||||
std::string GetFileTypeString(FileType type);
|
||||
|
||||
/// Return type for functions in Loader namespace
|
||||
enum class ResultStatus {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user