Compare commits
93 Commits
android-25
...
android-26
Author | SHA1 | Date | |
---|---|---|---|
5cd9627aed | |||
21eee9d95f | |||
e164055f68 | |||
f50145a486 | |||
7831662724 | |||
3e941840ff | |||
a57d025489 | |||
7a51eaa727 | |||
6c40d75e47 | |||
0a0c257206 | |||
7019023cbc | |||
c48c182fe0 | |||
98be02898b | |||
e1bdeb2942 | |||
015d666a4d | |||
624c90a439 | |||
0fb26acccc | |||
ed315fb8a5 | |||
975d6f1ec4 | |||
7c9e2255be | |||
9f6818a6e5 | |||
f1c16b487a | |||
6512f39061 | |||
22b91afa69 | |||
77107ba124 | |||
fa4dec9fe9 | |||
215e887be0 | |||
0da6704fc2 | |||
812754edec | |||
964e19ab56 | |||
9dc624f5dc | |||
dad9ea3e07 | |||
2c00599a53 | |||
864b046500 | |||
d12d9dad40 | |||
2b3f1d3fc5 | |||
984396a21a | |||
4f95ee5209 | |||
c04567fad4 | |||
89c2fd3d28 | |||
5ab49c833d | |||
0e74204aad | |||
a37bd0b9a7 | |||
01d89acd13 | |||
e85466c1ae | |||
352297d361 | |||
6c2d6cff19 | |||
e540757279 | |||
a8bca24292 | |||
5f3c03d6a8 | |||
6b956a6951 | |||
8689370830 | |||
8ffa27b311 | |||
6334616b44 | |||
8bbc209950 | |||
9e1a67b950 | |||
de5422b1fd | |||
45f450fca5 | |||
9a3fd76b25 | |||
60fc6df407 | |||
2e4a6b7f92 | |||
5f90bd88da | |||
c575a85233 | |||
ea4703cb31 | |||
0471e54e5a | |||
6012c9fe3a | |||
f65539504f | |||
62083fcafd | |||
2e5a9cf119 | |||
a45b8bc9bc | |||
a05bd3c47e | |||
2a2c92f181 | |||
f54277364c | |||
de2d496e71 | |||
7b5bdd076d | |||
e0c17a2113 | |||
b107435a3f | |||
ef50277124 | |||
ba70dc4c13 | |||
934e420e36 | |||
d5e4617ab5 | |||
b5a17b501b | |||
2b18957365 | |||
4c71bf3d90 | |||
fdf4a5bc90 | |||
b7d9eba72b | |||
380475af32 | |||
f46dc31683 | |||
56810541f0 | |||
461eaca7e8 | |||
368bf2211f | |||
de8a623932 | |||
865a0186b6 |
3
.github/workflows/verify.yml
vendored
3
.github/workflows/verify.yml
vendored
@ -81,8 +81,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# workaround for https://github.com/actions/setup-python/issues/577
|
||||
brew install autoconf automake boost@1.83 ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd || brew link --overwrite python@3.12
|
||||
brew install autoconf automake boost ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
|
14
README.md
14
README.md
@ -1,3 +1,17 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`2831f5dc6`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
||||
| [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [13081](https://github.com/yuzu-emu/yuzu//pull/13081) | [`2786d34dd`](https://github.com/yuzu-emu/yuzu//pull/13081/files) | aoc: Migrate to use cmif serialization | [FearlessTobi](https://github.com/FearlessTobi/) | Yes |
|
||||
| [13096](https://github.com/yuzu-emu/yuzu//pull/13096) | [`0a8759057`](https://github.com/yuzu-emu/yuzu//pull/13096/files) | texture_cache: use two-pass collection for costly load resources | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [13122](https://github.com/yuzu-emu/yuzu//pull/13122) | [`505b3e4a7`](https://github.com/yuzu-emu/yuzu//pull/13122/files) | vk_rasterizer: flip scissor y on lower left origin mode | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [13142](https://github.com/yuzu-emu/yuzu//pull/13142) | [`0369c6587`](https://github.com/yuzu-emu/yuzu//pull/13142/files) | android: Play vibrations asynchronously | [t895](https://github.com/t895/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
7
externals/CMakeLists.txt
vendored
7
externals/CMakeLists.txt
vendored
@ -314,3 +314,10 @@ endif()
|
||||
if (NOT TARGET SimpleIni::SimpleIni)
|
||||
add_subdirectory(simpleini)
|
||||
endif()
|
||||
|
||||
# sse2neon
|
||||
if (ARCHITECTURE_arm64 AND NOT TARGET sse2neon)
|
||||
add_library(sse2neon INTERFACE)
|
||||
target_include_directories(sse2neon INTERFACE sse2neon)
|
||||
endif()
|
||||
|
||||
|
9282
externals/sse2neon/sse2neon.h
vendored
Normal file
9282
externals/sse2neon/sse2neon.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -121,6 +121,7 @@ else()
|
||||
-Wno-attributes
|
||||
-Wno-invalid-offsetof
|
||||
-Wno-unused-parameter
|
||||
-Wno-missing-field-initializers
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
|
||||
@ -164,6 +165,7 @@ else()
|
||||
|
||||
if (MINGW)
|
||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||
add_compile_options("-msse4.1")
|
||||
|
||||
if (MINGW_STATIC_BUILD)
|
||||
add_definitions(-DQT_STATICPLUGIN)
|
||||
|
@ -64,17 +64,17 @@ data class PlayerInput(
|
||||
fun hasMapping(): Boolean {
|
||||
var hasMapping = false
|
||||
buttons.forEach {
|
||||
if (it != "[empty]") {
|
||||
if (it != "[empty]" && it.isNotEmpty()) {
|
||||
hasMapping = true
|
||||
}
|
||||
}
|
||||
analogs.forEach {
|
||||
if (it != "[empty]") {
|
||||
if (it != "[empty]" && it.isNotEmpty()) {
|
||||
hasMapping = true
|
||||
}
|
||||
}
|
||||
motions.forEach {
|
||||
if (it != "[empty]") {
|
||||
if (it != "[empty]" && it.isNotEmpty()) {
|
||||
hasMapping = true
|
||||
}
|
||||
}
|
||||
|
@ -780,7 +780,7 @@ class SettingsFragmentPresenter(
|
||||
playerIndex: Int,
|
||||
paramName: String,
|
||||
stick: NativeAnalog,
|
||||
defaultValue: Int
|
||||
defaultValue: Float
|
||||
): AbstractIntSetting =
|
||||
object : AbstractIntSetting {
|
||||
val params get() = NativeInput.getStickParam(playerIndex, stick)
|
||||
@ -788,7 +788,7 @@ class SettingsFragmentPresenter(
|
||||
override val key = ""
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int =
|
||||
(params.get(paramName, 0.15f) * 100).toInt()
|
||||
(params.get(paramName, defaultValue) * 100).toInt()
|
||||
|
||||
override fun setInt(value: Int) {
|
||||
val tempParams = params
|
||||
@ -796,12 +796,12 @@ class SettingsFragmentPresenter(
|
||||
NativeInput.setStickParam(playerIndex, stick, tempParams)
|
||||
}
|
||||
|
||||
override val defaultValue = defaultValue
|
||||
override val defaultValue = (defaultValue * 100).toInt()
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
||||
getInt(needsGlobal).toString()
|
||||
|
||||
override fun reset() = setInt(defaultValue)
|
||||
override fun reset() = setInt(this.defaultValue)
|
||||
}
|
||||
|
||||
private fun getExtraStickSettings(
|
||||
@ -811,11 +811,11 @@ class SettingsFragmentPresenter(
|
||||
val stickIsController =
|
||||
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
|
||||
val modifierRangeSetting =
|
||||
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
|
||||
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
|
||||
val stickRangeSetting =
|
||||
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
|
||||
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
|
||||
val stickDeadzoneSetting =
|
||||
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
|
||||
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
|
||||
|
||||
val out = mutableListOf<SettingsItem>().apply {
|
||||
if (stickIsController) {
|
||||
|
@ -292,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {
|
||||
// Unload user input.
|
||||
m_system.HIDCore().UnloadInputDevices();
|
||||
|
||||
// Enable all controllers
|
||||
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||
|
||||
// Shutdown the main emulated process
|
||||
if (m_load_result == Core::SystemResultStatus::Success) {
|
||||
m_system.DetachDebugger();
|
||||
@ -665,7 +668,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
|
||||
ASSERT(user_id);
|
||||
|
||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1,
|
||||
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, 1,
|
||||
user_id->AsU128(), 0);
|
||||
|
||||
const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
|
||||
@ -833,8 +836,8 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
|
||||
FileSys::OpenMode::Read);
|
||||
|
||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
||||
program_id, user_id->AsU128(), 0);
|
||||
{}, vfsNandDir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, program_id,
|
||||
user_id->AsU128(), 0);
|
||||
return Common::Android::ToJString(env, user_save_data_path);
|
||||
}
|
||||
|
||||
|
@ -102,8 +102,50 @@ void ApplyControllerConfig(size_t player_index,
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<s32> GetSupportedStyles(int player_index) {
|
||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
||||
const auto npad_style_set = hid_core.GetSupportedStyleTag();
|
||||
std::vector<s32> supported_indexes;
|
||||
if (npad_style_set.fullkey == 1) {
|
||||
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Fullkey));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_dual == 1) {
|
||||
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_left == 1) {
|
||||
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_right == 1) {
|
||||
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight));
|
||||
}
|
||||
|
||||
if (player_index == 0 && npad_style_set.handheld == 1) {
|
||||
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld));
|
||||
}
|
||||
|
||||
if (npad_style_set.gamecube == 1) {
|
||||
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube));
|
||||
}
|
||||
|
||||
return supported_indexes;
|
||||
}
|
||||
|
||||
void ConnectController(size_t player_index, bool connected) {
|
||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
||||
ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) {
|
||||
auto supported_styles = GetSupportedStyles(player_index);
|
||||
auto controller_style = controller->GetNpadStyleIndex(true);
|
||||
auto style = std::find(supported_styles.begin(), supported_styles.end(),
|
||||
static_cast<int>(controller_style));
|
||||
if (style == supported_styles.end() && !supported_styles.empty()) {
|
||||
controller->SetNpadStyleIndex(
|
||||
static_cast<Core::HID::NpadStyleIndex>(supported_styles[0]));
|
||||
}
|
||||
});
|
||||
|
||||
if (player_index == 0) {
|
||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
@ -522,36 +564,10 @@ jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv
|
||||
|
||||
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
|
||||
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
||||
const auto npad_style_set = hid_core.GetSupportedStyleTag();
|
||||
std::vector<s32> supported_indexes;
|
||||
if (npad_style_set.fullkey == 1) {
|
||||
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_dual == 1) {
|
||||
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_left == 1) {
|
||||
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_right == 1) {
|
||||
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
|
||||
}
|
||||
|
||||
if (j_player_index == 0 && npad_style_set.handheld == 1) {
|
||||
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
|
||||
}
|
||||
|
||||
if (npad_style_set.gamecube == 1) {
|
||||
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
|
||||
}
|
||||
|
||||
jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
|
||||
env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
|
||||
supported_indexes.data());
|
||||
auto supported_styles = GetSupportedStyles(j_player_index);
|
||||
jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
|
||||
env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
|
||||
supported_styles.data());
|
||||
return j_supported_indexes;
|
||||
}
|
||||
|
||||
|
@ -73,16 +73,15 @@ void Manager::BufferReleaseAndRegister() {
|
||||
}
|
||||
}
|
||||
|
||||
u32 Manager::GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names,
|
||||
[[maybe_unused]] const u32 max_count,
|
||||
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
|
||||
[[maybe_unused]] const bool filter) {
|
||||
std::scoped_lock l{mutex};
|
||||
|
||||
LinkToManager();
|
||||
|
||||
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
||||
if (input_devices.size() > 1) {
|
||||
names.emplace_back("Uac");
|
||||
if (!input_devices.empty() && !names.empty()) {
|
||||
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -60,13 +60,11 @@ public:
|
||||
* Get a list of audio in device names.
|
||||
*
|
||||
* @param names - Output container to write names to.
|
||||
* @param max_count - Maximum number of device names to write. Unused
|
||||
* @param filter - Should the list be filtered? Unused.
|
||||
*
|
||||
* @return Number of names written.
|
||||
*/
|
||||
u32 GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, u32 max_count,
|
||||
bool filter);
|
||||
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
|
@ -146,7 +146,11 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
tags[released++] = tag;
|
||||
if (released < tags.size()) {
|
||||
tags[released] = tag;
|
||||
}
|
||||
|
||||
released++;
|
||||
|
||||
if (released >= tags.size()) {
|
||||
break;
|
||||
|
@ -28,8 +28,8 @@ OpusDecoder::~OpusDecoder() {
|
||||
}
|
||||
}
|
||||
|
||||
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size) {
|
||||
Result OpusDecoder::Initialize(const OpusParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||
shared_buffer_size = transfer_memory_size;
|
||||
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
||||
@ -59,7 +59,7 @@ Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
|
||||
Result OpusDecoder::Initialize(const OpusMultiStreamParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||
shared_buffer_size = transfer_memory_size;
|
||||
|
@ -22,10 +22,10 @@ public:
|
||||
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
|
||||
~OpusDecoder();
|
||||
|
||||
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size);
|
||||
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||
Result Initialize(const OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size);
|
||||
Result Initialize(const OpusMultiStreamParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size);
|
||||
Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
|
||||
std::span<const u8> input_data, std::span<u8> output_data, bool reset);
|
||||
Result SetContext([[maybe_unused]] std::span<const u8> context);
|
||||
|
@ -38,7 +38,7 @@ OpusDecoderManager::OpusDecoderManager(Core::System& system_)
|
||||
}
|
||||
}
|
||||
|
||||
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
|
||||
Result OpusDecoderManager::GetWorkBufferSize(const OpusParameters& params, u32& out_size) {
|
||||
OpusParametersEx ex{
|
||||
.sample_rate = params.sample_rate,
|
||||
.channel_count = params.channel_count,
|
||||
@ -47,11 +47,11 @@ Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_si
|
||||
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
|
||||
}
|
||||
|
||||
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
|
||||
Result OpusDecoderManager::GetWorkBufferSizeEx(const OpusParametersEx& params, u32& out_size) {
|
||||
R_RETURN(GetWorkBufferSizeExEx(params, out_size));
|
||||
}
|
||||
|
||||
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
|
||||
Result OpusDecoderManager::GetWorkBufferSizeExEx(const OpusParametersEx& params, u32& out_size) {
|
||||
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
||||
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
||||
|
||||
@ -63,8 +63,8 @@ Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64&
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
|
||||
u64& out_size) {
|
||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(const OpusMultiStreamParameters& params,
|
||||
u32& out_size) {
|
||||
OpusMultiStreamParametersEx ex{
|
||||
.sample_rate = params.sample_rate,
|
||||
.channel_count = params.channel_count,
|
||||
@ -76,13 +76,13 @@ Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParame
|
||||
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
|
||||
}
|
||||
|
||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
|
||||
u64& out_size) {
|
||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(
|
||||
const OpusMultiStreamParametersEx& params, u32& out_size) {
|
||||
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
|
||||
}
|
||||
|
||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
|
||||
u64& out_size) {
|
||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(
|
||||
const OpusMultiStreamParametersEx& params, u32& out_size) {
|
||||
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
||||
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
||||
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
|
||||
|
@ -22,17 +22,19 @@ public:
|
||||
return hardware_opus;
|
||||
}
|
||||
|
||||
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
|
||||
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
|
||||
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
|
||||
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
|
||||
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
||||
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
||||
Result GetWorkBufferSize(const OpusParameters& params, u32& out_size);
|
||||
Result GetWorkBufferSizeEx(const OpusParametersEx& params, u32& out_size);
|
||||
Result GetWorkBufferSizeExEx(const OpusParametersEx& params, u32& out_size);
|
||||
Result GetWorkBufferSizeForMultiStream(const OpusMultiStreamParameters& params, u32& out_size);
|
||||
Result GetWorkBufferSizeForMultiStreamEx(const OpusMultiStreamParametersEx& params,
|
||||
u32& out_size);
|
||||
Result GetWorkBufferSizeForMultiStreamExEx(const OpusMultiStreamParametersEx& params,
|
||||
u32& out_size);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
HardwareOpus hardware_opus;
|
||||
std::array<u64, MaxChannels> required_workbuffer_sizes{};
|
||||
std::array<u32, MaxChannels> required_workbuffer_sizes{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::OpusDecoder
|
||||
|
@ -42,7 +42,7 @@ HardwareOpus::HardwareOpus(Core::System& system_)
|
||||
opus_decoder.SetSharedMemory(shared_memory);
|
||||
}
|
||||
|
||||
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||
u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||
if (!opus_decoder.IsRunning()) {
|
||||
return 0;
|
||||
}
|
||||
@ -55,10 +55,10 @@ u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
|
||||
return 0;
|
||||
}
|
||||
return shared_memory.dsp_return_data[0];
|
||||
return static_cast<u32>(shared_memory.dsp_return_data[0]);
|
||||
}
|
||||
|
||||
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
u32 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
std::scoped_lock l{mutex};
|
||||
shared_memory.host_send_data[0] = total_stream_count;
|
||||
shared_memory.host_send_data[1] = stereo_stream_count;
|
||||
@ -70,7 +70,7 @@ u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 st
|
||||
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
|
||||
return 0;
|
||||
}
|
||||
return shared_memory.dsp_return_data[0];
|
||||
return static_cast<u32>(shared_memory.dsp_return_data[0]);
|
||||
}
|
||||
|
||||
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
||||
@ -94,8 +94,9 @@ Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count,
|
||||
|
||||
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||
u32 total_stream_count,
|
||||
u32 stereo_stream_count, void* mappings,
|
||||
void* buffer, u64 buffer_size) {
|
||||
u32 stereo_stream_count,
|
||||
const void* mappings, void* buffer,
|
||||
u64 buffer_size) {
|
||||
std::scoped_lock l{mutex};
|
||||
shared_memory.host_send_data[0] = (u64)buffer;
|
||||
shared_memory.host_send_data[1] = buffer_size;
|
||||
|
@ -16,14 +16,14 @@ class HardwareOpus {
|
||||
public:
|
||||
HardwareOpus(Core::System& system);
|
||||
|
||||
u64 GetWorkBufferSize(u32 channel);
|
||||
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
||||
u32 GetWorkBufferSize(u32 channel);
|
||||
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
||||
|
||||
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
||||
u64 buffer_size);
|
||||
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||
u32 totaL_stream_count, u32 stereo_stream_count,
|
||||
void* mappings, void* buffer, u64 buffer_size);
|
||||
const void* mappings, void* buffer, u64 buffer_size);
|
||||
Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
|
||||
Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
|
||||
Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
|
||||
|
@ -20,7 +20,7 @@ struct OpusParametersEx {
|
||||
/* 0x00 */ u32 sample_rate;
|
||||
/* 0x04 */ u32 channel_count;
|
||||
/* 0x08 */ bool use_large_frame_size;
|
||||
/* 0x09 */ INSERT_PADDING_BYTES(7);
|
||||
/* 0x09 */ INSERT_PADDING_BYTES_NOINIT(7);
|
||||
}; // size = 0x10
|
||||
static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!");
|
||||
|
||||
@ -40,7 +40,7 @@ struct OpusMultiStreamParametersEx {
|
||||
/* 0x08 */ u32 total_stream_count;
|
||||
/* 0x0C */ u32 stereo_stream_count;
|
||||
/* 0x10 */ bool use_large_frame_size;
|
||||
/* 0x11 */ INSERT_PADDING_BYTES(7);
|
||||
/* 0x11 */ INSERT_PADDING_BYTES_NOINIT(7);
|
||||
/* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings;
|
||||
}; // size = 0x118
|
||||
static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
|
||||
|
@ -36,8 +36,7 @@ AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id
|
||||
: output_sink{system.AudioCore().GetOutputSink()},
|
||||
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
|
||||
|
||||
u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
const size_t max_count) const {
|
||||
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
||||
std::span<const AudioDeviceName> names{};
|
||||
|
||||
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
|
||||
@ -46,19 +45,18 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
names = device_names;
|
||||
}
|
||||
|
||||
const u32 out_count{static_cast<u32>(std::min(max_count, names.size()))};
|
||||
const u32 out_count{static_cast<u32>(std::min(out_buffer.size(), names.size()))};
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
out_buffer.push_back(names[i]);
|
||||
out_buffer[i] = names[i];
|
||||
}
|
||||
return out_count;
|
||||
}
|
||||
|
||||
u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
const size_t max_count) const {
|
||||
const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
|
||||
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
||||
const u32 out_count{static_cast<u32>(std::min(out_buffer.size(), output_device_names.size()))};
|
||||
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
out_buffer.push_back(output_device_names[i]);
|
||||
out_buffer[i] = output_device_names[i];
|
||||
}
|
||||
return out_count;
|
||||
}
|
||||
|
@ -36,20 +36,18 @@ public:
|
||||
* Get a list of the available output devices.
|
||||
*
|
||||
* @param out_buffer - Output buffer to write the available device names.
|
||||
* @param max_count - Maximum number of devices to write (count of out_buffer).
|
||||
* @return Number of device names written.
|
||||
*/
|
||||
u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
|
||||
u32 ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const;
|
||||
|
||||
/**
|
||||
* Get a list of the available output devices.
|
||||
* Different to above somehow...
|
||||
*
|
||||
* @param out_buffer - Output buffer to write the available device names.
|
||||
* @param max_count - Maximum number of devices to write (count of out_buffer).
|
||||
* @return Number of device names written.
|
||||
*/
|
||||
u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
|
||||
u32 ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const;
|
||||
|
||||
/**
|
||||
* Set the volume of all streams in the backend sink.
|
||||
|
@ -17,9 +17,8 @@ Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* ren
|
||||
|
||||
Result Renderer::Initialize(const AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory,
|
||||
const u64 transfer_memory_size, const u32 process_handle,
|
||||
Kernel::KProcess& process, const u64 applet_resource_user_id,
|
||||
const s32 session_id) {
|
||||
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
|
||||
const u64 applet_resource_user_id, const s32 session_id) {
|
||||
if (params.execution_mode == ExecutionMode::Auto) {
|
||||
if (!manager.AddSystem(system)) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
@ -30,7 +29,7 @@ Result Renderer::Initialize(const AudioRendererParameterInternal& params,
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, process,
|
||||
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
|
||||
applet_resource_user_id, session_id);
|
||||
|
||||
return ResultSuccess;
|
||||
|
@ -38,14 +38,14 @@ public:
|
||||
* @param params - Input parameters to initialize the system with.
|
||||
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
|
||||
* @param transfer_memory_size - Size of the transfer memory. Unused.
|
||||
* @param process_handle - Process handle, also used for memory. Unused.
|
||||
* @param process_handle - Process handle, also used for memory.
|
||||
* @param applet_resource_user_id - Applet id for this renderer. Unused.
|
||||
* @param session_id - Session id of this renderer.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result Initialize(const AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle, Kernel::KProcess& process, u64 applet_resource_user_id,
|
||||
Kernel::KProcess* process_handle, u64 applet_resource_user_id,
|
||||
s32 session_id);
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@
|
||||
namespace AudioCore::Renderer {
|
||||
|
||||
InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_,
|
||||
const u32 process_handle_, BehaviorInfo& behaviour_)
|
||||
Kernel::KProcess* process_handle_, BehaviorInfo& behaviour_)
|
||||
: input{input_.data() + sizeof(UpdateDataHeader)},
|
||||
input_origin{input_}, output{output_.data() + sizeof(UpdateDataHeader)},
|
||||
output_origin{output_}, in_header{reinterpret_cast<const UpdateDataHeader*>(
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace AudioCore::Renderer {
|
||||
class BehaviorInfo;
|
||||
class VoiceContext;
|
||||
@ -39,8 +43,8 @@ class InfoUpdater {
|
||||
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has the wrong size!");
|
||||
|
||||
public:
|
||||
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output, u32 process_handle,
|
||||
BehaviorInfo& behaviour);
|
||||
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output,
|
||||
Kernel::KProcess* process_handle, BehaviorInfo& behaviour);
|
||||
|
||||
/**
|
||||
* Update the voice channel resources.
|
||||
@ -197,7 +201,7 @@ private:
|
||||
/// Expected output size, see CheckConsumedSize
|
||||
u64 expected_output_size;
|
||||
/// Unused
|
||||
u32 process_handle;
|
||||
Kernel::KProcess* process_handle;
|
||||
/// Behaviour
|
||||
BehaviorInfo& behaviour;
|
||||
};
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
namespace AudioCore::Renderer {
|
||||
|
||||
PoolMapper::PoolMapper(u32 process_handle_, bool force_map_)
|
||||
PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, bool force_map_)
|
||||
: process_handle{process_handle_}, force_map{force_map_} {}
|
||||
|
||||
PoolMapper::PoolMapper(u32 process_handle_, std::span<MemoryPoolInfo> pool_infos_, u32 pool_count_,
|
||||
bool force_map_)
|
||||
PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, std::span<MemoryPoolInfo> pool_infos_,
|
||||
u32 pool_count_, bool force_map_)
|
||||
: process_handle{process_handle_}, pool_infos{pool_infos_.data()},
|
||||
pool_count{pool_count_}, force_map{force_map_} {}
|
||||
|
||||
@ -106,15 +106,17 @@ bool PoolMapper::IsForceMapEnabled() const {
|
||||
return force_map;
|
||||
}
|
||||
|
||||
u32 PoolMapper::GetProcessHandle(const MemoryPoolInfo* pool) const {
|
||||
Kernel::KProcess* PoolMapper::GetProcessHandle(const MemoryPoolInfo* pool) const {
|
||||
switch (pool->GetLocation()) {
|
||||
case MemoryPoolInfo::Location::CPU:
|
||||
return process_handle;
|
||||
case MemoryPoolInfo::Location::DSP:
|
||||
return Kernel::Svc::CurrentProcess;
|
||||
// return Kernel::Svc::CurrentProcess;
|
||||
return nullptr;
|
||||
}
|
||||
LOG_WARNING(Service_Audio, "Invalid MemoryPoolInfo location!");
|
||||
return Kernel::Svc::CurrentProcess;
|
||||
// return Kernel::Svc::CurrentProcess;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PoolMapper::Map([[maybe_unused]] const u32 handle, [[maybe_unused]] const CpuAddr cpu_addr,
|
||||
@ -147,14 +149,14 @@ bool PoolMapper::Unmap([[maybe_unused]] const u32 handle, [[maybe_unused]] const
|
||||
}
|
||||
|
||||
bool PoolMapper::Unmap(MemoryPoolInfo& pool) const {
|
||||
[[maybe_unused]] u32 handle{0};
|
||||
[[maybe_unused]] Kernel::KProcess* handle{};
|
||||
|
||||
switch (pool.GetLocation()) {
|
||||
case MemoryPoolInfo::Location::CPU:
|
||||
handle = process_handle;
|
||||
break;
|
||||
case MemoryPoolInfo::Location::DSP:
|
||||
handle = Kernel::Svc::CurrentProcess;
|
||||
// handle = Kernel::Svc::CurrentProcess;
|
||||
break;
|
||||
}
|
||||
// nn::audio::dsp::UnmapUserPointer(handle, pool->cpu_address, pool->size);
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace AudioCore::Renderer {
|
||||
class AddressInfo;
|
||||
|
||||
@ -18,9 +22,9 @@ class AddressInfo;
|
||||
*/
|
||||
class PoolMapper {
|
||||
public:
|
||||
explicit PoolMapper(u32 process_handle, bool force_map);
|
||||
explicit PoolMapper(u32 process_handle, std::span<MemoryPoolInfo> pool_infos, u32 pool_count,
|
||||
bool force_map);
|
||||
explicit PoolMapper(Kernel::KProcess* process_handle, bool force_map);
|
||||
explicit PoolMapper(Kernel::KProcess* process_handle, std::span<MemoryPoolInfo> pool_infos,
|
||||
u32 pool_count, bool force_map);
|
||||
|
||||
/**
|
||||
* Clear the usage state for all given pools.
|
||||
@ -98,7 +102,7 @@ public:
|
||||
* @return CurrentProcessHandle if location == DSP,
|
||||
* the PoolMapper's process_handle if location == CPU
|
||||
*/
|
||||
u32 GetProcessHandle(const MemoryPoolInfo* pool) const;
|
||||
Kernel::KProcess* GetProcessHandle(const MemoryPoolInfo* pool) const;
|
||||
|
||||
/**
|
||||
* Map the given region with the given handle. This is a no-op.
|
||||
@ -167,7 +171,7 @@ public:
|
||||
|
||||
private:
|
||||
/// Process handle for this mapper, used when location == CPU
|
||||
u32 process_handle;
|
||||
Kernel::KProcess* process_handle{};
|
||||
/// List of memory pools assigned to this mapper
|
||||
MemoryPoolInfo* pool_infos{};
|
||||
/// The number of pools
|
||||
|
@ -102,8 +102,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||
|
||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle_, Kernel::KProcess& process_,
|
||||
u64 applet_resource_user_id_, s32 session_id_) {
|
||||
Kernel::KProcess* process_handle_, u64 applet_resource_user_id_,
|
||||
s32 session_id_) {
|
||||
if (!CheckValidRevision(params.revision)) {
|
||||
return Service::Audio::ResultInvalidRevision;
|
||||
}
|
||||
@ -119,7 +119,6 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||
behavior.SetUserLibRevision(params.revision);
|
||||
|
||||
process_handle = process_handle_;
|
||||
process = &process_;
|
||||
applet_resource_user_id = applet_resource_user_id_;
|
||||
session_id = session_id_;
|
||||
|
||||
@ -132,7 +131,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||
render_device = params.rendering_device;
|
||||
execution_mode = params.execution_mode;
|
||||
|
||||
process->GetMemory().ZeroBlock(transfer_memory->GetSourceAddress(), transfer_memory_size);
|
||||
process_handle->GetMemory().ZeroBlock(transfer_memory->GetSourceAddress(),
|
||||
transfer_memory_size);
|
||||
|
||||
// Note: We're not actually using the transfer memory because it's a pain to code for.
|
||||
// Allocate the memory normally instead and hope the game doesn't try to read anything back
|
||||
@ -616,7 +616,7 @@ void System::SendCommandToDsp() {
|
||||
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
||||
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
||||
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
|
||||
applet_resource_user_id, process,
|
||||
applet_resource_user_id, process_handle,
|
||||
reset_command_buffers);
|
||||
reset_command_buffers = false;
|
||||
command_buffer_size = command_size;
|
||||
|
@ -74,14 +74,14 @@ public:
|
||||
* @param params - Input parameters to initialize the system with.
|
||||
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
|
||||
* @param transfer_memory_size - Size of the transfer memory. Unused.
|
||||
* @param process_handle - Process handle, also used for memory. Unused.
|
||||
* @param process_handle - Process handle, also used for memory.
|
||||
* @param applet_resource_user_id - Applet id for this renderer. Unused.
|
||||
* @param session_id - Session id of this renderer.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result Initialize(const AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle, Kernel::KProcess& process, u64 applet_resource_user_id,
|
||||
Kernel::KProcess* process_handle, u64 applet_resource_user_id,
|
||||
s32 session_id);
|
||||
|
||||
/**
|
||||
@ -278,9 +278,7 @@ private:
|
||||
/// Does what locks do
|
||||
std::mutex lock{};
|
||||
/// Process this audio render is operating within, used for memory reads/writes.
|
||||
Kernel::KProcess* process{};
|
||||
/// Handle for the process for this system, unused
|
||||
u32 process_handle{};
|
||||
Kernel::KProcess* process_handle{};
|
||||
/// Applet resource id for this system, unused
|
||||
u64 applet_resource_user_id{};
|
||||
/// Controls performance input and output
|
||||
|
@ -67,9 +67,13 @@ public:
|
||||
oboe::AudioStreamBuilder builder;
|
||||
|
||||
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
|
||||
ASSERT(result == oboe::Result::OK);
|
||||
if (result == oboe::Result::OK) {
|
||||
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
|
||||
}
|
||||
|
||||
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
|
||||
LOG_ERROR(Audio_Sink, "Failed to open {} stream. Using default channel count 2",
|
||||
direction == oboe::Direction::Output ? "output" : "input");
|
||||
return 2;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
|
@ -384,6 +384,12 @@ struct Values {
|
||||
AstcRecompression::Bc3,
|
||||
"astc_recompression",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
|
||||
VramUsageMode::Conservative,
|
||||
VramUsageMode::Conservative,
|
||||
VramUsageMode::Aggressive,
|
||||
"vram_usage_mode",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> async_presentation{linkage,
|
||||
#ifdef ANDROID
|
||||
true,
|
||||
|
@ -122,6 +122,8 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
|
||||
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||
|
||||
ENUM(VramUsageMode, Conservative, Aggressive);
|
||||
|
||||
ENUM(RendererBackend, OpenGL, Vulkan, Null);
|
||||
|
||||
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
|
||||
|
@ -38,6 +38,10 @@ std::string StringFromBuffer(std::span<const u8> data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(std::span<const char> data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
|
||||
// Turns " hej " into "hej". Also handles tabs.
|
||||
std::string StripSpaces(const std::string& str) {
|
||||
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
||||
|
@ -19,6 +19,7 @@ namespace Common {
|
||||
[[nodiscard]] std::string ToUpper(std::string str);
|
||||
|
||||
[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
|
||||
[[nodiscard]] std::string StringFromBuffer(std::span<const char> data);
|
||||
|
||||
[[nodiscard]] std::string StripSpaces(const std::string& s);
|
||||
[[nodiscard]] std::string StripQuotes(const std::string& s);
|
||||
|
@ -58,9 +58,14 @@ add_library(core STATIC
|
||||
file_sys/fs_operate_range.h
|
||||
file_sys/fs_path.h
|
||||
file_sys/fs_path_utility.h
|
||||
file_sys/fs_save_data_types.h
|
||||
file_sys/fs_string_util.h
|
||||
file_sys/fsa/fs_i_directory.h
|
||||
file_sys/fsa/fs_i_file.h
|
||||
file_sys/fsa/fs_i_filesystem.h
|
||||
file_sys/fsmitm_romfsbuild.cpp
|
||||
file_sys/fsmitm_romfsbuild.h
|
||||
file_sys/fssrv/fssrv_sf_path.h
|
||||
file_sys/fssystem/fs_i_storage.h
|
||||
file_sys/fssystem/fs_types.h
|
||||
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
|
||||
@ -481,31 +486,43 @@ add_library(core STATIC
|
||||
hle/service/am/service/system_applet_proxy.h
|
||||
hle/service/am/service/window_controller.cpp
|
||||
hle/service/am/service/window_controller.h
|
||||
hle/service/aoc/aoc_u.cpp
|
||||
hle/service/aoc/aoc_u.h
|
||||
hle/service/aoc/addon_content_manager.cpp
|
||||
hle/service/aoc/addon_content_manager.h
|
||||
hle/service/aoc/purchase_event_manager.cpp
|
||||
hle/service/aoc/purchase_event_manager.h
|
||||
hle/service/apm/apm.cpp
|
||||
hle/service/apm/apm.h
|
||||
hle/service/apm/apm_controller.cpp
|
||||
hle/service/apm/apm_controller.h
|
||||
hle/service/apm/apm_interface.cpp
|
||||
hle/service/apm/apm_interface.h
|
||||
hle/service/audio/audin_u.cpp
|
||||
hle/service/audio/audin_u.h
|
||||
hle/service/audio/audio.cpp
|
||||
hle/service/audio/audio.h
|
||||
hle/service/audio/audio_controller.cpp
|
||||
hle/service/audio/audio_controller.h
|
||||
hle/service/audio/audout_u.cpp
|
||||
hle/service/audio/audout_u.h
|
||||
hle/service/audio/audrec_a.cpp
|
||||
hle/service/audio/audrec_a.h
|
||||
hle/service/audio/audrec_u.cpp
|
||||
hle/service/audio/audrec_u.h
|
||||
hle/service/audio/audren_u.cpp
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/audio_device.cpp
|
||||
hle/service/audio/audio_device.h
|
||||
hle/service/audio/audio_in_manager.cpp
|
||||
hle/service/audio/audio_in_manager.h
|
||||
hle/service/audio/audio_in.cpp
|
||||
hle/service/audio/audio_in.h
|
||||
hle/service/audio/audio_out_manager.cpp
|
||||
hle/service/audio/audio_out_manager.h
|
||||
hle/service/audio/audio_out.cpp
|
||||
hle/service/audio/audio_out.h
|
||||
hle/service/audio/audio_renderer_manager.cpp
|
||||
hle/service/audio/audio_renderer_manager.h
|
||||
hle/service/audio/audio_renderer.cpp
|
||||
hle/service/audio/audio_renderer.h
|
||||
hle/service/audio/audio.cpp
|
||||
hle/service/audio/audio.h
|
||||
hle/service/audio/errors.h
|
||||
hle/service/audio/hwopus.cpp
|
||||
hle/service/audio/hwopus.h
|
||||
hle/service/audio/final_output_recorder_manager_for_applet.cpp
|
||||
hle/service/audio/final_output_recorder_manager_for_applet.h
|
||||
hle/service/audio/final_output_recorder_manager.cpp
|
||||
hle/service/audio/final_output_recorder_manager.h
|
||||
hle/service/audio/hardware_opus_decoder_manager.cpp
|
||||
hle/service/audio/hardware_opus_decoder_manager.h
|
||||
hle/service/audio/hardware_opus_decoder.cpp
|
||||
hle/service/audio/hardware_opus_decoder.h
|
||||
hle/service/bcat/backend/backend.cpp
|
||||
hle/service/bcat/backend/backend.h
|
||||
hle/service/bcat/bcat.cpp
|
||||
@ -595,6 +612,10 @@ add_library(core STATIC
|
||||
hle/service/filesystem/fsp/fs_i_file.h
|
||||
hle/service/filesystem/fsp/fs_i_filesystem.cpp
|
||||
hle/service/filesystem/fsp/fs_i_filesystem.h
|
||||
hle/service/filesystem/fsp/fs_i_multi_commit_manager.cpp
|
||||
hle/service/filesystem/fsp/fs_i_multi_commit_manager.h
|
||||
hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp
|
||||
hle/service/filesystem/fsp/fs_i_save_data_info_reader.h
|
||||
hle/service/filesystem/fsp/fs_i_storage.cpp
|
||||
hle/service/filesystem/fsp/fs_i_storage.h
|
||||
hle/service/filesystem/fsp/fsp_ldr.cpp
|
||||
@ -603,7 +624,9 @@ add_library(core STATIC
|
||||
hle/service/filesystem/fsp/fsp_pr.h
|
||||
hle/service/filesystem/fsp/fsp_srv.cpp
|
||||
hle/service/filesystem/fsp/fsp_srv.h
|
||||
hle/service/filesystem/fsp/fsp_util.h
|
||||
hle/service/filesystem/fsp/fsp_types.h
|
||||
hle/service/filesystem/fsp/save_data_transfer_prohibiter.cpp
|
||||
hle/service/filesystem/fsp/save_data_transfer_prohibiter.h
|
||||
hle/service/filesystem/romfs_controller.cpp
|
||||
hle/service/filesystem/romfs_controller.h
|
||||
hle/service/filesystem/save_data_controller.cpp
|
||||
@ -860,8 +883,20 @@ add_library(core STATIC
|
||||
hle/service/nvnflinger/ui/graphic_buffer.cpp
|
||||
hle/service/nvnflinger/ui/graphic_buffer.h
|
||||
hle/service/nvnflinger/window.h
|
||||
hle/service/olsc/daemon_controller.cpp
|
||||
hle/service/olsc/daemon_controller.h
|
||||
hle/service/olsc/native_handle_holder.cpp
|
||||
hle/service/olsc/native_handle_holder.h
|
||||
hle/service/olsc/olsc_service_for_application.cpp
|
||||
hle/service/olsc/olsc_service_for_application.h
|
||||
hle/service/olsc/olsc_service_for_system_service.cpp
|
||||
hle/service/olsc/olsc_service_for_system_service.h
|
||||
hle/service/olsc/olsc.cpp
|
||||
hle/service/olsc/olsc.h
|
||||
hle/service/olsc/remote_storage_controller.cpp
|
||||
hle/service/olsc/remote_storage_controller.h
|
||||
hle/service/olsc/transfer_task_list_controller.cpp
|
||||
hle/service/olsc/transfer_task_list_controller.h
|
||||
hle/service/omm/omm.cpp
|
||||
hle/service/omm/omm.h
|
||||
hle/service/omm/operation_mode_manager.cpp
|
||||
@ -881,16 +916,35 @@ add_library(core STATIC
|
||||
hle/service/os/mutex.h
|
||||
hle/service/pcie/pcie.cpp
|
||||
hle/service/pcie/pcie.h
|
||||
hle/service/pctl/parental_control_service_factory.cpp
|
||||
hle/service/pctl/parental_control_service_factory.h
|
||||
hle/service/pctl/parental_control_service.cpp
|
||||
hle/service/pctl/parental_control_service.h
|
||||
hle/service/pctl/pctl.cpp
|
||||
hle/service/pctl/pctl.h
|
||||
hle/service/pctl/pctl_module.cpp
|
||||
hle/service/pctl/pctl_module.h
|
||||
hle/service/pctl/pctl_results.h
|
||||
hle/service/pctl/pctl_types.h
|
||||
hle/service/pcv/pcv.cpp
|
||||
hle/service/pcv/pcv.h
|
||||
hle/service/pm/pm.cpp
|
||||
hle/service/pm/pm.h
|
||||
hle/service/prepo/prepo.cpp
|
||||
hle/service/prepo/prepo.h
|
||||
hle/service/psc/ovln/ovln_types.h
|
||||
hle/service/psc/ovln/receiver_service.cpp
|
||||
hle/service/psc/ovln/receiver_service.h
|
||||
hle/service/psc/ovln/receiver.cpp
|
||||
hle/service/psc/ovln/receiver.h
|
||||
hle/service/psc/ovln/sender_service.cpp
|
||||
hle/service/psc/ovln/sender_service.h
|
||||
hle/service/psc/ovln/sender.cpp
|
||||
hle/service/psc/ovln/sender.h
|
||||
hle/service/psc/pm_control.cpp
|
||||
hle/service/psc/pm_control.h
|
||||
hle/service/psc/pm_module.cpp
|
||||
hle/service/psc/pm_module.h
|
||||
hle/service/psc/pm_service.cpp
|
||||
hle/service/psc/pm_service.h
|
||||
hle/service/psc/psc.cpp
|
||||
hle/service/psc/psc.h
|
||||
hle/service/psc/time/alarms.cpp
|
||||
|
@ -43,6 +43,8 @@ public:
|
||||
DeviceMemoryManager(const DeviceMemory& device_memory);
|
||||
~DeviceMemoryManager();
|
||||
|
||||
static constexpr bool HAS_FLUSH_INVALIDATION = true;
|
||||
|
||||
void BindInterface(DeviceInterface* device_inter);
|
||||
|
||||
DAddr Allocate(size_t size);
|
||||
|
@ -522,13 +522,17 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
auto* memory_device_inter = registered_processes[asid.id];
|
||||
const auto release_pending = [&] {
|
||||
if (uncache_bytes > 0) {
|
||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
||||
uncache_bytes, false);
|
||||
if (memory_device_inter != nullptr) {
|
||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
||||
uncache_bytes, false);
|
||||
}
|
||||
uncache_bytes = 0;
|
||||
}
|
||||
if (cache_bytes > 0) {
|
||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
|
||||
cache_bytes, true);
|
||||
if (memory_device_inter != nullptr) {
|
||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
|
||||
cache_bytes, true);
|
||||
}
|
||||
cache_bytes = 0;
|
||||
}
|
||||
};
|
||||
|
@ -23,6 +23,8 @@ enum class OpenDirectoryMode : u64 {
|
||||
File = (1 << 1),
|
||||
|
||||
All = (Directory | File),
|
||||
|
||||
NotRequireFileSize = (1ULL << 31),
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
|
||||
|
||||
@ -36,4 +38,29 @@ enum class CreateOption : u8 {
|
||||
BigFile = (1 << 0),
|
||||
};
|
||||
|
||||
struct FileSystemAttribute {
|
||||
u8 dir_entry_name_length_max_defined;
|
||||
u8 file_entry_name_length_max_defined;
|
||||
u8 dir_path_name_length_max_defined;
|
||||
u8 file_path_name_length_max_defined;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x5);
|
||||
u8 utf16_dir_entry_name_length_max_defined;
|
||||
u8 utf16_file_entry_name_length_max_defined;
|
||||
u8 utf16_dir_path_name_length_max_defined;
|
||||
u8 utf16_file_path_name_length_max_defined;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x18);
|
||||
s32 dir_entry_name_length_max;
|
||||
s32 file_entry_name_length_max;
|
||||
s32 dir_path_name_length_max;
|
||||
s32 file_path_name_length_max;
|
||||
INSERT_PADDING_WORDS_NOINIT(0x5);
|
||||
s32 utf16_dir_entry_name_length_max;
|
||||
s32 utf16_file_entry_name_length_max;
|
||||
s32 utf16_dir_path_name_length_max;
|
||||
s32 utf16_file_path_name_length_max;
|
||||
INSERT_PADDING_WORDS_NOINIT(0x18);
|
||||
INSERT_PADDING_WORDS_NOINIT(0x1);
|
||||
};
|
||||
static_assert(sizeof(FileSystemAttribute) == 0xC0, "FileSystemAttribute has incorrect size");
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -10,7 +10,7 @@ namespace FileSys {
|
||||
|
||||
constexpr size_t RequiredAlignment = alignof(u64);
|
||||
|
||||
void* AllocateUnsafe(size_t size) {
|
||||
inline void* AllocateUnsafe(size_t size) {
|
||||
// Allocate
|
||||
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
|
||||
|
||||
@ -21,16 +21,16 @@ void* AllocateUnsafe(size_t size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void DeallocateUnsafe(void* ptr, size_t size) {
|
||||
inline void DeallocateUnsafe(void* ptr, size_t size) {
|
||||
// Deallocate the pointer
|
||||
::operator delete(ptr, std::align_val_t{RequiredAlignment});
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
inline void* Allocate(size_t size) {
|
||||
return AllocateUnsafe(size);
|
||||
}
|
||||
|
||||
void Deallocate(void* ptr, size_t size) {
|
||||
inline void Deallocate(void* ptr, size_t size) {
|
||||
// If the pointer is non-null, deallocate it
|
||||
if (ptr != nullptr) {
|
||||
DeallocateUnsafe(ptr, size);
|
||||
|
@ -381,7 +381,7 @@ public:
|
||||
|
||||
// Check that it's possible for us to remove a child
|
||||
auto* p = m_write_buffer.Get();
|
||||
s32 len = std::strlen(p);
|
||||
s32 len = static_cast<s32>(std::strlen(p));
|
||||
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
|
||||
|
||||
// Handle a trailing separator
|
||||
|
@ -426,9 +426,10 @@ public:
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size,
|
||||
bool is_windows_path, bool is_drive_relative_path,
|
||||
bool allow_all_characters = false) {
|
||||
static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
|
||||
size_t max_out_size, bool is_windows_path,
|
||||
bool is_drive_relative_path,
|
||||
bool allow_all_characters = false) {
|
||||
// Use StringTraits names for remainder of scope
|
||||
using namespace StringTraits;
|
||||
|
||||
|
188
src/core/file_sys/fs_save_data_types.h
Normal file
188
src/core/file_sys/fs_save_data_types.h
Normal file
@ -0,0 +1,188 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
using SaveDataId = u64;
|
||||
using SystemSaveDataId = u64;
|
||||
using SystemBcatSaveDataId = SystemSaveDataId;
|
||||
using ProgramId = u64;
|
||||
|
||||
enum class SaveDataSpaceId : u8 {
|
||||
System = 0,
|
||||
User = 1,
|
||||
SdSystem = 2,
|
||||
Temporary = 3,
|
||||
SdUser = 4,
|
||||
|
||||
ProperSystem = 100,
|
||||
SafeMode = 101,
|
||||
};
|
||||
|
||||
enum class SaveDataType : u8 {
|
||||
System = 0,
|
||||
Account = 1,
|
||||
Bcat = 2,
|
||||
Device = 3,
|
||||
Temporary = 4,
|
||||
Cache = 5,
|
||||
SystemBcat = 6,
|
||||
};
|
||||
|
||||
enum class SaveDataRank : u8 {
|
||||
Primary = 0,
|
||||
Secondary = 1,
|
||||
};
|
||||
|
||||
struct SaveDataSize {
|
||||
u64 normal;
|
||||
u64 journal;
|
||||
};
|
||||
static_assert(sizeof(SaveDataSize) == 0x10, "SaveDataSize has invalid size.");
|
||||
|
||||
using UserId = u128;
|
||||
static_assert(std::is_trivially_copyable_v<UserId>, "Data type must be trivially copyable.");
|
||||
static_assert(sizeof(UserId) == 0x10, "UserId has invalid size.");
|
||||
|
||||
constexpr inline SystemSaveDataId InvalidSystemSaveDataId = 0;
|
||||
constexpr inline UserId InvalidUserId = {};
|
||||
|
||||
enum class SaveDataFlags : u32 {
|
||||
None = (0 << 0),
|
||||
KeepAfterResettingSystemSaveData = (1 << 0),
|
||||
KeepAfterRefurbishment = (1 << 1),
|
||||
KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2),
|
||||
NeedsSecureDelete = (1 << 3),
|
||||
};
|
||||
|
||||
enum class SaveDataMetaType : u8 {
|
||||
None = 0,
|
||||
Thumbnail = 1,
|
||||
ExtensionContext = 2,
|
||||
};
|
||||
|
||||
struct SaveDataMetaInfo {
|
||||
u32 size;
|
||||
SaveDataMetaType type;
|
||||
INSERT_PADDING_BYTES(0xB);
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<SaveDataMetaInfo>,
|
||||
"Data type must be trivially copyable.");
|
||||
static_assert(sizeof(SaveDataMetaInfo) == 0x10, "SaveDataMetaInfo has invalid size.");
|
||||
|
||||
struct SaveDataCreationInfo {
|
||||
s64 size;
|
||||
s64 journal_size;
|
||||
s64 block_size;
|
||||
u64 owner_id;
|
||||
u32 flags;
|
||||
SaveDataSpaceId space_id;
|
||||
bool pseudo;
|
||||
INSERT_PADDING_BYTES(0x1A);
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<SaveDataCreationInfo>,
|
||||
"Data type must be trivially copyable.");
|
||||
static_assert(sizeof(SaveDataCreationInfo) == 0x40, "SaveDataCreationInfo has invalid size.");
|
||||
|
||||
struct SaveDataAttribute {
|
||||
ProgramId program_id;
|
||||
UserId user_id;
|
||||
SystemSaveDataId system_save_data_id;
|
||||
SaveDataType type;
|
||||
SaveDataRank rank;
|
||||
u16 index;
|
||||
INSERT_PADDING_BYTES(0x1C);
|
||||
|
||||
static constexpr SaveDataAttribute Make(ProgramId program_id, SaveDataType type, UserId user_id,
|
||||
SystemSaveDataId system_save_data_id, u16 index,
|
||||
SaveDataRank rank) {
|
||||
return {
|
||||
.program_id = program_id,
|
||||
.user_id = user_id,
|
||||
.system_save_data_id = system_save_data_id,
|
||||
.type = type,
|
||||
.rank = rank,
|
||||
.index = index,
|
||||
};
|
||||
}
|
||||
|
||||
static constexpr SaveDataAttribute Make(ProgramId program_id, SaveDataType type, UserId user_id,
|
||||
SystemSaveDataId system_save_data_id, u16 index) {
|
||||
return Make(program_id, type, user_id, system_save_data_id, index, SaveDataRank::Primary);
|
||||
}
|
||||
|
||||
static constexpr SaveDataAttribute Make(ProgramId program_id, SaveDataType type, UserId user_id,
|
||||
SystemSaveDataId system_save_data_id) {
|
||||
return Make(program_id, type, user_id, system_save_data_id, 0, SaveDataRank::Primary);
|
||||
}
|
||||
|
||||
std::string DebugInfo() const {
|
||||
return fmt::format(
|
||||
"[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, "
|
||||
"rank={}, index={}]",
|
||||
program_id, user_id[1], user_id[0], system_save_data_id, static_cast<u8>(type),
|
||||
static_cast<u8>(rank), index);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(SaveDataAttribute) == 0x40);
|
||||
static_assert(std::is_trivially_destructible<SaveDataAttribute>::value);
|
||||
|
||||
constexpr inline bool operator<(const SaveDataAttribute& lhs, const SaveDataAttribute& rhs) {
|
||||
return std::tie(lhs.program_id, lhs.user_id, lhs.system_save_data_id, lhs.index, lhs.rank) <
|
||||
std::tie(rhs.program_id, rhs.user_id, rhs.system_save_data_id, rhs.index, rhs.rank);
|
||||
}
|
||||
|
||||
constexpr inline bool operator==(const SaveDataAttribute& lhs, const SaveDataAttribute& rhs) {
|
||||
return std::tie(lhs.program_id, lhs.user_id, lhs.system_save_data_id, lhs.type, lhs.rank,
|
||||
lhs.index) == std::tie(rhs.program_id, rhs.user_id, rhs.system_save_data_id,
|
||||
rhs.type, rhs.rank, rhs.index);
|
||||
}
|
||||
|
||||
constexpr inline bool operator!=(const SaveDataAttribute& lhs, const SaveDataAttribute& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
struct SaveDataExtraData {
|
||||
SaveDataAttribute attr;
|
||||
u64 owner_id;
|
||||
s64 timestamp;
|
||||
u32 flags;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
s64 available_size;
|
||||
s64 journal_size;
|
||||
s64 commit_id;
|
||||
INSERT_PADDING_BYTES(0x190);
|
||||
};
|
||||
static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has invalid size.");
|
||||
static_assert(std::is_trivially_copyable_v<SaveDataExtraData>,
|
||||
"Data type must be trivially copyable.");
|
||||
|
||||
struct SaveDataFilter {
|
||||
bool use_program_id;
|
||||
bool use_save_data_type;
|
||||
bool use_user_id;
|
||||
bool use_save_data_id;
|
||||
bool use_index;
|
||||
SaveDataRank rank;
|
||||
SaveDataAttribute attribute;
|
||||
};
|
||||
static_assert(sizeof(SaveDataFilter) == 0x48, "SaveDataFilter has invalid size.");
|
||||
static_assert(std::is_trivially_copyable_v<SaveDataFilter>,
|
||||
"Data type must be trivially copyable.");
|
||||
|
||||
struct HashSalt {
|
||||
static constexpr size_t Size = 32;
|
||||
|
||||
std::array<u8, Size> value;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<HashSalt>, "Data type must be trivially copyable.");
|
||||
static_assert(sizeof(HashSalt) == HashSalt::Size);
|
||||
|
||||
} // namespace FileSys
|
@ -19,6 +19,11 @@ constexpr int Strlen(const T* str) {
|
||||
return length;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int Strnlen(const T* str, std::size_t count) {
|
||||
return Strnlen(str, static_cast<int>(count));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int Strnlen(const T* str, int count) {
|
||||
ASSERT(str != nullptr);
|
||||
@ -32,6 +37,11 @@ constexpr int Strnlen(const T* str, int count) {
|
||||
return length;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int Strncmp(const T* lhs, const T* rhs, std::size_t count) {
|
||||
return Strncmp(lhs, rhs, static_cast<int>(count));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
||||
ASSERT(lhs != nullptr);
|
||||
@ -51,6 +61,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
||||
return l - r;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static constexpr int Strlcpy(T* dst, const T* src, std::size_t count) {
|
||||
return Strlcpy<T>(dst, src, static_cast<int>(count));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static constexpr int Strlcpy(T* dst, const T* src, int count) {
|
||||
ASSERT(dst != nullptr);
|
||||
|
91
src/core/file_sys/fsa/fs_i_directory.h
Normal file
91
src/core/file_sys/fsa/fs_i_directory.h
Normal file
@ -0,0 +1,91 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/fs_directory.h"
|
||||
#include "core/file_sys/fs_file.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys::Fsa {
|
||||
|
||||
class IDirectory {
|
||||
public:
|
||||
explicit IDirectory(VirtualDir backend_, OpenDirectoryMode mode)
|
||||
: backend(std::move(backend_)) {
|
||||
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
||||
// Build entry index now to save time later.
|
||||
if (True(mode & OpenDirectoryMode::Directory)) {
|
||||
BuildEntryIndex(backend->GetSubdirectories(), DirectoryEntryType::Directory);
|
||||
}
|
||||
if (True(mode & OpenDirectoryMode::File)) {
|
||||
BuildEntryIndex(backend->GetFiles(), DirectoryEntryType::File);
|
||||
}
|
||||
}
|
||||
virtual ~IDirectory() {}
|
||||
|
||||
Result Read(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
|
||||
R_UNLESS(out_count != nullptr, ResultNullptrArgument);
|
||||
if (max_entries == 0) {
|
||||
*out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_UNLESS(out_entries != nullptr, ResultNullptrArgument);
|
||||
R_UNLESS(max_entries > 0, ResultInvalidArgument);
|
||||
R_RETURN(this->DoRead(out_count, out_entries, max_entries));
|
||||
}
|
||||
|
||||
Result GetEntryCount(s64* out) {
|
||||
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||
R_RETURN(this->DoGetEntryCount(out));
|
||||
}
|
||||
|
||||
private:
|
||||
Result DoRead(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
|
||||
const u64 actual_entries =
|
||||
std::min(static_cast<u64>(max_entries), entries.size() - next_entry_index);
|
||||
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
|
||||
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
|
||||
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
|
||||
|
||||
next_entry_index += actual_entries;
|
||||
*out_count = actual_entries;
|
||||
|
||||
std::memcpy(out_entries, begin, range_size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DoGetEntryCount(s64* out) {
|
||||
*out = entries.size() - next_entry_index;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// TODO: Remove this when VFS is gone
|
||||
template <typename T>
|
||||
void BuildEntryIndex(const std::vector<T>& new_data, DirectoryEntryType type) {
|
||||
entries.reserve(entries.size() + new_data.size());
|
||||
|
||||
for (const auto& new_entry : new_data) {
|
||||
auto name = new_entry->GetName();
|
||||
|
||||
if (type == DirectoryEntryType::File && name == GetSaveDataSizeFileName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.emplace_back(name, static_cast<s8>(type),
|
||||
type == DirectoryEntryType::Directory ? 0 : new_entry->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
VirtualDir backend;
|
||||
std::vector<DirectoryEntry> entries;
|
||||
u64 next_entry_index = 0;
|
||||
};
|
||||
|
||||
} // namespace FileSys::Fsa
|
167
src/core/file_sys/fsa/fs_i_file.h
Normal file
167
src/core/file_sys/fsa/fs_i_file.h
Normal file
@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/overflow.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/fs_file.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/fs_operate_range.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys::Fsa {
|
||||
|
||||
class IFile {
|
||||
public:
|
||||
explicit IFile(VirtualFile backend_) : backend(std::move(backend_)) {}
|
||||
virtual ~IFile() {}
|
||||
|
||||
Result Read(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
|
||||
// Check that we have an output pointer
|
||||
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||
|
||||
// If we have nothing to read, just succeed
|
||||
if (size == 0) {
|
||||
*out = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// Check that the read is valid
|
||||
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
|
||||
R_UNLESS(offset >= 0, ResultOutOfRange);
|
||||
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
|
||||
|
||||
// Do the read
|
||||
R_RETURN(this->DoRead(out, offset, buffer, size, option));
|
||||
}
|
||||
|
||||
Result Read(size_t* out, s64 offset, void* buffer, size_t size) {
|
||||
R_RETURN(this->Read(out, offset, buffer, size, ReadOption::None));
|
||||
}
|
||||
|
||||
Result GetSize(s64* out) {
|
||||
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||
R_RETURN(this->DoGetSize(out));
|
||||
}
|
||||
|
||||
Result Flush() {
|
||||
R_RETURN(this->DoFlush());
|
||||
}
|
||||
|
||||
Result Write(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
|
||||
// Handle the zero-size case
|
||||
if (size == 0) {
|
||||
if (option.HasFlushFlag()) {
|
||||
R_TRY(this->Flush());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// Check the write is valid
|
||||
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
|
||||
R_UNLESS(offset >= 0, ResultOutOfRange);
|
||||
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
|
||||
|
||||
R_RETURN(this->DoWrite(offset, buffer, size, option));
|
||||
}
|
||||
|
||||
Result SetSize(s64 size) {
|
||||
R_UNLESS(size >= 0, ResultOutOfRange);
|
||||
R_RETURN(this->DoSetSize(size));
|
||||
}
|
||||
|
||||
Result OperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
|
||||
const void* src, size_t src_size) {
|
||||
R_RETURN(this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
Result OperateRange(OperationId op_id, s64 offset, s64 size) {
|
||||
R_RETURN(this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0));
|
||||
}
|
||||
|
||||
protected:
|
||||
Result DryRead(size_t* out, s64 offset, size_t size, const ReadOption& option,
|
||||
OpenMode open_mode) {
|
||||
// Check that we can read
|
||||
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Read) != 0, ResultReadNotPermitted);
|
||||
|
||||
// Get the file size, and validate our offset
|
||||
s64 file_size = 0;
|
||||
R_TRY(this->DoGetSize(std::addressof(file_size)));
|
||||
R_UNLESS(offset <= file_size, ResultOutOfRange);
|
||||
|
||||
*out = static_cast<size_t>(std::min(file_size - offset, static_cast<s64>(size)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DrySetSize(s64 size, OpenMode open_mode) {
|
||||
// Check that we can write
|
||||
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DryWrite(bool* out_append, s64 offset, size_t size, const WriteOption& option,
|
||||
OpenMode open_mode) {
|
||||
// Check that we can write
|
||||
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
|
||||
|
||||
// Get the file size
|
||||
s64 file_size = 0;
|
||||
R_TRY(this->DoGetSize(&file_size));
|
||||
|
||||
// Determine if we need to append
|
||||
*out_append = false;
|
||||
if (file_size < offset + static_cast<s64>(size)) {
|
||||
R_UNLESS(static_cast<u32>(open_mode & OpenMode::AllowAppend) != 0,
|
||||
ResultFileExtensionWithoutOpenModeAllowAppend);
|
||||
*out_append = true;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
private:
|
||||
Result DoRead(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
|
||||
const auto read_size = backend->Read(static_cast<u8*>(buffer), size, offset);
|
||||
*out = read_size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DoGetSize(s64* out) {
|
||||
*out = backend->GetSize();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DoFlush() {
|
||||
// Exists for SDK compatibiltity -- No need to flush file.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DoWrite(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
|
||||
const std::size_t written = backend->Write(static_cast<const u8*>(buffer), size, offset);
|
||||
|
||||
ASSERT_MSG(written == size,
|
||||
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", size,
|
||||
written);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DoSetSize(s64 size) {
|
||||
backend->Resize(size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DoOperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
|
||||
const void* src, size_t src_size) {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
VirtualFile backend;
|
||||
};
|
||||
|
||||
} // namespace FileSys::Fsa
|
206
src/core/file_sys/fsa/fs_i_filesystem.h
Normal file
206
src/core/file_sys/fsa/fs_i_filesystem.h
Normal file
@ -0,0 +1,206 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/fs_path.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
|
||||
namespace FileSys::Fsa {
|
||||
|
||||
class IFile;
|
||||
class IDirectory;
|
||||
|
||||
enum class QueryId : u32 {
|
||||
SetConcatenationFileAttribute = 0,
|
||||
UpdateMac = 1,
|
||||
IsSignedSystemPartitionOnSdCardValid = 2,
|
||||
QueryUnpreparedFileInformation = 3,
|
||||
};
|
||||
|
||||
class IFileSystem {
|
||||
public:
|
||||
explicit IFileSystem(VirtualDir backend_) : backend{std::move(backend_)} {}
|
||||
virtual ~IFileSystem() {}
|
||||
|
||||
Result CreateFile(const Path& path, s64 size, CreateOption option) {
|
||||
R_UNLESS(size >= 0, ResultOutOfRange);
|
||||
R_RETURN(this->DoCreateFile(path, size, static_cast<int>(option)));
|
||||
}
|
||||
|
||||
Result CreateFile(const Path& path, s64 size) {
|
||||
R_RETURN(this->CreateFile(path, size, CreateOption::None));
|
||||
}
|
||||
|
||||
Result DeleteFile(const Path& path) {
|
||||
R_RETURN(this->DoDeleteFile(path));
|
||||
}
|
||||
|
||||
Result CreateDirectory(const Path& path) {
|
||||
R_RETURN(this->DoCreateDirectory(path));
|
||||
}
|
||||
|
||||
Result DeleteDirectory(const Path& path) {
|
||||
R_RETURN(this->DoDeleteDirectory(path));
|
||||
}
|
||||
|
||||
Result DeleteDirectoryRecursively(const Path& path) {
|
||||
R_RETURN(this->DoDeleteDirectoryRecursively(path));
|
||||
}
|
||||
|
||||
Result RenameFile(const Path& old_path, const Path& new_path) {
|
||||
R_RETURN(this->DoRenameFile(old_path, new_path));
|
||||
}
|
||||
|
||||
Result RenameDirectory(const Path& old_path, const Path& new_path) {
|
||||
R_RETURN(this->DoRenameDirectory(old_path, new_path));
|
||||
}
|
||||
|
||||
Result GetEntryType(DirectoryEntryType* out, const Path& path) {
|
||||
R_RETURN(this->DoGetEntryType(out, path));
|
||||
}
|
||||
|
||||
Result OpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
|
||||
R_UNLESS(out_file != nullptr, ResultNullptrArgument);
|
||||
R_UNLESS(static_cast<u32>(mode & OpenMode::ReadWrite) != 0, ResultInvalidOpenMode);
|
||||
R_UNLESS(static_cast<u32>(mode & ~OpenMode::All) == 0, ResultInvalidOpenMode);
|
||||
R_RETURN(this->DoOpenFile(out_file, path, mode));
|
||||
}
|
||||
|
||||
Result OpenDirectory(VirtualDir* out_dir, const Path& path, OpenDirectoryMode mode) {
|
||||
R_UNLESS(out_dir != nullptr, ResultNullptrArgument);
|
||||
R_UNLESS(static_cast<u64>(mode & OpenDirectoryMode::All) != 0, ResultInvalidOpenMode);
|
||||
R_UNLESS(static_cast<u64>(
|
||||
mode & ~(OpenDirectoryMode::All | OpenDirectoryMode::NotRequireFileSize)) == 0,
|
||||
ResultInvalidOpenMode);
|
||||
R_RETURN(this->DoOpenDirectory(out_dir, path, mode));
|
||||
}
|
||||
|
||||
Result Commit() {
|
||||
R_RETURN(this->DoCommit());
|
||||
}
|
||||
|
||||
Result GetFreeSpaceSize(s64* out, const Path& path) {
|
||||
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||
R_RETURN(this->DoGetFreeSpaceSize(out, path));
|
||||
}
|
||||
|
||||
Result GetTotalSpaceSize(s64* out, const Path& path) {
|
||||
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||
R_RETURN(this->DoGetTotalSpaceSize(out, path));
|
||||
}
|
||||
|
||||
Result CleanDirectoryRecursively(const Path& path) {
|
||||
R_RETURN(this->DoCleanDirectoryRecursively(path));
|
||||
}
|
||||
|
||||
Result GetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
|
||||
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||
R_RETURN(this->DoGetFileTimeStampRaw(out, path));
|
||||
}
|
||||
|
||||
Result QueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
|
||||
const Path& path) {
|
||||
R_RETURN(this->DoQueryEntry(dst, dst_size, src, src_size, query, path));
|
||||
}
|
||||
|
||||
// These aren't accessible as commands
|
||||
Result CommitProvisionally(s64 counter) {
|
||||
R_RETURN(this->DoCommitProvisionally(counter));
|
||||
}
|
||||
|
||||
Result Rollback() {
|
||||
R_RETURN(this->DoRollback());
|
||||
}
|
||||
|
||||
Result Flush() {
|
||||
R_RETURN(this->DoFlush());
|
||||
}
|
||||
|
||||
private:
|
||||
Result DoCreateFile(const Path& path, s64 size, int flags) {
|
||||
R_RETURN(backend.CreateFile(path.GetString(), size));
|
||||
}
|
||||
|
||||
Result DoDeleteFile(const Path& path) {
|
||||
R_RETURN(backend.DeleteFile(path.GetString()));
|
||||
}
|
||||
|
||||
Result DoCreateDirectory(const Path& path) {
|
||||
R_RETURN(backend.CreateDirectory(path.GetString()));
|
||||
}
|
||||
|
||||
Result DoDeleteDirectory(const Path& path) {
|
||||
R_RETURN(backend.DeleteDirectory(path.GetString()));
|
||||
}
|
||||
|
||||
Result DoDeleteDirectoryRecursively(const Path& path) {
|
||||
R_RETURN(backend.DeleteDirectoryRecursively(path.GetString()));
|
||||
}
|
||||
|
||||
Result DoRenameFile(const Path& old_path, const Path& new_path) {
|
||||
R_RETURN(backend.RenameFile(old_path.GetString(), new_path.GetString()));
|
||||
}
|
||||
|
||||
Result DoRenameDirectory(const Path& old_path, const Path& new_path) {
|
||||
R_RETURN(backend.RenameDirectory(old_path.GetString(), new_path.GetString()));
|
||||
}
|
||||
|
||||
Result DoGetEntryType(DirectoryEntryType* out, const Path& path) {
|
||||
R_RETURN(backend.GetEntryType(out, path.GetString()));
|
||||
}
|
||||
|
||||
Result DoOpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
|
||||
R_RETURN(backend.OpenFile(out_file, path.GetString(), mode));
|
||||
}
|
||||
|
||||
Result DoOpenDirectory(VirtualDir* out_directory, const Path& path, OpenDirectoryMode mode) {
|
||||
R_RETURN(backend.OpenDirectory(out_directory, path.GetString()));
|
||||
}
|
||||
|
||||
Result DoCommit() {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result DoGetFreeSpaceSize(s64* out, const Path& path) {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result DoGetTotalSpaceSize(s64* out, const Path& path) {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result DoCleanDirectoryRecursively(const Path& path) {
|
||||
R_RETURN(backend.CleanDirectoryRecursively(path.GetString()));
|
||||
}
|
||||
|
||||
Result DoGetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
|
||||
R_RETURN(backend.GetFileTimeStampRaw(out, path.GetString()));
|
||||
}
|
||||
|
||||
Result DoQueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
|
||||
const Path& path) {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
// These aren't accessible as commands
|
||||
Result DoCommitProvisionally(s64 counter) {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result DoRollback() {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result DoFlush() {
|
||||
R_THROW(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Service::FileSystem::VfsDirectoryServiceWrapper backend;
|
||||
};
|
||||
|
||||
} // namespace FileSys::Fsa
|
36
src/core/file_sys/fssrv/fssrv_sf_path.h
Normal file
36
src/core/file_sys/fssrv/fssrv_sf_path.h
Normal file
@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/fs_directory.h"
|
||||
|
||||
namespace FileSys::Sf {
|
||||
|
||||
struct Path {
|
||||
char str[EntryNameLengthMax + 1];
|
||||
|
||||
static constexpr Path Encode(const char* p) {
|
||||
Path path = {};
|
||||
for (size_t i = 0; i < sizeof(path) - 1; i++) {
|
||||
path.str[i] = p[i];
|
||||
if (p[i] == '\x00') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
static constexpr size_t GetPathLength(const Path& path) {
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<Path>, "Path must be trivially copyable.");
|
||||
|
||||
using FspPath = Path;
|
||||
|
||||
} // namespace FileSys::Sf
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "core/crypto/aes_util.h"
|
||||
|
@ -14,48 +14,11 @@ namespace FileSys {
|
||||
|
||||
namespace {
|
||||
|
||||
void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
|
||||
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
|
||||
if (meta.zero_1 != 0) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"Possibly incorrect SaveDataAttribute, type is "
|
||||
"SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
|
||||
meta.zero_1);
|
||||
}
|
||||
if (meta.zero_2 != 0) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"Possibly incorrect SaveDataAttribute, type is "
|
||||
"SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
|
||||
meta.zero_2);
|
||||
}
|
||||
if (meta.zero_3 != 0) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"Possibly incorrect SaveDataAttribute, type is "
|
||||
"SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
|
||||
meta.zero_3);
|
||||
}
|
||||
}
|
||||
|
||||
if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is "
|
||||
"non-zero ({:016X}).",
|
||||
meta.title_id);
|
||||
}
|
||||
|
||||
if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is "
|
||||
"non-zero ({:016X}{:016X})",
|
||||
meta.user_id[1], meta.user_id[0]);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) {
|
||||
return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage ||
|
||||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
|
||||
(attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) &&
|
||||
attr.title_id == 0 && attr.save_id == 0);
|
||||
return attr.type == SaveDataType::Cache || attr.type == SaveDataType::Temporary ||
|
||||
(space == SaveDataSpaceId::User && ///< Normal Save Data -- Current Title & User
|
||||
(attr.type == SaveDataType::Account || attr.type == SaveDataType::Device) &&
|
||||
attr.program_id == 0 && attr.system_save_data_id == 0);
|
||||
}
|
||||
|
||||
std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id,
|
||||
@ -63,7 +26,7 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
||||
// Only detect nand user saves.
|
||||
const auto space_id_path = [space_id]() -> std::string_view {
|
||||
switch (space_id) {
|
||||
case SaveDataSpaceId::NandUser:
|
||||
case SaveDataSpaceId::User:
|
||||
return "/user/save";
|
||||
default:
|
||||
return "";
|
||||
@ -79,9 +42,9 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
||||
|
||||
// Only detect account/device saves from the future location.
|
||||
switch (type) {
|
||||
case SaveDataType::SaveData:
|
||||
case SaveDataType::Account:
|
||||
return fmt::format("{}/account/{}/{:016X}/0", space_id_path, uuid.RawString(), title_id);
|
||||
case SaveDataType::DeviceSaveData:
|
||||
case SaveDataType::Device:
|
||||
return fmt::format("{}/device/{:016X}/0", space_id_path, title_id);
|
||||
default:
|
||||
return "";
|
||||
@ -90,13 +53,6 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string SaveDataAttribute::DebugInfo() const {
|
||||
return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, "
|
||||
"rank={}, index={}]",
|
||||
title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type),
|
||||
static_cast<u8>(rank), index);
|
||||
}
|
||||
|
||||
SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
|
||||
VirtualDir save_directory_)
|
||||
: system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {
|
||||
@ -108,18 +64,16 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
|
||||
SaveDataFactory::~SaveDataFactory() = default;
|
||||
|
||||
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
||||
PrintSaveDataAttributeWarnings(meta);
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
|
||||
meta.user_id, meta.system_save_data_id);
|
||||
|
||||
return dir->CreateDirectoryRelative(save_directory);
|
||||
}
|
||||
|
||||
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
|
||||
meta.user_id, meta.system_save_data_id);
|
||||
|
||||
auto out = dir->GetDirectoryRelative(save_directory);
|
||||
|
||||
@ -136,11 +90,11 @@ VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) con
|
||||
|
||||
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
case SaveDataSpaceId::System:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::NandUser:
|
||||
case SaveDataSpaceId::User:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
case SaveDataSpaceId::Temporary:
|
||||
return "/temp/";
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
@ -153,7 +107,7 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
|
||||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
// be interpreted as the title id of the current process.
|
||||
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
|
||||
if (type == SaveDataType::Account || type == SaveDataType::Device) {
|
||||
if (title_id == 0) {
|
||||
title_id = program_id;
|
||||
}
|
||||
@ -173,16 +127,16 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
|
||||
switch (type) {
|
||||
case SaveDataType::SystemSaveData:
|
||||
case SaveDataType::System:
|
||||
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
|
||||
case SaveDataType::SaveData:
|
||||
case SaveDataType::DeviceSaveData:
|
||||
case SaveDataType::Account:
|
||||
case SaveDataType::Device:
|
||||
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
|
||||
title_id);
|
||||
case SaveDataType::TemporaryStorage:
|
||||
case SaveDataType::Temporary:
|
||||
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
|
||||
title_id);
|
||||
case SaveDataType::CacheStorage:
|
||||
case SaveDataType::Cache:
|
||||
return fmt::format("{}save/cache/{:016X}", out, title_id);
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
|
||||
@ -202,7 +156,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
|
||||
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
u128 user_id) const {
|
||||
const auto path =
|
||||
GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
|
||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||
|
||||
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
|
||||
@ -221,7 +175,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
SaveDataSize new_value) const {
|
||||
const auto path =
|
||||
GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
|
||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||
|
||||
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/fs_save_data_types.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@ -16,73 +17,6 @@ class System;
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class SaveDataSpaceId : u8 {
|
||||
NandSystem = 0,
|
||||
NandUser = 1,
|
||||
SdCardSystem = 2,
|
||||
TemporaryStorage = 3,
|
||||
SdCardUser = 4,
|
||||
ProperSystem = 100,
|
||||
SafeMode = 101,
|
||||
};
|
||||
|
||||
enum class SaveDataType : u8 {
|
||||
SystemSaveData = 0,
|
||||
SaveData = 1,
|
||||
BcatDeliveryCacheStorage = 2,
|
||||
DeviceSaveData = 3,
|
||||
TemporaryStorage = 4,
|
||||
CacheStorage = 5,
|
||||
SystemBcat = 6,
|
||||
};
|
||||
|
||||
enum class SaveDataRank : u8 {
|
||||
Primary = 0,
|
||||
Secondary = 1,
|
||||
};
|
||||
|
||||
enum class SaveDataFlags : u32 {
|
||||
None = (0 << 0),
|
||||
KeepAfterResettingSystemSaveData = (1 << 0),
|
||||
KeepAfterRefurbishment = (1 << 1),
|
||||
KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2),
|
||||
NeedsSecureDelete = (1 << 3),
|
||||
};
|
||||
|
||||
struct SaveDataAttribute {
|
||||
u64 title_id;
|
||||
u128 user_id;
|
||||
u64 save_id;
|
||||
SaveDataType type;
|
||||
SaveDataRank rank;
|
||||
u16 index;
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
u64 zero_1;
|
||||
u64 zero_2;
|
||||
u64 zero_3;
|
||||
|
||||
std::string DebugInfo() const;
|
||||
};
|
||||
static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size.");
|
||||
|
||||
struct SaveDataExtraData {
|
||||
SaveDataAttribute attr;
|
||||
u64 owner_id;
|
||||
s64 timestamp;
|
||||
SaveDataFlags flags;
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
s64 available_size;
|
||||
s64 journal_size;
|
||||
s64 commit_id;
|
||||
std::array<u8, 0x190> unused;
|
||||
};
|
||||
static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size.");
|
||||
|
||||
struct SaveDataSize {
|
||||
u64 normal;
|
||||
u64 journal;
|
||||
};
|
||||
|
||||
constexpr const char* GetSaveDataSizeFileName() {
|
||||
return ".yuzu_save_size";
|
||||
}
|
||||
|
@ -44,15 +44,32 @@ public:
|
||||
GuestMemory() = delete;
|
||||
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
|
||||
Common::ScratchBuffer<T>* backup = nullptr)
|
||||
: m_memory{memory}, m_addr{addr}, m_size{size} {
|
||||
: m_memory{&memory}, m_addr{addr}, m_size{size} {
|
||||
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Read) {
|
||||
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
|
||||
if (!this->TrySetSpan()) {
|
||||
if (backup) {
|
||||
backup->resize_destructive(this->size());
|
||||
m_data_span = *backup;
|
||||
m_span_valid = true;
|
||||
m_is_data_copy = true;
|
||||
} else {
|
||||
m_data_copy.resize(this->size());
|
||||
m_data_span = std::span(m_data_copy);
|
||||
m_span_valid = true;
|
||||
m_is_data_copy = true;
|
||||
}
|
||||
}
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Read) {
|
||||
Read(addr, size, backup);
|
||||
}
|
||||
}
|
||||
|
||||
~GuestMemory() = default;
|
||||
|
||||
GuestMemory(GuestMemory&& rhs) = default;
|
||||
GuestMemory& operator=(GuestMemory&& rhs) = default;
|
||||
|
||||
T* data() noexcept {
|
||||
return m_data_span.data();
|
||||
}
|
||||
@ -109,8 +126,8 @@ public:
|
||||
}
|
||||
|
||||
if (this->TrySetSpan()) {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
m_memory.FlushRegion(m_addr, this->size_bytes());
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe && M::HAS_FLUSH_INVALIDATION) {
|
||||
m_memory->FlushRegion(m_addr, this->size_bytes());
|
||||
}
|
||||
} else {
|
||||
if (backup) {
|
||||
@ -123,9 +140,9 @@ public:
|
||||
m_is_data_copy = true;
|
||||
m_span_valid = true;
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
|
||||
m_memory->ReadBlock(m_addr, this->data(), this->size_bytes());
|
||||
} else {
|
||||
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
|
||||
m_memory->ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
|
||||
}
|
||||
}
|
||||
return m_data_span;
|
||||
@ -133,18 +150,19 @@ public:
|
||||
|
||||
void Write(std::span<T> write_data) noexcept {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
|
||||
m_memory->WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
|
||||
m_memory->WriteBlock(m_addr, write_data.data(), this->size_bytes());
|
||||
} else {
|
||||
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
|
||||
m_memory->WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
bool TrySetSpan() noexcept {
|
||||
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
|
||||
if (u8* ptr = m_memory->GetSpan(m_addr, this->size_bytes()); ptr) {
|
||||
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
|
||||
m_span_valid = true;
|
||||
m_is_data_copy = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -159,7 +177,7 @@ protected:
|
||||
return m_addr_changed;
|
||||
}
|
||||
|
||||
M& m_memory;
|
||||
M* m_memory;
|
||||
u64 m_addr{};
|
||||
size_t m_size{};
|
||||
std::span<T> m_data_span{};
|
||||
@ -175,17 +193,7 @@ public:
|
||||
GuestMemoryScoped() = delete;
|
||||
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
|
||||
Common::ScratchBuffer<T>* backup = nullptr)
|
||||
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
|
||||
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
|
||||
if (!this->TrySetSpan()) {
|
||||
if (backup) {
|
||||
this->m_data_span = *backup;
|
||||
this->m_span_valid = true;
|
||||
this->m_is_data_copy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {}
|
||||
|
||||
~GuestMemoryScoped() {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Write) {
|
||||
@ -196,15 +204,17 @@ public:
|
||||
if (this->AddressChanged() || this->IsDataCopy()) {
|
||||
ASSERT(this->m_span_valid);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
|
||||
this->m_memory->WriteBlockCached(this->m_addr, this->data(),
|
||||
this->size_bytes());
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
|
||||
this->m_memory->WriteBlock(this->m_addr, this->data(), this->size_bytes());
|
||||
} else {
|
||||
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
|
||||
this->m_memory->WriteBlockUnsafe(this->m_addr, this->data(),
|
||||
this->size_bytes());
|
||||
}
|
||||
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
|
||||
(FLAGS & GuestMemoryFlags::Cached)) {
|
||||
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
|
||||
this->m_memory->InvalidateRegion(this->m_addr, this->size_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,13 +123,13 @@ Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID use
|
||||
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
||||
|
||||
FileSys::SaveDataAttribute attribute{};
|
||||
attribute.title_id = m_applet->program_id;
|
||||
attribute.program_id = m_applet->program_id;
|
||||
attribute.user_id = user_id.AsU128();
|
||||
attribute.type = FileSys::SaveDataType::SaveData;
|
||||
attribute.type = FileSys::SaveDataType::Account;
|
||||
|
||||
FileSys::VirtualDir save_data{};
|
||||
R_TRY(system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
|
||||
&save_data, FileSys::SaveDataSpaceId::NandUser, attribute));
|
||||
&save_data, FileSys::SaveDataSpaceId::User, attribute));
|
||||
|
||||
*out_size = 0;
|
||||
R_SUCCEED();
|
||||
|
223
src/core/hle/service/aoc/addon_content_manager.cpp
Normal file
223
src/core/hle/service/aoc/addon_content_manager.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/aoc/addon_content_manager.h"
|
||||
#include "core/hle/service/aoc/purchase_event_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return FileSys::GetBaseTitleID(title_id) == base;
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
const auto list =
|
||||
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
|
||||
[](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
|
||||
add_on_content.erase(
|
||||
std::remove_if(
|
||||
add_on_content.begin(), add_on_content.end(),
|
||||
[&rcu](u64 tid) {
|
||||
return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
|
||||
Loader::ResultStatus::Success;
|
||||
}),
|
||||
add_on_content.end());
|
||||
return add_on_content;
|
||||
}
|
||||
|
||||
IAddOnContentManager::IAddOnContentManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)},
|
||||
service_context{system_, "aoc:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CountAddOnContentByApplicationId"},
|
||||
{1, nullptr, "ListAddOnContentByApplicationId"},
|
||||
{2, D<&IAddOnContentManager::CountAddOnContent>, "CountAddOnContent"},
|
||||
{3, D<&IAddOnContentManager::ListAddOnContent>, "ListAddOnContent"},
|
||||
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
|
||||
{5, D<&IAddOnContentManager::GetAddOnContentBaseId>, "GetAddOnContentBaseId"},
|
||||
{6, nullptr, "PrepareAddOnContentByApplicationId"},
|
||||
{7, D<&IAddOnContentManager::PrepareAddOnContent>, "PrepareAddOnContent"},
|
||||
{8, D<&IAddOnContentManager::GetAddOnContentListChangedEvent>, "GetAddOnContentListChangedEvent"},
|
||||
{9, nullptr, "GetAddOnContentLostErrorCode"},
|
||||
{10, D<&IAddOnContentManager::GetAddOnContentListChangedEventWithProcessId>, "GetAddOnContentListChangedEventWithProcessId"},
|
||||
{11, D<&IAddOnContentManager::NotifyMountAddOnContent>, "NotifyMountAddOnContent"},
|
||||
{12, D<&IAddOnContentManager::NotifyUnmountAddOnContent>, "NotifyUnmountAddOnContent"},
|
||||
{13, nullptr, "IsAddOnContentMountedForDebug"},
|
||||
{50, D<&IAddOnContentManager::CheckAddOnContentMountStatus>, "CheckAddOnContentMountStatus"},
|
||||
{100, D<&IAddOnContentManager::CreateEcPurchasedEventManager>, "CreateEcPurchasedEventManager"},
|
||||
{101, D<&IAddOnContentManager::CreatePermanentEcPurchasedEventManager>, "CreatePermanentEcPurchasedEventManager"},
|
||||
{110, nullptr, "CreateContentsServiceManager"},
|
||||
{200, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
|
||||
{300, nullptr, "SetupHostAddOnContent"},
|
||||
{301, nullptr, "GetRegisteredAddOnContentPath"},
|
||||
{302, nullptr, "UpdateCachedList"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
aoc_change_event = service_context.CreateEvent("GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
IAddOnContentManager::~IAddOnContentManager() {
|
||||
service_context.CloseEvent(aoc_change_event);
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
*out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
*out_count = static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
for (u64 content_id : add_on_content) {
|
||||
if (FileSys::GetBaseTitleID(content_id) != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
||||
|
||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
|
||||
std::memcpy(out_addons.data(), out.data(), *out_count * sizeof(u32));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
||||
ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first == nullptr) {
|
||||
*out_title_id = FileSys::GetAOCBaseTitleID(title_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
*out_title_id = res.first->GetDLCBaseTitleId();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::PrepareAddOnContent(s32 addon_index, ClientProcessId process_id) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
|
||||
process_id.pid);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::GetAddOnContentListChangedEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_event = &aoc_change_event->GetReadableEvent();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::GetAddOnContentListChangedEventWithProcessId(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event, ClientProcessId process_id) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_event = &aoc_change_event->GetReadableEvent();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::NotifyMountAddOnContent() {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::NotifyUnmountAddOnContent() {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CheckAddOnContentMountStatus() {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CreateEcPurchasedEventManager(
|
||||
OutInterface<IPurchaseEventManager> out_interface) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_interface = std::make_shared<IPurchaseEventManager>(system);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CreatePermanentEcPurchasedEventManager(
|
||||
OutInterface<IPurchaseEventManager> out_interface) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_interface = std::make_shared<IPurchaseEventManager>(system);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
server_manager->RegisterNamedService("aoc:u", std::make_shared<IAddOnContentManager>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
51
src/core/hle/service/aoc/addon_content_manager.h
Normal file
51
src/core/hle/service/aoc/addon_content_manager.h
Normal file
@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class IPurchaseEventManager;
|
||||
|
||||
class IAddOnContentManager final : public ServiceFramework<IAddOnContentManager> {
|
||||
public:
|
||||
explicit IAddOnContentManager(Core::System& system);
|
||||
~IAddOnContentManager() override;
|
||||
|
||||
Result CountAddOnContent(Out<u32> out_count, ClientProcessId process_id);
|
||||
Result ListAddOnContent(Out<u32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, ClientProcessId process_id);
|
||||
Result GetAddOnContentBaseId(Out<u64> out_title_id, ClientProcessId process_id);
|
||||
Result PrepareAddOnContent(s32 addon_index, ClientProcessId process_id);
|
||||
Result GetAddOnContentListChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetAddOnContentListChangedEventWithProcessId(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event, ClientProcessId process_id);
|
||||
Result NotifyMountAddOnContent();
|
||||
Result NotifyUnmountAddOnContent();
|
||||
Result CheckAddOnContentMountStatus();
|
||||
Result CreateEcPurchasedEventManager(OutInterface<IPurchaseEventManager> out_interface);
|
||||
Result CreatePermanentEcPurchasedEventManager(
|
||||
OutInterface<IPurchaseEventManager> out_interface);
|
||||
|
||||
private:
|
||||
std::vector<u64> add_on_content;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* aoc_change_event;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,340 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400};
|
||||
|
||||
static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return FileSys::GetBaseTitleID(title_id) == base;
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
const auto list =
|
||||
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
|
||||
[](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
|
||||
add_on_content.erase(
|
||||
std::remove_if(
|
||||
add_on_content.begin(), add_on_content.end(),
|
||||
[&rcu](u64 tid) {
|
||||
return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
|
||||
Loader::ResultStatus::Success;
|
||||
}),
|
||||
add_on_content.end());
|
||||
return add_on_content;
|
||||
}
|
||||
|
||||
class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> {
|
||||
public:
|
||||
explicit IPurchaseEventManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "IPurchaseEventManager"}, service_context{
|
||||
system, "IPurchaseEventManager"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IPurchaseEventManager::SetDefaultDeliveryTarget, "SetDefaultDeliveryTarget"},
|
||||
{1, &IPurchaseEventManager::SetDeliveryTarget, "SetDeliveryTarget"},
|
||||
{2, &IPurchaseEventManager::GetPurchasedEventReadableHandle, "GetPurchasedEventReadableHandle"},
|
||||
{3, &IPurchaseEventManager::PopPurchasedProductInfo, "PopPurchasedProductInfo"},
|
||||
{4, &IPurchaseEventManager::PopPurchasedProductInfoWithUid, "PopPurchasedProductInfoWithUid"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
purchased_event = service_context.CreateEvent("IPurchaseEventManager:PurchasedEvent");
|
||||
}
|
||||
|
||||
~IPurchaseEventManager() override {
|
||||
service_context.CloseEvent(purchased_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetDefaultDeliveryTarget(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto unknown_1 = rp.Pop<u64>();
|
||||
[[maybe_unused]] const auto unknown_2 = ctx.ReadBuffer();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, unknown_1={}", unknown_1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void SetDeliveryTarget(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto unknown_1 = rp.Pop<u64>();
|
||||
[[maybe_unused]] const auto unknown_2 = ctx.ReadBuffer();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, unknown_1={}", unknown_1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetPurchasedEventReadableHandle(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(purchased_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void PopPurchasedProductInfo(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
void PopPurchasedProductInfoWithUid(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* purchased_event;
|
||||
};
|
||||
|
||||
AOC_U::AOC_U(Core::System& system_)
|
||||
: ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)},
|
||||
service_context{system_, "aoc:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CountAddOnContentByApplicationId"},
|
||||
{1, nullptr, "ListAddOnContentByApplicationId"},
|
||||
{2, &AOC_U::CountAddOnContent, "CountAddOnContent"},
|
||||
{3, &AOC_U::ListAddOnContent, "ListAddOnContent"},
|
||||
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
|
||||
{5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
|
||||
{6, nullptr, "PrepareAddOnContentByApplicationId"},
|
||||
{7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
|
||||
{8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
|
||||
{9, nullptr, "GetAddOnContentLostErrorCode"},
|
||||
{10, &AOC_U::GetAddOnContentListChangedEventWithProcessId, "GetAddOnContentListChangedEventWithProcessId"},
|
||||
{11, &AOC_U::NotifyMountAddOnContent, "NotifyMountAddOnContent"},
|
||||
{12, &AOC_U::NotifyUnmountAddOnContent, "NotifyUnmountAddOnContent"},
|
||||
{13, nullptr, "IsAddOnContentMountedForDebug"},
|
||||
{50, &AOC_U::CheckAddOnContentMountStatus, "CheckAddOnContentMountStatus"},
|
||||
{100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"},
|
||||
{101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"},
|
||||
{110, nullptr, "CreateContentsServiceManager"},
|
||||
{200, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
|
||||
{300, nullptr, "SetupHostAddOnContent"},
|
||||
{301, nullptr, "GetRegisteredAddOnContentPath"},
|
||||
{302, nullptr, "UpdateCachedList"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
aoc_change_event = service_context.CreateEvent("GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() {
|
||||
service_context.CloseEvent(aoc_change_event);
|
||||
}
|
||||
|
||||
void AOC_U::CountAddOnContent(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
rb.Push<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push<u32>(static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
|
||||
}
|
||||
|
||||
void AOC_U::ListAddOnContent(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u32 offset;
|
||||
u32 count;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [offset, count, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
for (u64 content_id : add_on_content) {
|
||||
if (FileSys::GetBaseTitleID(content_id) != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
|
||||
}
|
||||
}
|
||||
|
||||
if (out.size() < offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
out.resize(out_count);
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentBaseId(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first == nullptr) {
|
||||
rb.Push(FileSys::GetAOCBaseTitleID(title_id));
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(res.first->GetDLCBaseTitleId());
|
||||
}
|
||||
|
||||
void AOC_U::PrepareAddOnContent(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
s32 addon_index;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [addon_index, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentListChangedEvent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentListChangedEventWithProcessId(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void AOC_U::NotifyMountAddOnContent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::NotifyUnmountAddOnContent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::CheckAddOnContentMountStatus(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::CreateEcPurchasedEventManager(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPurchaseEventManager>(system);
|
||||
}
|
||||
|
||||
void AOC_U::CreatePermanentEcPurchasedEventManager(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPurchaseEventManager>(system);
|
||||
}
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
server_manager->RegisterNamedService("aoc:u", std::make_shared<AOC_U>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,45 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class AOC_U final : public ServiceFramework<AOC_U> {
|
||||
public:
|
||||
explicit AOC_U(Core::System& system);
|
||||
~AOC_U() override;
|
||||
|
||||
private:
|
||||
void CountAddOnContent(HLERequestContext& ctx);
|
||||
void ListAddOnContent(HLERequestContext& ctx);
|
||||
void GetAddOnContentBaseId(HLERequestContext& ctx);
|
||||
void PrepareAddOnContent(HLERequestContext& ctx);
|
||||
void GetAddOnContentListChangedEvent(HLERequestContext& ctx);
|
||||
void GetAddOnContentListChangedEventWithProcessId(HLERequestContext& ctx);
|
||||
void NotifyMountAddOnContent(HLERequestContext& ctx);
|
||||
void NotifyUnmountAddOnContent(HLERequestContext& ctx);
|
||||
void CheckAddOnContentMountStatus(HLERequestContext& ctx);
|
||||
void CreateEcPurchasedEventManager(HLERequestContext& ctx);
|
||||
void CreatePermanentEcPurchasedEventManager(HLERequestContext& ctx);
|
||||
|
||||
std::vector<u64> add_on_content;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* aoc_change_event;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::AOC
|
67
src/core/hle/service/aoc/purchase_event_manager.cpp
Normal file
67
src/core/hle/service/aoc/purchase_event_manager.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/aoc/purchase_event_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400};
|
||||
|
||||
IPurchaseEventManager::IPurchaseEventManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "IPurchaseEventManager"}, service_context{system,
|
||||
"IPurchaseEventManager"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IPurchaseEventManager::SetDefaultDeliveryTarget>, "SetDefaultDeliveryTarget"},
|
||||
{1, D<&IPurchaseEventManager::SetDeliveryTarget>, "SetDeliveryTarget"},
|
||||
{2, D<&IPurchaseEventManager::GetPurchasedEvent>, "GetPurchasedEvent"},
|
||||
{3, D<&IPurchaseEventManager::PopPurchasedProductInfo>, "PopPurchasedProductInfo"},
|
||||
{4, D<&IPurchaseEventManager::PopPurchasedProductInfoWithUid>, "PopPurchasedProductInfoWithUid"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
purchased_event = service_context.CreateEvent("IPurchaseEventManager:PurchasedEvent");
|
||||
}
|
||||
|
||||
IPurchaseEventManager::~IPurchaseEventManager() {
|
||||
service_context.CloseEvent(purchased_event);
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::SetDefaultDeliveryTarget(
|
||||
ClientProcessId process_id, InBuffer<BufferAttr_HipcMapAlias> in_buffer) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, process_id={}", process_id.pid);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::SetDeliveryTarget(u64 unknown,
|
||||
InBuffer<BufferAttr_HipcMapAlias> in_buffer) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, unknown={}", unknown);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::GetPurchasedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AOC, "called");
|
||||
|
||||
*out_event = &purchased_event->GetReadableEvent();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::PopPurchasedProductInfo() {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_RETURN(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::PopPurchasedProductInfoWithUid() {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_RETURN(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
30
src/core/hle/service/aoc/purchase_event_manager.h
Normal file
30
src/core/hle/service/aoc/purchase_event_manager.h
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> {
|
||||
public:
|
||||
explicit IPurchaseEventManager(Core::System& system_);
|
||||
~IPurchaseEventManager() override;
|
||||
|
||||
Result SetDefaultDeliveryTarget(ClientProcessId process_id,
|
||||
InBuffer<BufferAttr_HipcMapAlias> in_buffer);
|
||||
Result SetDeliveryTarget(u64 unknown, InBuffer<BufferAttr_HipcMapAlias> in_buffer);
|
||||
Result GetPurchasedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result PopPurchasedProductInfo();
|
||||
Result PopPurchasedProductInfoWithUid();
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* purchased_event;
|
||||
};
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,393 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/in/audio_in_system.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/audio/audin_u.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioIn;
|
||||
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioInParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioIn"},
|
||||
service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
|
||||
process{handle}, impl{std::make_shared<In>(system_, manager, event, session_id)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioIn::GetAudioInState, "GetAudioInState"},
|
||||
{1, &IAudioIn::Start, "Start"},
|
||||
{2, &IAudioIn::Stop, "Stop"},
|
||||
{3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
|
||||
{4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
|
||||
{6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
|
||||
{7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
|
||||
{8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
|
||||
{9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
|
||||
{10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
|
||||
{11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
|
||||
{12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
|
||||
{13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
|
||||
{14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
process->Open();
|
||||
|
||||
if (impl->GetSystem()
|
||||
.Initialize(device_name, in_params, handle, applet_resource_user_id)
|
||||
.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
|
||||
}
|
||||
}
|
||||
|
||||
~IAudioIn() override {
|
||||
impl->Free();
|
||||
service_context.CloseEvent(event);
|
||||
process->Close();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<In> GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAudioInState(HLERequestContext& ctx) {
|
||||
const auto state = static_cast<u32>(impl->GetState());
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. State={}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(state);
|
||||
}
|
||||
|
||||
void Start(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto result = impl->StartSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Stop(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto result = impl->StopSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void AppendAudioInBuffer(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 tag = rp.PopRaw<u64>();
|
||||
|
||||
const auto in_buffer_size{ctx.GetReadBufferSize()};
|
||||
if (in_buffer_size < sizeof(AudioInBuffer)) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
|
||||
}
|
||||
|
||||
const auto& in_buffer = ctx.ReadBuffer();
|
||||
AudioInBuffer buffer{};
|
||||
std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
|
||||
|
||||
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
|
||||
|
||||
auto result = impl->AppendBuffer(buffer, tag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterBufferEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& buffer_event = impl->GetBufferEvent();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void GetReleasedAudioInBuffer(HLERequestContext& ctx) {
|
||||
const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
|
||||
released_buffer.resize_destructive(write_buffer_size);
|
||||
released_buffer[0] = 0;
|
||||
|
||||
const auto count = impl->GetReleasedBuffers(released_buffer);
|
||||
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers",
|
||||
impl->GetSystem().GetSessionId(), count);
|
||||
|
||||
ctx.WriteBuffer(released_buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void ContainsAudioInBuffer(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_queued);
|
||||
}
|
||||
|
||||
void GetAudioInBufferCount(HLERequestContext& ctx) {
|
||||
const auto buffer_count = impl->GetBufferCount();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_count);
|
||||
}
|
||||
|
||||
void SetDeviceGain(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto volume{rp.Pop<f32>()};
|
||||
LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
|
||||
|
||||
impl->SetVolume(volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetDeviceGain(HLERequestContext& ctx) {
|
||||
auto volume{impl->GetVolume()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
void FlushAudioInBuffers(HLERequestContext& ctx) {
|
||||
bool flushed{impl->FlushAudioInBuffers()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(flushed);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* event;
|
||||
Kernel::KProcess* process;
|
||||
std::shared_ptr<AudioCore::AudioIn::In> impl;
|
||||
Common::ScratchBuffer<u64> released_buffer;
|
||||
};
|
||||
|
||||
AudInU::AudInU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"},
|
||||
impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudInU::ListAudioIns, "ListAudioIns"},
|
||||
{1, &AudInU::OpenAudioIn, "OpenAudioIn"},
|
||||
{2, &AudInU::ListAudioIns, "ListAudioInsAuto"},
|
||||
{3, &AudInU::OpenAudioIn, "OpenAudioInAuto"},
|
||||
{4, &AudInU::ListAudioInsAutoFiltered, "ListAudioInsAutoFiltered"},
|
||||
{5, &AudInU::OpenAudioInProtocolSpecified, "OpenAudioInProtocolSpecified"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
AudInU::~AudInU() = default;
|
||||
|
||||
void AudInU::ListAudioIns(HLERequestContext& ctx) {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
|
||||
u32 out_count{0};
|
||||
if (write_count > 0) {
|
||||
out_count = impl->GetDeviceNames(device_names, write_count, false);
|
||||
ctx.WriteBuffer(device_names);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AudInU::ListAudioInsAutoFiltered(HLERequestContext& ctx) {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
|
||||
u32 out_count{0};
|
||||
if (write_count > 0) {
|
||||
out_count = impl->GetDeviceNames(device_names, write_count, true);
|
||||
ctx.WriteBuffer(device_names);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AudInU::OpenAudioIn(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto in_params{rp.PopRaw<AudioInParameter>()};
|
||||
auto applet_resource_user_id{rp.PopRaw<u64>()};
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
auto device_name = Common::StringFromBuffer(device_name_data);
|
||||
auto handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
|
||||
if (process.IsNull()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to get process handle");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock l{impl->mutex};
|
||||
auto link{impl->LinkToManager()};
|
||||
if (link.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(link);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t new_session_id{};
|
||||
auto result{impl->AcquireSessionId(new_session_id)};
|
||||
if (result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_in =
|
||||
std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
|
||||
process.GetPointerUnsafe(), applet_resource_user_id);
|
||||
impl->sessions[new_session_id] = audio_in->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format =
|
||||
static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
std::string out_name{out_system.GetName()};
|
||||
ctx.WriteBuffer(out_name);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudioInParameterInternal>(out_params);
|
||||
rb.PushIpcInterface<IAudioIn>(audio_in);
|
||||
}
|
||||
|
||||
void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto protocol_specified{rp.PopRaw<u64>()};
|
||||
auto in_params{rp.PopRaw<AudioInParameter>()};
|
||||
auto applet_resource_user_id{rp.PopRaw<u64>()};
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
auto device_name = Common::StringFromBuffer(device_name_data);
|
||||
auto handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
|
||||
if (process.IsNull()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to get process handle");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock l{impl->mutex};
|
||||
auto link{impl->LinkToManager()};
|
||||
if (link.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(link);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t new_session_id{};
|
||||
auto result{impl->AcquireSessionId(new_session_id)};
|
||||
if (result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_in =
|
||||
std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
|
||||
process.GetPointerUnsafe(), applet_resource_user_id);
|
||||
impl->sessions[new_session_id] = audio_in->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format =
|
||||
static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
std::string out_name{out_system.GetName()};
|
||||
if (protocol_specified == 0) {
|
||||
if (out_system.IsUac()) {
|
||||
out_name = "UacIn";
|
||||
} else {
|
||||
out_name = "DeviceIn";
|
||||
}
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out_name);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudioInParameterInternal>(out_params);
|
||||
rb.PushIpcInterface<IAudioIn>(audio_in);
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,38 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_in_manager.h"
|
||||
#include "audio_core/in/audio_in.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
class Manager;
|
||||
class In;
|
||||
} // namespace AudioCore::AudioOut
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class AudInU final : public ServiceFramework<AudInU> {
|
||||
public:
|
||||
explicit AudInU(Core::System& system_);
|
||||
~AudInU() override;
|
||||
|
||||
private:
|
||||
void ListAudioIns(HLERequestContext& ctx);
|
||||
void ListAudioInsAutoFiltered(HLERequestContext& ctx);
|
||||
void OpenInOutImpl(HLERequestContext& ctx);
|
||||
void OpenAudioIn(HLERequestContext& ctx);
|
||||
void OpenAudioInProtocolSpecified(HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioCore::AudioIn::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -2,14 +2,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/audin_u.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/audio/audio_controller.h"
|
||||
#include "core/hle/service/audio/audout_u.h"
|
||||
#include "core/hle/service/audio/audrec_a.h"
|
||||
#include "core/hle/service/audio/audrec_u.h"
|
||||
#include "core/hle/service/audio/audren_u.h"
|
||||
#include "core/hle/service/audio/hwopus.h"
|
||||
#include "core/hle/service/audio/audio_in_manager.h"
|
||||
#include "core/hle/service/audio/audio_out_manager.h"
|
||||
#include "core/hle/service/audio/audio_renderer_manager.h"
|
||||
#include "core/hle/service/audio/final_output_recorder_manager.h"
|
||||
#include "core/hle/service/audio/final_output_recorder_manager_for_applet.h"
|
||||
#include "core/hle/service/audio/hardware_opus_decoder_manager.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@ -19,12 +19,16 @@ void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system));
|
||||
server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system));
|
||||
server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system));
|
||||
server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system));
|
||||
server_manager->RegisterNamedService("audrec:u", std::make_shared<AudRecU>(system));
|
||||
server_manager->RegisterNamedService("audren:u", std::make_shared<AudRenU>(system));
|
||||
server_manager->RegisterNamedService("hwopus", std::make_shared<HwOpus>(system));
|
||||
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system));
|
||||
server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system));
|
||||
server_manager->RegisterNamedService(
|
||||
"audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system));
|
||||
server_manager->RegisterNamedService("audrec:u",
|
||||
std::make_shared<IFinalOutputRecorderManager>(system));
|
||||
server_manager->RegisterNamedService("audren:u",
|
||||
std::make_shared<IAudioRendererManager>(system));
|
||||
server_manager->RegisterNamedService("hwopus",
|
||||
std::make_shared<IHardwareOpusDecoderManager>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
@ -16,27 +16,27 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetTargetVolume"},
|
||||
{1, nullptr, "SetTargetVolume"},
|
||||
{2, C<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
||||
{3, C<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
||||
{2, D<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
||||
{3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
||||
{4, nullptr, "IsTargetMute"},
|
||||
{5, nullptr, "SetTargetMute"},
|
||||
{6, nullptr, "IsTargetConnected"},
|
||||
{7, nullptr, "SetDefaultTarget"},
|
||||
{8, nullptr, "GetDefaultTarget"},
|
||||
{9, C<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
|
||||
{10, C<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
|
||||
{9, D<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
|
||||
{10, D<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
|
||||
{11, nullptr, "SetForceMutePolicy"},
|
||||
{12, C<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
|
||||
{13, C<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
|
||||
{14, C<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
|
||||
{12, D<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
|
||||
{13, D<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
|
||||
{14, D<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
|
||||
{15, nullptr, "SetOutputTarget"},
|
||||
{16, nullptr, "SetInputTargetForceEnabled"},
|
||||
{17, C<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
|
||||
{18, C<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
|
||||
{17, D<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
|
||||
{18, D<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
|
||||
{19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
|
||||
{20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
|
||||
{21, nullptr, "GetAudioOutputTargetForPlayReport"},
|
||||
{22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
|
||||
{22, D<&IAudioController::NotifyHeadphoneVolumeWarningDisplayedEvent>, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
|
||||
{23, nullptr, "SetSystemOutputMasterVolume"},
|
||||
{24, nullptr, "GetSystemOutputMasterVolume"},
|
||||
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
|
||||
@ -44,11 +44,11 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
{27, nullptr, "SetVolumeMappingTableForDev"},
|
||||
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
|
||||
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||
{30, C<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
||||
{31, C<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
||||
{30, D<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
||||
{31, D<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
||||
{32, nullptr, "GetActiveOutputTarget"},
|
||||
{33, nullptr, "GetTargetDeviceInfo"},
|
||||
{34, C<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
||||
{34, D<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
||||
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||
{36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||
{37, nullptr, "SetHearingProtectionSafeguardEnabled"},
|
||||
@ -138,7 +138,7 @@ Result IAudioController::SetOutputModeSetting(Set::AudioOutputModeTarget target,
|
||||
}
|
||||
|
||||
Result IAudioController::SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode) {
|
||||
LOG_WARNING(Audio, "(STUBBED) called");
|
||||
LOG_WARNING(Audio, "(STUBBED) called, output_level_mode={}", output_level_mode);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -150,6 +150,11 @@ Result IAudioController::GetHeadphoneOutputLevelMode(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::NotifyHeadphoneVolumeWarningDisplayedEvent() {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) {
|
||||
LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", is_speaker_auto_mute_enabled);
|
||||
|
||||
|
@ -45,6 +45,7 @@ private:
|
||||
Set::AudioOutputMode output_mode);
|
||||
Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode);
|
||||
Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode);
|
||||
Result NotifyHeadphoneVolumeWarningDisplayedEvent();
|
||||
Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled);
|
||||
Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled);
|
||||
Result AcquireTargetNotification(OutCopyHandle<Kernel::KReadableEvent> out_notification_event);
|
||||
|
163
src/core/hle/service/audio/audio_device.cpp
Normal file
163
src/core/hle/service/audio/audio_device.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/audio/audio_device.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
|
||||
u32 device_num)
|
||||
: ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"},
|
||||
impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)},
|
||||
event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioDevice::ListAudioDeviceName>, "ListAudioDeviceName"},
|
||||
{1, D<&IAudioDevice::SetAudioDeviceOutputVolume>, "SetAudioDeviceOutputVolume"},
|
||||
{2, D<&IAudioDevice::GetAudioDeviceOutputVolume>, "GetAudioDeviceOutputVolume"},
|
||||
{3, D<&IAudioDevice::GetActiveAudioDeviceName>, "GetActiveAudioDeviceName"},
|
||||
{4, D<&IAudioDevice::QueryAudioDeviceSystemEvent>, "QueryAudioDeviceSystemEvent"},
|
||||
{5, D<&IAudioDevice::GetActiveChannelCount>, "GetActiveChannelCount"},
|
||||
{6, D<&IAudioDevice::ListAudioDeviceNameAuto>, "ListAudioDeviceNameAuto"},
|
||||
{7, D<&IAudioDevice::SetAudioDeviceOutputVolumeAuto>, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{8, D<&IAudioDevice::GetAudioDeviceOutputVolumeAuto>, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{10, D<&IAudioDevice::GetActiveAudioDeviceNameAuto>, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, D<&IAudioDevice::QueryAudioDeviceInputEvent>, "QueryAudioDeviceInputEvent"},
|
||||
{12, D<&IAudioDevice::QueryAudioDeviceOutputEvent>, "QueryAudioDeviceOutputEvent"},
|
||||
{13, D<&IAudioDevice::GetActiveAudioDeviceName>, "GetActiveAudioOutputDeviceName"},
|
||||
{14, D<&IAudioDevice::ListAudioOutputDeviceName>, "ListAudioOutputDeviceName"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
event->Signal();
|
||||
}
|
||||
|
||||
IAudioDevice::~IAudioDevice() {
|
||||
service_context.CloseEvent(event);
|
||||
}
|
||||
|
||||
Result IAudioDevice::ListAudioDeviceName(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_names, Out<s32> out_count) {
|
||||
R_RETURN(this->ListAudioDeviceNameAuto(out_names, out_count));
|
||||
}
|
||||
|
||||
Result IAudioDevice::SetAudioDeviceOutputVolume(
|
||||
InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> name, f32 volume) {
|
||||
R_RETURN(this->SetAudioDeviceOutputVolumeAuto(name, volume));
|
||||
}
|
||||
|
||||
Result IAudioDevice::GetAudioDeviceOutputVolume(
|
||||
Out<f32> out_volume, InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> name) {
|
||||
R_RETURN(this->GetAudioDeviceOutputVolumeAuto(out_volume, name));
|
||||
}
|
||||
|
||||
Result IAudioDevice::GetActiveAudioDeviceName(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_name) {
|
||||
R_RETURN(this->GetActiveAudioDeviceNameAuto(out_name));
|
||||
}
|
||||
|
||||
Result IAudioDevice::ListAudioDeviceNameAuto(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> out_names,
|
||||
Out<s32> out_count) {
|
||||
*out_count = impl->ListAudioDeviceName(out_names);
|
||||
|
||||
std::string out{};
|
||||
for (s32 i = 0; i < *out_count; i++) {
|
||||
std::string a{};
|
||||
u32 j = 0;
|
||||
while (out_names[i].name[j] != '\0') {
|
||||
a += out_names[i].name[j];
|
||||
j++;
|
||||
}
|
||||
out += "\n\t" + a;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::SetAudioDeviceOutputVolumeAuto(
|
||||
InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> name, f32 volume) {
|
||||
R_UNLESS(!name.empty(), Audio::ResultInsufficientBuffer);
|
||||
|
||||
const std::string device_name = Common::StringFromBuffer(name[0].name);
|
||||
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", device_name, volume);
|
||||
|
||||
if (device_name == "AudioTvOutput") {
|
||||
impl->SetDeviceVolumes(volume);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::GetAudioDeviceOutputVolumeAuto(
|
||||
Out<f32> out_volume, InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> name) {
|
||||
R_UNLESS(!name.empty(), Audio::ResultInsufficientBuffer);
|
||||
|
||||
const std::string device_name = Common::StringFromBuffer(name[0].name);
|
||||
LOG_DEBUG(Service_Audio, "called. Name={}", device_name);
|
||||
|
||||
*out_volume = 1.0f;
|
||||
if (device_name == "AudioTvOutput") {
|
||||
*out_volume = impl->GetDeviceVolume(device_name);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::GetActiveAudioDeviceNameAuto(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> out_name) {
|
||||
R_UNLESS(!out_name.empty(), Audio::ResultInsufficientBuffer);
|
||||
out_name[0] = AudioDevice::AudioDeviceName("AudioTvOutput");
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::QueryAudioDeviceSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
event->Signal();
|
||||
*out_event = &event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::QueryAudioDeviceInputEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
*out_event = &event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::QueryAudioDeviceOutputEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_event = &event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::GetActiveChannelCount(Out<u32> out_active_channel_count) {
|
||||
*out_active_channel_count = system.AudioCore().GetOutputSink().GetSystemChannels();
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", *out_active_channel_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioDevice::ListAudioOutputDeviceName(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_names, Out<s32> out_count) {
|
||||
*out_count = impl->ListAudioOutputDeviceName(out_names);
|
||||
|
||||
std::string out{};
|
||||
for (s32 i = 0; i < *out_count; i++) {
|
||||
std::string a{};
|
||||
u32 j = 0;
|
||||
while (out_names[i].name[j] != '\0') {
|
||||
a += out_names[i].name[j];
|
||||
j++;
|
||||
}
|
||||
out += "\n\t" + a;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
58
src/core/hle/service/audio/audio_device.h
Normal file
58
src/core/hle/service/audio/audio_device.h
Normal file
@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
using AudioCore::Renderer::AudioDevice;
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
|
||||
public:
|
||||
explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
|
||||
u32 device_num);
|
||||
~IAudioDevice() override;
|
||||
|
||||
private:
|
||||
Result ListAudioDeviceName(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_names,
|
||||
Out<s32> out_count);
|
||||
Result SetAudioDeviceOutputVolume(
|
||||
InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> name, f32 volume);
|
||||
Result GetAudioDeviceOutputVolume(
|
||||
Out<f32> out_volume, InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> name);
|
||||
Result GetActiveAudioDeviceName(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_name);
|
||||
Result ListAudioDeviceNameAuto(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> out_names,
|
||||
Out<s32> out_count);
|
||||
Result SetAudioDeviceOutputVolumeAuto(
|
||||
InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> name, f32 volume);
|
||||
Result GetAudioDeviceOutputVolumeAuto(
|
||||
Out<f32> out_volume, InArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> name);
|
||||
Result GetActiveAudioDeviceNameAuto(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcAutoSelect> out_name);
|
||||
Result QueryAudioDeviceSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result QueryAudioDeviceInputEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result QueryAudioDeviceOutputEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetActiveChannelCount(Out<u32> out_active_channel_count);
|
||||
Result ListAudioOutputDeviceName(
|
||||
OutArray<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_names,
|
||||
Out<s32> out_count);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioCore::Renderer::AudioDevice> impl;
|
||||
Kernel::KEvent* event;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
146
src/core/hle/service/audio/audio_in.cpp
Normal file
146
src/core/hle/service/audio/audio_in.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/audio/audio_in.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioIn;
|
||||
|
||||
IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioInParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"},
|
||||
event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<In>(system_,
|
||||
manager, event,
|
||||
session_id)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioIn::GetAudioInState>, "GetAudioInState"},
|
||||
{1, D<&IAudioIn::Start>, "Start"},
|
||||
{2, D<&IAudioIn::Stop>, "Stop"},
|
||||
{3, D<&IAudioIn::AppendAudioInBuffer>, "AppendAudioInBuffer"},
|
||||
{4, D<&IAudioIn::RegisterBufferEvent>, "RegisterBufferEvent"},
|
||||
{5, D<&IAudioIn::GetReleasedAudioInBuffers>, "GetReleasedAudioInBuffers"},
|
||||
{6, D<&IAudioIn::ContainsAudioInBuffer>, "ContainsAudioInBuffer"},
|
||||
{7, D<&IAudioIn::AppendAudioInBuffer>, "AppendUacInBuffer"},
|
||||
{8, D<&IAudioIn::AppendAudioInBufferAuto>, "AppendAudioInBufferAuto"},
|
||||
{9, D<&IAudioIn::GetReleasedAudioInBuffersAuto>, "GetReleasedAudioInBuffersAuto"},
|
||||
{10, D<&IAudioIn::AppendAudioInBufferAuto>, "AppendUacInBufferAuto"},
|
||||
{11, D<&IAudioIn::GetAudioInBufferCount>, "GetAudioInBufferCount"},
|
||||
{12, D<&IAudioIn::SetDeviceGain>, "SetDeviceGain"},
|
||||
{13, D<&IAudioIn::GetDeviceGain>, "GetDeviceGain"},
|
||||
{14, D<&IAudioIn::FlushAudioInBuffers>, "FlushAudioInBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
process->Open();
|
||||
|
||||
if (impl->GetSystem()
|
||||
.Initialize(device_name, in_params, handle, applet_resource_user_id)
|
||||
.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
|
||||
}
|
||||
}
|
||||
|
||||
IAudioIn::~IAudioIn() {
|
||||
impl->Free();
|
||||
service_context.CloseEvent(event);
|
||||
process->Close();
|
||||
}
|
||||
|
||||
Result IAudioIn::GetAudioInState(Out<u32> out_state) {
|
||||
*out_state = static_cast<u32>(impl->GetState());
|
||||
LOG_DEBUG(Service_Audio, "called. state={}", *out_state);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::Start() {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(impl->StartSystem());
|
||||
}
|
||||
|
||||
Result IAudioIn::Stop() {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(impl->StopSystem());
|
||||
}
|
||||
|
||||
Result IAudioIn::AppendAudioInBuffer(InArray<AudioInBuffer, BufferAttr_HipcMapAlias> buffer,
|
||||
u64 buffer_client_ptr) {
|
||||
R_RETURN(this->AppendAudioInBufferAuto(buffer, buffer_client_ptr));
|
||||
}
|
||||
|
||||
Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioInBuffer, BufferAttr_HipcAutoSelect> buffer,
|
||||
u64 buffer_client_ptr) {
|
||||
if (buffer.empty()) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
|
||||
R_THROW(Audio::ResultInsufficientBuffer);
|
||||
}
|
||||
|
||||
[[maybe_unused]] const auto session_id{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", session_id,
|
||||
buffer_client_ptr);
|
||||
|
||||
R_RETURN(impl->AppendBuffer(buffer[0], buffer_client_ptr));
|
||||
}
|
||||
|
||||
Result IAudioIn::RegisterBufferEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_event = &impl->GetBufferEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::GetReleasedAudioInBuffers(OutArray<u64, BufferAttr_HipcMapAlias> out_audio_buffer,
|
||||
Out<u32> out_count) {
|
||||
R_RETURN(this->GetReleasedAudioInBuffersAuto(out_audio_buffer, out_count));
|
||||
}
|
||||
|
||||
Result IAudioIn::GetReleasedAudioInBuffersAuto(
|
||||
OutArray<u64, BufferAttr_HipcAutoSelect> out_audio_buffer, Out<u32> out_count) {
|
||||
|
||||
if (!out_audio_buffer.empty()) {
|
||||
out_audio_buffer[0] = 0;
|
||||
}
|
||||
*out_count = impl->GetReleasedBuffers(out_audio_buffer);
|
||||
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers",
|
||||
impl->GetSystem().GetSessionId(), *out_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::ContainsAudioInBuffer(Out<bool> out_contains_buffer, u64 buffer_client_ptr) {
|
||||
*out_contains_buffer = impl->ContainsAudioBuffer(buffer_client_ptr);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", buffer_client_ptr,
|
||||
*out_contains_buffer);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::GetAudioInBufferCount(Out<u32> out_buffer_count) {
|
||||
*out_buffer_count = impl->GetBufferCount();
|
||||
LOG_DEBUG(Service_Audio, "called. Buffer count={}", *out_buffer_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::SetDeviceGain(f32 device_gain) {
|
||||
impl->SetVolume(device_gain);
|
||||
LOG_DEBUG(Service_Audio, "called. Gain {}", device_gain);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::GetDeviceGain(Out<f32> out_device_gain) {
|
||||
*out_device_gain = impl->GetVolume();
|
||||
LOG_DEBUG(Service_Audio, "called. Gain {}", *out_device_gain);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioIn::FlushAudioInBuffers(Out<bool> out_flushed) {
|
||||
*out_flushed = impl->FlushAudioInBuffers();
|
||||
LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", *out_flushed);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
53
src/core/hle/service/audio/audio_in.h
Normal file
53
src/core/hle/service/audio/audio_in.h
Normal file
@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/in/audio_in.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
explicit IAudioIn(Core::System& system_, AudioCore::AudioIn::Manager& manager,
|
||||
size_t session_id, const std::string& device_name,
|
||||
const AudioCore::AudioIn::AudioInParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id);
|
||||
~IAudioIn() override;
|
||||
|
||||
std::shared_ptr<AudioCore::AudioIn::In> GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
Result GetAudioInState(Out<u32> out_state);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result AppendAudioInBuffer(
|
||||
InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcMapAlias> buffer,
|
||||
u64 buffer_client_ptr);
|
||||
Result AppendAudioInBufferAuto(
|
||||
InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcAutoSelect> buffer,
|
||||
u64 buffer_client_ptr);
|
||||
Result RegisterBufferEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetReleasedAudioInBuffers(OutArray<u64, BufferAttr_HipcMapAlias> out_audio_buffer,
|
||||
Out<u32> out_count);
|
||||
Result GetReleasedAudioInBuffersAuto(OutArray<u64, BufferAttr_HipcAutoSelect> out_audio_buffer,
|
||||
Out<u32> out_count);
|
||||
Result ContainsAudioInBuffer(Out<bool> out_contains_buffer, u64 buffer_client_ptr);
|
||||
Result GetAudioInBufferCount(Out<u32> out_buffer_count);
|
||||
Result SetDeviceGain(f32 device_gain);
|
||||
Result GetDeviceGain(Out<f32> out_device_gain);
|
||||
Result FlushAudioInBuffers(Out<bool> out_flushed);
|
||||
|
||||
private:
|
||||
Kernel::KProcess* process;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* event;
|
||||
std::shared_ptr<AudioCore::AudioIn::In> impl;
|
||||
Common::ScratchBuffer<u64> released_buffer;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
125
src/core/hle/service/audio/audio_in_manager.cpp
Normal file
125
src/core/hle/service/audio/audio_in_manager.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/audio/audio_in.h"
|
||||
#include "core/hle/service/audio/audio_in_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioIn;
|
||||
|
||||
IAudioInManager::IAudioInManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audin:u"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
|
||||
system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioInManager::ListAudioIns>, "ListAudioIns"},
|
||||
{1, D<&IAudioInManager::OpenAudioIn>, "OpenAudioIn"},
|
||||
{2, D<&IAudioInManager::ListAudioIns>, "ListAudioInsAuto"},
|
||||
{3, D<&IAudioInManager::OpenAudioIn>, "OpenAudioInAuto"},
|
||||
{4, D<&IAudioInManager::ListAudioInsAutoFiltered>, "ListAudioInsAutoFiltered"},
|
||||
{5, D<&IAudioInManager::OpenAudioInProtocolSpecified>, "OpenAudioInProtocolSpecified"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IAudioInManager::~IAudioInManager() = default;
|
||||
|
||||
Result IAudioInManager::ListAudioIns(
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_ins, Out<u32> out_count) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count));
|
||||
}
|
||||
|
||||
Result IAudioInManager::OpenAudioIn(Out<AudioInParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
|
||||
AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name,
|
||||
name, {}, parameter, process_handle, aruid));
|
||||
}
|
||||
|
||||
Result IAudioInManager::ListAudioInsAuto(
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count));
|
||||
}
|
||||
|
||||
Result IAudioInManager::OpenAudioInAuto(
|
||||
Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name,
|
||||
name, {}, parameter, process_handle, aruid));
|
||||
}
|
||||
|
||||
Result IAudioInManager::ListAudioInsAutoFiltered(
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_count = impl->GetDeviceNames(out_audio_ins, true);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioInManager::OpenAudioInProtocolSpecified(
|
||||
Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol,
|
||||
AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (!process_handle) {
|
||||
LOG_ERROR(Service_Audio, "Failed to get process handle");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
if (name.empty() || out_name.empty()) {
|
||||
LOG_ERROR(Service_Audio, "Invalid buffers");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock l{impl->mutex};
|
||||
|
||||
size_t new_session_id{};
|
||||
|
||||
R_TRY(impl->LinkToManager());
|
||||
R_TRY(impl->AcquireSessionId(new_session_id));
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
const auto device_name = Common::StringFromBuffer(name[0].name);
|
||||
*out_audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
|
||||
parameter, process_handle.Get(), aruid.pid);
|
||||
impl->sessions[new_session_id] = (*out_audio_in)->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = aruid.pid;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
*out_parameter_internal =
|
||||
AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format = static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
out_name[0] = AudioDeviceName(out_system.GetName());
|
||||
|
||||
if (protocol == Protocol{}) {
|
||||
if (out_system.IsUac()) {
|
||||
out_name[0] = AudioDeviceName("UacIn");
|
||||
} else {
|
||||
out_name[0] = AudioDeviceName("DeviceIn");
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
57
src/core/hle/service/audio/audio_in_manager.h
Normal file
57
src/core/hle/service/audio/audio_in_manager.h
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_in_manager.h"
|
||||
#include "audio_core/in/audio_in_system.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
using AudioDeviceName = AudioCore::Renderer::AudioDevice::AudioDeviceName;
|
||||
using Protocol = std::array<u32, 2>;
|
||||
|
||||
class IAudioIn;
|
||||
|
||||
class IAudioInManager final : public ServiceFramework<IAudioInManager> {
|
||||
public:
|
||||
explicit IAudioInManager(Core::System& system_);
|
||||
~IAudioInManager() override;
|
||||
|
||||
private:
|
||||
Result ListAudioIns(OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_ins,
|
||||
Out<u32> out_count);
|
||||
Result OpenAudioIn(Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
|
||||
AudioCore::AudioIn::AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid);
|
||||
|
||||
Result ListAudioInsAuto(OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins,
|
||||
Out<u32> out_count);
|
||||
Result OpenAudioInAuto(Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name,
|
||||
AudioCore::AudioIn::AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid);
|
||||
|
||||
Result ListAudioInsAutoFiltered(
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count);
|
||||
Result OpenAudioInProtocolSpecified(
|
||||
Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioIn>> out_audio_in,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol,
|
||||
AudioCore::AudioIn::AudioInParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::AudioIn::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
146
src/core/hle/service/audio/audio_out.cpp
Normal file
146
src/core/hle/service/audio/audio_out.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/out/audio_out.h"
|
||||
#include "audio_core/out/audio_out_system.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/audio/audio_out.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
|
||||
const std::string& device_name, const AudioOutParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
|
||||
event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
|
||||
impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
|
||||
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioOut::GetAudioOutState>, "GetAudioOutState"},
|
||||
{1, D<&IAudioOut::Start>, "Start"},
|
||||
{2, D<&IAudioOut::Stop>, "Stop"},
|
||||
{3, D<&IAudioOut::AppendAudioOutBuffer>, "AppendAudioOutBuffer"},
|
||||
{4, D<&IAudioOut::RegisterBufferEvent>, "RegisterBufferEvent"},
|
||||
{5, D<&IAudioOut::GetReleasedAudioOutBuffers>, "GetReleasedAudioOutBuffers"},
|
||||
{6, D<&IAudioOut::ContainsAudioOutBuffer>, "ContainsAudioOutBuffer"},
|
||||
{7, D<&IAudioOut::AppendAudioOutBufferAuto>, "AppendAudioOutBufferAuto"},
|
||||
{8, D<&IAudioOut::GetReleasedAudioOutBuffersAuto>, "GetReleasedAudioOutBuffersAuto"},
|
||||
{9, D<&IAudioOut::GetAudioOutBufferCount>, "GetAudioOutBufferCount"},
|
||||
{10, D<&IAudioOut::GetAudioOutPlayedSampleCount>, "GetAudioOutPlayedSampleCount"},
|
||||
{11, D<&IAudioOut::FlushAudioOutBuffers>, "FlushAudioOutBuffers"},
|
||||
{12, D<&IAudioOut::SetAudioOutVolume>, "SetAudioOutVolume"},
|
||||
{13, D<&IAudioOut::GetAudioOutVolume>, "GetAudioOutVolume"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
process->Open();
|
||||
}
|
||||
|
||||
IAudioOut::~IAudioOut() {
|
||||
impl->Free();
|
||||
service_context.CloseEvent(event);
|
||||
process->Close();
|
||||
}
|
||||
|
||||
Result IAudioOut::GetAudioOutState(Out<u32> out_state) {
|
||||
*out_state = static_cast<u32>(impl->GetState());
|
||||
LOG_DEBUG(Service_Audio, "called. state={}", *out_state);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::Start() {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(impl->StartSystem());
|
||||
}
|
||||
|
||||
Result IAudioOut::Stop() {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(impl->StopSystem());
|
||||
}
|
||||
|
||||
Result IAudioOut::AppendAudioOutBuffer(
|
||||
InArray<AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) {
|
||||
R_RETURN(this->AppendAudioOutBufferAuto(audio_out_buffer, buffer_client_ptr));
|
||||
}
|
||||
|
||||
Result IAudioOut::AppendAudioOutBufferAuto(
|
||||
InArray<AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) {
|
||||
if (audio_out_buffer.empty()) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
|
||||
R_THROW(Audio::ResultInsufficientBuffer);
|
||||
}
|
||||
|
||||
LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}",
|
||||
impl->GetSystem().GetSessionId(), buffer_client_ptr);
|
||||
R_RETURN(impl->AppendBuffer(audio_out_buffer[0], buffer_client_ptr));
|
||||
}
|
||||
|
||||
Result IAudioOut::RegisterBufferEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_event = &impl->GetBufferEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::GetReleasedAudioOutBuffers(
|
||||
OutArray<u64, BufferAttr_HipcMapAlias> out_audio_buffer, Out<u32> out_count) {
|
||||
R_RETURN(this->GetReleasedAudioOutBuffersAuto(out_audio_buffer, out_count));
|
||||
}
|
||||
|
||||
Result IAudioOut::GetReleasedAudioOutBuffersAuto(
|
||||
OutArray<u64, BufferAttr_HipcAutoSelect> out_audio_buffer, Out<u32> out_count) {
|
||||
|
||||
if (!out_audio_buffer.empty()) {
|
||||
out_audio_buffer[0] = 0;
|
||||
}
|
||||
*out_count = impl->GetReleasedBuffers(out_audio_buffer);
|
||||
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers",
|
||||
impl->GetSystem().GetSessionId(), *out_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::ContainsAudioOutBuffer(Out<bool> out_contains_buffer, u64 buffer_client_ptr) {
|
||||
*out_contains_buffer = impl->ContainsAudioBuffer(buffer_client_ptr);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", buffer_client_ptr,
|
||||
*out_contains_buffer);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::GetAudioOutBufferCount(Out<u32> out_buffer_count) {
|
||||
*out_buffer_count = impl->GetBufferCount();
|
||||
LOG_DEBUG(Service_Audio, "called. Buffer count={}", *out_buffer_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::GetAudioOutPlayedSampleCount(Out<u64> out_played_sample_count) {
|
||||
*out_played_sample_count = impl->GetPlayedSampleCount();
|
||||
LOG_DEBUG(Service_Audio, "called. Played samples={}", *out_played_sample_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::FlushAudioOutBuffers(Out<bool> out_flushed) {
|
||||
*out_flushed = impl->FlushAudioOutBuffers();
|
||||
LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", *out_flushed);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::SetAudioOutVolume(f32 volume) {
|
||||
LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
|
||||
impl->SetVolume(volume);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOut::GetAudioOutVolume(Out<f32> out_volume) {
|
||||
*out_volume = impl->GetVolume();
|
||||
LOG_DEBUG(Service_Audio, "called. Volume={}", *out_volume);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
58
src/core/hle/service/audio/audio_out.h
Normal file
58
src/core/hle/service/audio/audio_out.h
Normal file
@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "audio_core/out/audio_out_system.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioOut : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
|
||||
size_t session_id, const std::string& device_name,
|
||||
const AudioCore::AudioOut::AudioOutParameter& in_params,
|
||||
Kernel::KProcess* handle, u64 applet_resource_user_id);
|
||||
~IAudioOut() override;
|
||||
|
||||
std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
Result GetAudioOutState(Out<u32> out_state);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result AppendAudioOutBuffer(
|
||||
InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer,
|
||||
u64 buffer_client_ptr);
|
||||
Result AppendAudioOutBufferAuto(
|
||||
InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer,
|
||||
u64 buffer_client_ptr);
|
||||
Result RegisterBufferEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetReleasedAudioOutBuffers(OutArray<u64, BufferAttr_HipcMapAlias> out_audio_buffer,
|
||||
Out<u32> out_count);
|
||||
Result GetReleasedAudioOutBuffersAuto(OutArray<u64, BufferAttr_HipcAutoSelect> out_audio_buffer,
|
||||
Out<u32> out_count);
|
||||
Result ContainsAudioOutBuffer(Out<bool> out_contains_buffer, u64 buffer_client_ptr);
|
||||
Result GetAudioOutBufferCount(Out<u32> out_buffer_count);
|
||||
Result GetAudioOutPlayedSampleCount(Out<u64> out_played_sample_count);
|
||||
Result FlushAudioOutBuffers(Out<bool> out_flushed);
|
||||
Result SetAudioOutVolume(f32 volume);
|
||||
Result GetAudioOutVolume(Out<f32> out_volume);
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* event;
|
||||
Kernel::KProcess* process;
|
||||
std::shared_ptr<AudioCore::AudioOut::Out> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
101
src/core/hle/service/audio/audio_out_manager.cpp
Normal file
101
src/core/hle/service/audio/audio_out_manager.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/audio/audio_out.h"
|
||||
#include "core/hle/service/audio/audio_out_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
||||
{1, D<&IAudioOutManager::OpenAudioOut>, "OpenAudioOut"},
|
||||
{2, D<&IAudioOutManager::ListAudioOutsAuto>, "ListAudioOutsAuto"},
|
||||
{3, D<&IAudioOutManager::OpenAudioOutAuto>, "OpenAudioOutAuto"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IAudioOutManager::~IAudioOutManager() = default;
|
||||
|
||||
Result IAudioOutManager::ListAudioOuts(
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_outs, Out<u32> out_count) {
|
||||
R_RETURN(this->ListAudioOutsAuto(out_audio_outs, out_count));
|
||||
}
|
||||
|
||||
Result IAudioOutManager::OpenAudioOut(Out<AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioOut>> out_audio_out,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
|
||||
AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
R_RETURN(this->OpenAudioOutAuto(out_parameter_internal, out_audio_out, out_name, name,
|
||||
parameter, process_handle, aruid));
|
||||
}
|
||||
|
||||
Result IAudioOutManager::ListAudioOutsAuto(
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_outs, Out<u32> out_count) {
|
||||
if (!out_audio_outs.empty()) {
|
||||
out_audio_outs[0] = AudioDeviceName("DeviceOut");
|
||||
*out_count = 1;
|
||||
LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
|
||||
} else {
|
||||
*out_count = 0;
|
||||
LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOutManager::OpenAudioOutAuto(
|
||||
Out<AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioOut>> out_audio_out,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
|
||||
if (!process_handle) {
|
||||
LOG_ERROR(Service_Audio, "Failed to get process handle");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
if (name.empty() || out_name.empty()) {
|
||||
LOG_ERROR(Service_Audio, "Invalid buffers");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
size_t new_session_id{};
|
||||
R_TRY(impl->LinkToManager());
|
||||
R_TRY(impl->AcquireSessionId(new_session_id));
|
||||
|
||||
const auto device_name = Common::StringFromBuffer(name[0].name);
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
|
||||
parameter, process_handle.Get(), aruid.pid);
|
||||
R_TRY(audio_out->GetImpl()->GetSystem().Initialize(device_name, parameter, process_handle.Get(),
|
||||
aruid.pid));
|
||||
|
||||
*out_audio_out = audio_out;
|
||||
impl->sessions[new_session_id] = audio_out->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = aruid.pid;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
*out_parameter_internal =
|
||||
AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format = static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
44
src/core/hle/service/audio/audio_out_manager.h
Normal file
44
src/core/hle/service/audio/audio_out_manager.h
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "audio_core/out/audio_out.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
using AudioDeviceName = AudioCore::Renderer::AudioDevice::AudioDeviceName;
|
||||
class IAudioOut;
|
||||
|
||||
class IAudioOutManager final : public ServiceFramework<IAudioOutManager> {
|
||||
public:
|
||||
explicit IAudioOutManager(Core::System& system_);
|
||||
~IAudioOutManager() override;
|
||||
|
||||
private:
|
||||
Result ListAudioOuts(OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_outs,
|
||||
Out<u32> out_count);
|
||||
Result OpenAudioOut(Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioOut>> out_audio_out,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
|
||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid);
|
||||
Result ListAudioOutsAuto(OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_outs,
|
||||
Out<u32> out_count);
|
||||
Result OpenAudioOutAuto(
|
||||
Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal,
|
||||
Out<SharedPointer<IAudioOut>> out_audio_out,
|
||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
|
||||
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name,
|
||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
139
src/core/hle/service/audio/audio_renderer.cpp
Normal file
139
src/core/hle/service/audio/audio_renderer.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/audio/audio_renderer.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_,
|
||||
AudioCore::AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
Kernel::KProcess* process_handle_, u64 applet_resource_user_id,
|
||||
s32 session_id)
|
||||
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
|
||||
rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
|
||||
impl{std::make_unique<Renderer>(system_, manager, rendered_event)}, process_handle{
|
||||
process_handle_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"},
|
||||
{1, D<&IAudioRenderer::GetSampleCount>, "GetSampleCount"},
|
||||
{2, D<&IAudioRenderer::GetMixBufferCount>, "GetMixBufferCount"},
|
||||
{3, D<&IAudioRenderer::GetState>, "GetState"},
|
||||
{4, D<&IAudioRenderer::RequestUpdate>, "RequestUpdate"},
|
||||
{5, D<&IAudioRenderer::Start>, "Start"},
|
||||
{6, D<&IAudioRenderer::Stop>, "Stop"},
|
||||
{7, D<&IAudioRenderer::QuerySystemEvent>, "QuerySystemEvent"},
|
||||
{8, D<&IAudioRenderer::SetRenderingTimeLimit>, "SetRenderingTimeLimit"},
|
||||
{9, D<&IAudioRenderer::GetRenderingTimeLimit>, "GetRenderingTimeLimit"},
|
||||
{10, D<&IAudioRenderer::RequestUpdateAuto>, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
{12, D<&IAudioRenderer::SetVoiceDropParameter>, "SetVoiceDropParameter"},
|
||||
{13, D<&IAudioRenderer::GetVoiceDropParameter>, "GetVoiceDropParameter"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
process_handle->Open();
|
||||
impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
|
||||
applet_resource_user_id, session_id);
|
||||
}
|
||||
|
||||
IAudioRenderer::~IAudioRenderer() {
|
||||
impl->Finalize();
|
||||
service_context.CloseEvent(rendered_event);
|
||||
process_handle->Close();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::GetSampleRate(Out<u32> out_sample_rate) {
|
||||
*out_sample_rate = impl->GetSystem().GetSampleRate();
|
||||
LOG_DEBUG(Service_Audio, "called. Sample rate {}", *out_sample_rate);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::GetSampleCount(Out<u32> out_sample_count) {
|
||||
*out_sample_count = impl->GetSystem().GetSampleCount();
|
||||
LOG_DEBUG(Service_Audio, "called. Sample count {}", *out_sample_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::GetState(Out<u32> out_state) {
|
||||
*out_state = !impl->GetSystem().IsActive();
|
||||
LOG_DEBUG(Service_Audio, "called, state {}", *out_state);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::GetMixBufferCount(Out<u32> out_mix_buffer_count) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_mix_buffer_count = impl->GetSystem().GetMixBufferCount();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::RequestUpdate(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_performance_buffer,
|
||||
InBuffer<BufferAttr_HipcMapAlias> input) {
|
||||
R_RETURN(this->RequestUpdateAuto(out_buffer, out_performance_buffer, input));
|
||||
}
|
||||
|
||||
Result IAudioRenderer::RequestUpdateAuto(
|
||||
OutBuffer<BufferAttr_HipcAutoSelect> out_buffer,
|
||||
OutBuffer<BufferAttr_HipcAutoSelect> out_performance_buffer,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> input) {
|
||||
LOG_TRACE(Service_Audio, "called");
|
||||
|
||||
const auto result = impl->RequestUpdate(input, out_performance_buffer, out_buffer);
|
||||
if (result.IsFailure()) {
|
||||
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.GetDescription());
|
||||
}
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
Result IAudioRenderer::Start() {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
impl->Start();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::Stop() {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
impl->Stop();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::QuerySystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_UNLESS(impl->GetSystem().GetExecutionMode() != AudioCore::ExecutionMode::Manual,
|
||||
Audio::ResultNotSupported);
|
||||
*out_event = &rendered_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::SetRenderingTimeLimit(u32 rendering_time_limit) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
impl->GetSystem().SetRenderingTimeLimit(rendering_time_limit);
|
||||
;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::GetRenderingTimeLimit(Out<u32> out_rendering_time_limit) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_rendering_time_limit = impl->GetSystem().GetRenderingTimeLimit();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::SetVoiceDropParameter(f32 voice_drop_parameter) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
impl->GetSystem().SetVoiceDropParameter(voice_drop_parameter);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRenderer::GetVoiceDropParameter(Out<f32> out_voice_drop_parameter) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
*out_voice_drop_parameter = impl->GetSystem().GetVoiceDropParameter();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
54
src/core/hle/service/audio/audio_renderer.h
Normal file
54
src/core/hle/service/audio/audio_renderer.h
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/renderer/audio_renderer.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
explicit IAudioRenderer(Core::System& system_, AudioCore::Renderer::Manager& manager_,
|
||||
AudioCore::AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
Kernel::KProcess* process_handle_, u64 applet_resource_user_id,
|
||||
s32 session_id);
|
||||
~IAudioRenderer() override;
|
||||
|
||||
private:
|
||||
Result GetSampleRate(Out<u32> out_sample_rate);
|
||||
Result GetSampleCount(Out<u32> out_sample_count);
|
||||
Result GetState(Out<u32> out_state);
|
||||
Result GetMixBufferCount(Out<u32> out_mix_buffer_count);
|
||||
Result RequestUpdate(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_performance_buffer,
|
||||
InBuffer<BufferAttr_HipcMapAlias> input);
|
||||
Result RequestUpdateAuto(OutBuffer<BufferAttr_HipcAutoSelect> out_buffer,
|
||||
OutBuffer<BufferAttr_HipcAutoSelect> out_performance_buffer,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> input);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result QuerySystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result SetRenderingTimeLimit(u32 rendering_time_limit);
|
||||
Result GetRenderingTimeLimit(Out<u32> out_rendering_time_limit);
|
||||
Result SetVoiceDropParameter(f32 voice_drop_parameter);
|
||||
Result GetVoiceDropParameter(Out<f32> out_voice_drop_parameter);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* rendered_event;
|
||||
AudioCore::Renderer::Manager& manager;
|
||||
std::unique_ptr<AudioCore::Renderer::Renderer> impl;
|
||||
Kernel::KProcess* process_handle;
|
||||
Common::ScratchBuffer<u8> output_buffer;
|
||||
Common::ScratchBuffer<u8> performance_buffer;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
104
src/core/hle/service/audio/audio_renderer_manager.cpp
Normal file
104
src/core/hle/service/audio/audio_renderer_manager.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_render_manager.h"
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/audio/audio_device.h"
|
||||
#include "core/hle/service/audio/audio_renderer.h"
|
||||
#include "core/hle/service/audio/audio_renderer_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
||||
{1, D<&IAudioRendererManager::GetWorkBufferSize>, "GetWorkBufferSize"},
|
||||
{2, D<&IAudioRendererManager::GetAudioDeviceService>, "GetAudioDeviceService"},
|
||||
{3, nullptr, "OpenAudioRendererForManualExecution"},
|
||||
{4, D<&IAudioRendererManager::GetAudioDeviceServiceWithRevisionInfo>, "GetAudioDeviceServiceWithRevisionInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IAudioRendererManager::~IAudioRendererManager() = default;
|
||||
|
||||
Result IAudioRendererManager::OpenAudioRenderer(
|
||||
Out<SharedPointer<IAudioRenderer>> out_audio_renderer,
|
||||
AudioCore::AudioRendererParameterInternal parameter,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle, u64 tmem_size,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
|
||||
LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
|
||||
R_THROW(Audio::ResultOutOfSessions);
|
||||
}
|
||||
|
||||
const auto session_id{impl->GetSessionId()};
|
||||
if (session_id == -1) {
|
||||
LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
|
||||
R_THROW(Audio::ResultOutOfSessions);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
|
||||
impl->GetSessionCount());
|
||||
|
||||
*out_audio_renderer =
|
||||
std::make_shared<IAudioRenderer>(system, *impl, parameter, tmem_handle.Get(), tmem_size,
|
||||
process_handle.Get(), aruid.pid, session_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRendererManager::GetWorkBufferSize(Out<u64> out_size,
|
||||
AudioCore::AudioRendererParameterInternal params) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
R_TRY(impl->GetWorkBufferSize(params, *out_size))
|
||||
|
||||
std::string output_info{};
|
||||
output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
|
||||
output_info +=
|
||||
fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
|
||||
output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
|
||||
static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
|
||||
output_info += fmt::format(
|
||||
"\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
|
||||
"{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
|
||||
"Context {:04X}",
|
||||
params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
|
||||
params.splitter_destinations, params.voices, params.perf_frames,
|
||||
params.external_context_size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
|
||||
output_info, *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRendererManager::GetAudioDeviceService(
|
||||
Out<SharedPointer<IAudioDevice>> out_audio_device, ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called, aruid={:#x}", aruid.pid);
|
||||
*out_audio_device = std::make_shared<IAudioDevice>(
|
||||
system, aruid.pid, Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioRendererManager::GetAudioDeviceServiceWithRevisionInfo(
|
||||
Out<SharedPointer<IAudioDevice>> out_audio_device, u32 revision,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_Audio, "called, revision={} aruid={:#x}", AudioCore::GetRevisionNum(revision),
|
||||
aruid.pid);
|
||||
*out_audio_device =
|
||||
std::make_shared<IAudioDevice>(system, aruid.pid, revision, num_audio_devices++);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
37
src/core/hle/service/audio/audio_renderer_manager.h
Normal file
37
src/core/hle/service/audio/audio_renderer_manager.h
Normal file
@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_render_manager.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioDevice;
|
||||
class IAudioRenderer;
|
||||
|
||||
class IAudioRendererManager final : public ServiceFramework<IAudioRendererManager> {
|
||||
public:
|
||||
explicit IAudioRendererManager(Core::System& system_);
|
||||
~IAudioRendererManager() override;
|
||||
|
||||
private:
|
||||
Result OpenAudioRenderer(Out<SharedPointer<IAudioRenderer>> out_audio_renderer,
|
||||
AudioCore::AudioRendererParameterInternal parameter,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle, u64 tmem_size,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
ClientAppletResourceUserId aruid);
|
||||
Result GetWorkBufferSize(Out<u64> out_size,
|
||||
AudioCore::AudioRendererParameterInternal parameter);
|
||||
Result GetAudioDeviceService(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||
ClientAppletResourceUserId aruid);
|
||||
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||
u32 revision, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
||||
u32 num_audio_devices{0};
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,323 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/out/audio_out_system.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/audio/audout_u.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
|
||||
size_t session_id, const std::string& device_name,
|
||||
const AudioOutParameter& in_params, Kernel::KProcess* handle,
|
||||
u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
|
||||
event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
|
||||
impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
|
||||
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
{1, &IAudioOut::Start, "Start"},
|
||||
{2, &IAudioOut::Stop, "Stop"},
|
||||
{3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
|
||||
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
|
||||
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
|
||||
{7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
|
||||
{8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
|
||||
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
|
||||
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
|
||||
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
|
||||
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
|
||||
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
process->Open();
|
||||
}
|
||||
|
||||
~IAudioOut() override {
|
||||
impl->Free();
|
||||
service_context.CloseEvent(event);
|
||||
process->Close();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAudioOutState(HLERequestContext& ctx) {
|
||||
const auto state = static_cast<u32>(impl->GetState());
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. State={}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(state);
|
||||
}
|
||||
|
||||
void Start(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto result = impl->StartSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Stop(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto result = impl->StopSystem();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void AppendAudioOutBuffer(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 tag = rp.PopRaw<u64>();
|
||||
|
||||
const auto in_buffer_size{ctx.GetReadBufferSize()};
|
||||
if (in_buffer_size < sizeof(AudioOutBuffer)) {
|
||||
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
|
||||
}
|
||||
|
||||
const auto& in_buffer = ctx.ReadBuffer();
|
||||
AudioOutBuffer buffer{};
|
||||
std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
|
||||
|
||||
LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}",
|
||||
impl->GetSystem().GetSessionId(), tag);
|
||||
|
||||
auto result = impl->AppendBuffer(buffer, tag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void RegisterBufferEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& buffer_event = impl->GetBufferEvent();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void GetReleasedAudioOutBuffers(HLERequestContext& ctx) {
|
||||
const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
|
||||
released_buffer.resize_destructive(write_buffer_size);
|
||||
released_buffer[0] = 0;
|
||||
|
||||
const auto count = impl->GetReleasedBuffers(released_buffer);
|
||||
|
||||
ctx.WriteBuffer(released_buffer);
|
||||
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers",
|
||||
impl->GetSystem().GetSessionId(), count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(count);
|
||||
}
|
||||
|
||||
void ContainsAudioOutBuffer(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_queued);
|
||||
}
|
||||
|
||||
void GetAudioOutBufferCount(HLERequestContext& ctx) {
|
||||
const auto buffer_count = impl->GetBufferCount();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_count);
|
||||
}
|
||||
|
||||
void GetAudioOutPlayedSampleCount(HLERequestContext& ctx) {
|
||||
const auto samples_played = impl->GetPlayedSampleCount();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(samples_played);
|
||||
}
|
||||
|
||||
void FlushAudioOutBuffers(HLERequestContext& ctx) {
|
||||
bool flushed{impl->FlushAudioOutBuffers()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(flushed);
|
||||
}
|
||||
|
||||
void SetAudioOutVolume(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto volume = rp.Pop<f32>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
|
||||
|
||||
impl->SetVolume(volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetAudioOutVolume(HLERequestContext& ctx) {
|
||||
const auto volume = impl->GetVolume();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* event;
|
||||
Kernel::KProcess* process;
|
||||
std::shared_ptr<AudioCore::AudioOut::Out> impl;
|
||||
Common::ScratchBuffer<u64> released_buffer;
|
||||
};
|
||||
|
||||
AudOutU::AudOutU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"},
|
||||
impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
|
||||
{3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
AudOutU::~AudOutU() = default;
|
||||
|
||||
void AudOutU::ListAudioOuts(HLERequestContext& ctx) {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
std::scoped_lock l{impl->mutex};
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
if (write_count > 0) {
|
||||
device_names.emplace_back("DeviceOut");
|
||||
LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
|
||||
} else {
|
||||
LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(device_names);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(device_names.size()));
|
||||
}
|
||||
|
||||
void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto in_params{rp.PopRaw<AudioOutParameter>()};
|
||||
auto applet_resource_user_id{rp.PopRaw<u64>()};
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
auto device_name = Common::StringFromBuffer(device_name_data);
|
||||
auto handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
|
||||
if (process.IsNull()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to get process handle");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
auto link{impl->LinkToManager()};
|
||||
if (link.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(link);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t new_session_id{};
|
||||
auto result{impl->AcquireSessionId(new_session_id)};
|
||||
if (result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
||||
impl->num_free_sessions);
|
||||
|
||||
auto audio_out =
|
||||
std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, in_params,
|
||||
process.GetPointerUnsafe(), applet_resource_user_id);
|
||||
result = audio_out->GetImpl()->GetSystem().Initialize(
|
||||
device_name, in_params, process.GetPointerUnsafe(), applet_resource_user_id);
|
||||
if (result.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
impl->sessions[new_session_id] = audio_out->GetImpl();
|
||||
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
|
||||
|
||||
auto& out_system = impl->sessions[new_session_id]->GetSystem();
|
||||
AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
|
||||
.channel_count = out_system.GetChannelCount(),
|
||||
.sample_format =
|
||||
static_cast<u32>(out_system.GetSampleFormat()),
|
||||
.state = static_cast<u32>(out_system.GetState())};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
ctx.WriteBuffer(out_system.GetName());
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<AudioOutParameterInternal>(out_params);
|
||||
rb.PushIpcInterface<IAudioOut>(audio_out);
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,37 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "audio_core/out/audio_out.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
class Manager;
|
||||
class Out;
|
||||
} // namespace AudioCore::AudioOut
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioOut;
|
||||
|
||||
class AudOutU final : public ServiceFramework<AudOutU> {
|
||||
public:
|
||||
explicit AudOutU(Core::System& system_);
|
||||
~AudOutU() override;
|
||||
|
||||
private:
|
||||
void ListAudioOuts(HLERequestContext& ctx);
|
||||
void OpenAudioOut(HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,552 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/common/audio_renderer_parameter.h"
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
#include "audio_core/renderer/audio_renderer.h"
|
||||
#include "audio_core/renderer/voice/voice_info.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_ranges.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/audio/audren_u.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
explicit IAudioRenderer(Core::System& system_, Manager& manager_,
|
||||
AudioCore::AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle, Kernel::KProcess& process_,
|
||||
u64 applet_resource_user_id, s32 session_id)
|
||||
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
|
||||
rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
|
||||
impl{std::make_unique<Renderer>(system_, manager, rendered_event)}, process{process_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
|
||||
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
|
||||
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetState, "GetState"},
|
||||
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
|
||||
{5, &IAudioRenderer::Start, "Start"},
|
||||
{6, &IAudioRenderer::Stop, "Stop"},
|
||||
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
|
||||
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
|
||||
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
||||
{10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
{12, &IAudioRenderer::SetVoiceDropParameter, "SetVoiceDropParameter"},
|
||||
{13, &IAudioRenderer::GetVoiceDropParameter, "GetVoiceDropParameter"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
process.Open();
|
||||
impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, process,
|
||||
applet_resource_user_id, session_id);
|
||||
}
|
||||
|
||||
~IAudioRenderer() override {
|
||||
impl->Finalize();
|
||||
service_context.CloseEvent(rendered_event);
|
||||
process.Close();
|
||||
}
|
||||
|
||||
private:
|
||||
void GetSampleRate(HLERequestContext& ctx) {
|
||||
const auto sample_rate{impl->GetSystem().GetSampleRate()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(sample_rate);
|
||||
}
|
||||
|
||||
void GetSampleCount(HLERequestContext& ctx) {
|
||||
const auto sample_count{impl->GetSystem().GetSampleCount()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(sample_count);
|
||||
}
|
||||
|
||||
void GetState(HLERequestContext& ctx) {
|
||||
const u32 state{!impl->GetSystem().IsActive()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called, state {}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(state);
|
||||
}
|
||||
|
||||
void GetMixBufferCount(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(buffer_count);
|
||||
}
|
||||
|
||||
void RequestUpdate(HLERequestContext& ctx) {
|
||||
LOG_TRACE(Service_Audio, "called");
|
||||
|
||||
const auto input{ctx.ReadBuffer(0)};
|
||||
|
||||
// These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
|
||||
// checking size 0. Performance size is 0 for most games.
|
||||
|
||||
auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
|
||||
if (is_buffer_b) {
|
||||
const auto buffersB{ctx.BufferDescriptorB()};
|
||||
output_buffer.resize_destructive(buffersB[0].Size());
|
||||
performance_buffer.resize_destructive(buffersB[1].Size());
|
||||
} else {
|
||||
const auto buffersC{ctx.BufferDescriptorC()};
|
||||
output_buffer.resize_destructive(buffersC[0].Size());
|
||||
performance_buffer.resize_destructive(buffersC[1].Size());
|
||||
}
|
||||
|
||||
auto result = impl->RequestUpdate(input, performance_buffer, output_buffer);
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
if (is_buffer_b) {
|
||||
ctx.WriteBufferB(output_buffer.data(), output_buffer.size(), 0);
|
||||
ctx.WriteBufferB(performance_buffer.data(), performance_buffer.size(), 1);
|
||||
} else {
|
||||
ctx.WriteBufferC(output_buffer.data(), output_buffer.size(), 0);
|
||||
ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!",
|
||||
result.GetDescription());
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Start(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
impl->Start();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Stop(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
impl->Stop();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void QuerySystemEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(Audio::ResultNotSupported);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(rendered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void SetRenderingTimeLimit(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto limit = rp.PopRaw<u32>();
|
||||
|
||||
auto& system_ = impl->GetSystem();
|
||||
system_.SetRenderingTimeLimit(limit);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetRenderingTimeLimit(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& system_ = impl->GetSystem();
|
||||
auto time = system_.GetRenderingTimeLimit();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(time);
|
||||
}
|
||||
|
||||
void ExecuteAudioRendererRendering(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void SetVoiceDropParameter(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto voice_drop_param{rp.Pop<f32>()};
|
||||
|
||||
auto& system_ = impl->GetSystem();
|
||||
system_.SetVoiceDropParameter(voice_drop_param);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetVoiceDropParameter(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto& system_ = impl->GetSystem();
|
||||
auto voice_drop_param{system_.GetVoiceDropParameter()};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(voice_drop_param);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* rendered_event;
|
||||
Manager& manager;
|
||||
std::unique_ptr<Renderer> impl;
|
||||
Kernel::KProcess& process;
|
||||
Common::ScratchBuffer<u8> output_buffer;
|
||||
Common::ScratchBuffer<u8> performance_buffer;
|
||||
};
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
|
||||
public:
|
||||
explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
|
||||
u32 device_num)
|
||||
: ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"},
|
||||
impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)},
|
||||
event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
{2, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolume"},
|
||||
{3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{6, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceNameAuto"},
|
||||
{7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{8, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
|
||||
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
|
||||
{13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
|
||||
{14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
event->Signal();
|
||||
}
|
||||
|
||||
~IAudioDevice() override {
|
||||
service_context.CloseEvent(event);
|
||||
}
|
||||
|
||||
private:
|
||||
void ListAudioDeviceName(HLERequestContext& ctx) {
|
||||
const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
|
||||
|
||||
std::vector<AudioDevice::AudioDeviceName> out_names{};
|
||||
|
||||
const u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
|
||||
|
||||
std::string out{};
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
std::string a{};
|
||||
u32 j = 0;
|
||||
while (out_names[i].name[j] != '\0') {
|
||||
a += out_names[i].name[j];
|
||||
j++;
|
||||
}
|
||||
out += "\n\t" + a;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
ctx.WriteBuffer(out_names);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void SetAudioDeviceOutputVolume(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const f32 volume = rp.Pop<f32>();
|
||||
|
||||
const auto device_name_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(device_name_buffer);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
|
||||
|
||||
if (name == "AudioTvOutput") {
|
||||
impl->SetDeviceVolumes(volume);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetAudioDeviceOutputVolume(HLERequestContext& ctx) {
|
||||
const auto device_name_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(device_name_buffer);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Name={}", name);
|
||||
|
||||
f32 volume{1.0f};
|
||||
if (name == "AudioTvOutput") {
|
||||
volume = impl->GetDeviceVolume(name);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
void GetActiveAudioDeviceName(HLERequestContext& ctx) {
|
||||
const auto write_size = ctx.GetWriteBufferSize();
|
||||
std::string out_name{"AudioTvOutput"};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
|
||||
|
||||
out_name.resize(write_size);
|
||||
|
||||
ctx.WriteBuffer(out_name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void QueryAudioDeviceSystemEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetActiveChannelCount(HLERequestContext& ctx) {
|
||||
const auto& sink{system.AudioCore().GetOutputSink()};
|
||||
u32 channel_count{sink.GetSystemChannels()};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(channel_count);
|
||||
}
|
||||
|
||||
void QueryAudioDeviceInputEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void QueryAudioDeviceOutputEvent(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void ListAudioOutputDeviceName(HLERequestContext& ctx) {
|
||||
const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
|
||||
|
||||
std::vector<AudioDevice::AudioDeviceName> out_names{};
|
||||
|
||||
const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
|
||||
|
||||
std::string out{};
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
std::string a{};
|
||||
u32 j = 0;
|
||||
while (out_names[i].name[j] != '\0') {
|
||||
a += out_names[i].name[j];
|
||||
j++;
|
||||
}
|
||||
out += "\n\t" + a;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
ctx.WriteBuffer(out_names);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioDevice> impl;
|
||||
Kernel::KEvent* event;
|
||||
};
|
||||
|
||||
AudRenU::AudRenU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"},
|
||||
service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
|
||||
{1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
|
||||
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
|
||||
{3, nullptr, "OpenAudioRendererForManualExecution"},
|
||||
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
AudRenU::~AudRenU() = default;
|
||||
|
||||
void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
AudioCore::AudioRendererParameterInternal params;
|
||||
rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
|
||||
rp.Skip(1, false);
|
||||
auto transfer_memory_size = rp.Pop<u64>();
|
||||
auto applet_resource_user_id = rp.Pop<u64>();
|
||||
auto transfer_memory_handle = ctx.GetCopyHandle(0);
|
||||
auto process_handle = ctx.GetCopyHandle(1);
|
||||
|
||||
if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
|
||||
LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(Audio::ResultOutOfSessions);
|
||||
return;
|
||||
}
|
||||
|
||||
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle).GetPointerUnsafe()};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
const auto session_id{impl->GetSessionId()};
|
||||
if (session_id == -1) {
|
||||
LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(Audio::ResultOutOfSessions);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
|
||||
impl->GetSessionCount());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
|
||||
transfer_memory_size, process_handle, *process,
|
||||
applet_resource_user_id, session_id);
|
||||
}
|
||||
|
||||
void AudRenU::GetWorkBufferSize(HLERequestContext& ctx) {
|
||||
AudioCore::AudioRendererParameterInternal params;
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
|
||||
|
||||
u64 size{0};
|
||||
auto result = impl->GetWorkBufferSize(params, size);
|
||||
|
||||
std::string output_info{};
|
||||
output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
|
||||
output_info +=
|
||||
fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
|
||||
output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
|
||||
static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
|
||||
output_info += fmt::format(
|
||||
"\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
|
||||
"{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
|
||||
"Context {:04X}",
|
||||
params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
|
||||
params.splitter_destinations, params.voices, params.perf_frames,
|
||||
params.external_context_size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
|
||||
output_info, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push<u64>(size);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceService(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto applet_resource_user_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
|
||||
::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
|
||||
}
|
||||
|
||||
void AudRenU::OpenAudioRendererForManualExecution(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_Audio, "called. Implement me!");
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u32 revision;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
|
||||
AudioCore::GetRevisionNum(revision), applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
|
||||
num_audio_devices++);
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,35 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/audio_render_manager.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Audio {
|
||||
class IAudioRenderer;
|
||||
|
||||
class AudRenU final : public ServiceFramework<AudRenU> {
|
||||
public:
|
||||
explicit AudRenU(Core::System& system_);
|
||||
~AudRenU() override;
|
||||
|
||||
private:
|
||||
void OpenAudioRenderer(HLERequestContext& ctx);
|
||||
void GetWorkBufferSize(HLERequestContext& ctx);
|
||||
void GetAudioDeviceService(HLERequestContext& ctx);
|
||||
void OpenAudioRendererForManualExecution(HLERequestContext& ctx);
|
||||
void GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
||||
u32 num_audio_devices{0};
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/audio/audrec_u.h"
|
||||
#include "core/hle/service/audio/final_output_recorder_manager.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@ -30,13 +30,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
AudRecU::AudRecU(Core::System& system_) : ServiceFramework{system_, "audrec:u"} {
|
||||
IFinalOutputRecorderManager::IFinalOutputRecorderManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audrec:u"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenFinalOutputRecorder"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
AudRecU::~AudRecU() = default;
|
||||
IFinalOutputRecorderManager::~IFinalOutputRecorderManager() = default;
|
||||
|
||||
} // namespace Service::Audio
|
@ -11,10 +11,10 @@ class System;
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class AudRecA final : public ServiceFramework<AudRecA> {
|
||||
class IFinalOutputRecorderManager final : public ServiceFramework<IFinalOutputRecorderManager> {
|
||||
public:
|
||||
explicit AudRecA(Core::System& system_);
|
||||
~AudRecA() override;
|
||||
explicit IFinalOutputRecorderManager(Core::System& system_);
|
||||
~IFinalOutputRecorderManager() override;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,11 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/audio/audrec_a.h"
|
||||
#include "core/hle/service/audio/final_output_recorder_manager_for_applet.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
AudRecA::AudRecA(Core::System& system_) : ServiceFramework{system_, "audrec:a"} {
|
||||
IFinalOutputRecorderManagerForApplet::IFinalOutputRecorderManagerForApplet(Core::System& system_)
|
||||
: ServiceFramework{system_, "audrec:a"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestSuspend"},
|
||||
@ -16,6 +17,6 @@ AudRecA::AudRecA(Core::System& system_) : ServiceFramework{system_, "audrec:a"}
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
AudRecA::~AudRecA() = default;
|
||||
IFinalOutputRecorderManagerForApplet::~IFinalOutputRecorderManagerForApplet() = default;
|
||||
|
||||
} // namespace Service::Audio
|
@ -11,10 +11,11 @@ class System;
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class AudRecU final : public ServiceFramework<AudRecU> {
|
||||
class IFinalOutputRecorderManagerForApplet final
|
||||
: public ServiceFramework<IFinalOutputRecorderManagerForApplet> {
|
||||
public:
|
||||
explicit AudRecU(Core::System& system_);
|
||||
~AudRecU() override;
|
||||
explicit IFinalOutputRecorderManagerForApplet(Core::System& system_);
|
||||
~IFinalOutputRecorderManagerForApplet() override;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
145
src/core/hle/service/audio/hardware_opus_decoder.cpp
Normal file
145
src/core/hle/service/audio/hardware_opus_decoder.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/audio/hardware_opus_decoder.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
using namespace AudioCore::OpusDecoder;
|
||||
|
||||
IHardwareOpusDecoder::IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus)
|
||||
: ServiceFramework{system_, "IHardwareOpusDecoder"},
|
||||
impl{std::make_unique<AudioCore::OpusDecoder::OpusDecoder>(system_, hardware_opus)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IHardwareOpusDecoder::DecodeInterleavedOld>, "DecodeInterleavedOld"},
|
||||
{1, D<&IHardwareOpusDecoder::SetContext>, "SetContext"},
|
||||
{2, D<&IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld>, "DecodeInterleavedForMultiStreamOld"},
|
||||
{3, D<&IHardwareOpusDecoder::SetContextForMultiStream>, "SetContextForMultiStream"},
|
||||
{4, D<&IHardwareOpusDecoder::DecodeInterleavedWithPerfOld>, "DecodeInterleavedWithPerfOld"},
|
||||
{5, D<&IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld>, "DecodeInterleavedForMultiStreamWithPerfOld"},
|
||||
{6, D<&IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld>, "DecodeInterleavedWithPerfAndResetOld"},
|
||||
{7, D<&IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld>, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
|
||||
{8, D<&IHardwareOpusDecoder::DecodeInterleaved>, "DecodeInterleaved"},
|
||||
{9, D<&IHardwareOpusDecoder::DecodeInterleavedForMultiStream>, "DecodeInterleavedForMultiStream"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IHardwareOpusDecoder::~IHardwareOpusDecoder() = default;
|
||||
|
||||
Result IHardwareOpusDecoder::Initialize(const OpusParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size) {
|
||||
return impl->Initialize(params, transfer_memory, transfer_memory_size);
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::Initialize(const OpusMultiStreamParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size) {
|
||||
return impl->Initialize(params, transfer_memory, transfer_memory_size);
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedOld(OutBuffer<BufferAttr_HipcMapAlias> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data) {
|
||||
R_TRY(impl->DecodeInterleaved(out_data_size, nullptr, out_sample_count, opus_data, out_pcm_data,
|
||||
false));
|
||||
LOG_DEBUG(Service_Audio, "bytes read {:#x} samples generated {}", *out_data_size,
|
||||
*out_sample_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::SetContext(InBuffer<BufferAttr_HipcMapAlias> decoder_context) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(impl->SetContext(decoder_context));
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_pcm_data, Out<u32> out_data_size,
|
||||
Out<u32> out_sample_count, InBuffer<BufferAttr_HipcMapAlias> opus_data) {
|
||||
R_TRY(impl->DecodeInterleavedForMultiStream(out_data_size, nullptr, out_sample_count, opus_data,
|
||||
out_pcm_data, false));
|
||||
LOG_DEBUG(Service_Audio, "bytes read {:#x} samples generated {}", *out_data_size,
|
||||
*out_sample_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::SetContextForMultiStream(
|
||||
InBuffer<BufferAttr_HipcMapAlias> decoder_context) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
R_RETURN(impl->SetContext(decoder_context));
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedWithPerfOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data) {
|
||||
R_TRY(impl->DecodeInterleaved(out_data_size, out_time_taken, out_sample_count, opus_data,
|
||||
out_pcm_data, false));
|
||||
LOG_DEBUG(Service_Audio, "bytes read {:#x} samples generated {} time taken {}", *out_data_size,
|
||||
*out_sample_count, *out_time_taken);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data) {
|
||||
R_TRY(impl->DecodeInterleavedForMultiStream(out_data_size, out_time_taken, out_sample_count,
|
||||
opus_data, out_pcm_data, false));
|
||||
LOG_DEBUG(Service_Audio, "bytes read {:#x} samples generated {} time taken {}", *out_data_size,
|
||||
*out_sample_count, *out_time_taken);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data, bool reset) {
|
||||
R_TRY(impl->DecodeInterleaved(out_data_size, out_time_taken, out_sample_count, opus_data,
|
||||
out_pcm_data, reset));
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read {:#x} samples generated {} time taken {}", reset,
|
||||
*out_data_size, *out_sample_count, *out_time_taken);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data, bool reset) {
|
||||
R_TRY(impl->DecodeInterleavedForMultiStream(out_data_size, out_time_taken, out_sample_count,
|
||||
opus_data, out_pcm_data, reset));
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read {:#x} samples generated {} time taken {}", reset,
|
||||
*out_data_size, *out_sample_count, *out_time_taken);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleaved(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> opus_data,
|
||||
bool reset) {
|
||||
R_TRY(impl->DecodeInterleaved(out_data_size, out_time_taken, out_sample_count, opus_data,
|
||||
out_pcm_data, reset));
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read {:#x} samples generated {} time taken {}", reset,
|
||||
*out_data_size, *out_sample_count, *out_time_taken);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoder::DecodeInterleavedForMultiStream(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> opus_data,
|
||||
bool reset) {
|
||||
R_TRY(impl->DecodeInterleavedForMultiStream(out_data_size, out_time_taken, out_sample_count,
|
||||
opus_data, out_pcm_data, reset));
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read {:#x} samples generated {} time taken {}", reset,
|
||||
*out_data_size, *out_sample_count, *out_time_taken);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
63
src/core/hle/service/audio/hardware_opus_decoder.h
Normal file
63
src/core/hle/service/audio/hardware_opus_decoder.h
Normal file
@ -0,0 +1,63 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/opus/decoder.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> {
|
||||
public:
|
||||
explicit IHardwareOpusDecoder(Core::System& system_,
|
||||
AudioCore::OpusDecoder::HardwareOpus& hardware_opus);
|
||||
~IHardwareOpusDecoder() override;
|
||||
|
||||
Result Initialize(const AudioCore::OpusDecoder::OpusParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size);
|
||||
Result Initialize(const AudioCore::OpusDecoder::OpusMultiStreamParametersEx& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size);
|
||||
|
||||
private:
|
||||
Result DecodeInterleavedOld(OutBuffer<BufferAttr_HipcMapAlias> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data);
|
||||
Result SetContext(InBuffer<BufferAttr_HipcMapAlias> decoder_context);
|
||||
Result DecodeInterleavedForMultiStreamOld(OutBuffer<BufferAttr_HipcMapAlias> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data);
|
||||
Result SetContextForMultiStream(InBuffer<BufferAttr_HipcMapAlias> decoder_context);
|
||||
Result DecodeInterleavedWithPerfOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data);
|
||||
Result DecodeInterleavedForMultiStreamWithPerfOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data);
|
||||
Result DecodeInterleavedWithPerfAndResetOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data, bool reset);
|
||||
Result DecodeInterleavedForMultiStreamWithPerfAndResetOld(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias> opus_data, bool reset);
|
||||
Result DecodeInterleaved(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> opus_data,
|
||||
bool reset);
|
||||
Result DecodeInterleavedForMultiStream(
|
||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_pcm_data,
|
||||
Out<u32> out_data_size, Out<u32> out_sample_count, Out<u64> out_time_taken,
|
||||
InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> opus_data,
|
||||
bool reset);
|
||||
|
||||
std::unique_ptr<AudioCore::OpusDecoder::OpusDecoder> impl;
|
||||
Common::ScratchBuffer<u8> output_data;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
156
src/core/hle/service/audio/hardware_opus_decoder_manager.cpp
Normal file
156
src/core/hle/service/audio/hardware_opus_decoder_manager.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/audio/hardware_opus_decoder.h"
|
||||
#include "core/hle/service/audio/hardware_opus_decoder_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
using namespace AudioCore::OpusDecoder;
|
||||
|
||||
IHardwareOpusDecoderManager::IHardwareOpusDecoderManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IHardwareOpusDecoderManager::OpenHardwareOpusDecoder>, "OpenHardwareOpusDecoder"},
|
||||
{1, D<&IHardwareOpusDecoderManager::GetWorkBufferSize>, "GetWorkBufferSize"},
|
||||
{2, D<&IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStream>, "OpenOpusDecoderForMultiStream"},
|
||||
{3, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStream>, "GetWorkBufferSizeForMultiStream"},
|
||||
{4, D<&IHardwareOpusDecoderManager::OpenHardwareOpusDecoderEx>, "OpenHardwareOpusDecoderEx"},
|
||||
{5, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeEx>, "GetWorkBufferSizeEx"},
|
||||
{6, D<&IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStreamEx>, "OpenHardwareOpusDecoderForMultiStreamEx"},
|
||||
{7, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamEx>, "GetWorkBufferSizeForMultiStreamEx"},
|
||||
{8, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeExEx>, "GetWorkBufferSizeExEx"},
|
||||
{9, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx>, "GetWorkBufferSizeForMultiStreamExEx"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IHardwareOpusDecoderManager::~IHardwareOpusDecoderManager() = default;
|
||||
|
||||
Result IHardwareOpusDecoderManager::OpenHardwareOpusDecoder(
|
||||
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder, OpusParameters params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle) {
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size {:#x}",
|
||||
params.sample_rate, params.channel_count, tmem_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
OpusParametersEx ex{
|
||||
.sample_rate = params.sample_rate,
|
||||
.channel_count = params.channel_count,
|
||||
.use_large_frame_size = false,
|
||||
};
|
||||
R_TRY(decoder->Initialize(ex, tmem_handle.Get(), tmem_size));
|
||||
|
||||
*out_decoder = decoder;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::GetWorkBufferSize(Out<u32> out_size, OpusParameters params) {
|
||||
R_TRY(impl.GetWorkBufferSize(params, *out_size));
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size {:#x}",
|
||||
params.sample_rate, params.channel_count, *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStream(
|
||||
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
|
||||
InLargeData<OpusMultiStreamParameters, BufferAttr_HipcPointer> params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle) {
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
"transfer_memory_size {:#x}",
|
||||
params->sample_rate, params->channel_count, params->total_stream_count,
|
||||
params->stereo_stream_count, tmem_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
|
||||
OpusMultiStreamParametersEx ex{
|
||||
.sample_rate = params->sample_rate,
|
||||
.channel_count = params->channel_count,
|
||||
.total_stream_count = params->total_stream_count,
|
||||
.stereo_stream_count = params->stereo_stream_count,
|
||||
.use_large_frame_size = false,
|
||||
.mappings{},
|
||||
};
|
||||
std::memcpy(ex.mappings.data(), params->mappings.data(), sizeof(params->mappings));
|
||||
R_TRY(decoder->Initialize(ex, tmem_handle.Get(), tmem_size));
|
||||
|
||||
*out_decoder = decoder;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStream(
|
||||
Out<u32> out_size, InLargeData<OpusMultiStreamParameters, BufferAttr_HipcPointer> params) {
|
||||
R_TRY(impl.GetWorkBufferSizeForMultiStream(*params, *out_size));
|
||||
LOG_DEBUG(Service_Audio, "size {:#x}", *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::OpenHardwareOpusDecoderEx(
|
||||
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder, OpusParametersEx params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle) {
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size {:#x}",
|
||||
params.sample_rate, params.channel_count, tmem_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
R_TRY(decoder->Initialize(params, tmem_handle.Get(), tmem_size));
|
||||
|
||||
*out_decoder = decoder;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::GetWorkBufferSizeEx(Out<u32> out_size,
|
||||
OpusParametersEx params) {
|
||||
R_TRY(impl.GetWorkBufferSizeEx(params, *out_size));
|
||||
LOG_DEBUG(Service_Audio, "size {:#x}", *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::OpenHardwareOpusDecoderForMultiStreamEx(
|
||||
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
|
||||
InLargeData<OpusMultiStreamParametersEx, BufferAttr_HipcPointer> params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle) {
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
"use_large_frame_size {}"
|
||||
"transfer_memory_size {:#x}",
|
||||
params->sample_rate, params->channel_count, params->total_stream_count,
|
||||
params->stereo_stream_count, params->use_large_frame_size, tmem_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
|
||||
R_TRY(decoder->Initialize(*params, tmem_handle.Get(), tmem_size));
|
||||
|
||||
*out_decoder = decoder;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(
|
||||
Out<u32> out_size, InLargeData<OpusMultiStreamParametersEx, BufferAttr_HipcPointer> params) {
|
||||
R_TRY(impl.GetWorkBufferSizeForMultiStreamEx(*params, *out_size));
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
"use_large_frame_size {} -- returned size {:#x}",
|
||||
params->sample_rate, params->channel_count, params->total_stream_count,
|
||||
params->stereo_stream_count, params->use_large_frame_size, *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::GetWorkBufferSizeExEx(Out<u32> out_size,
|
||||
OpusParametersEx params) {
|
||||
R_TRY(impl.GetWorkBufferSizeExEx(params, *out_size));
|
||||
LOG_DEBUG(Service_Audio, "size {:#x}", *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(
|
||||
Out<u32> out_size, InLargeData<OpusMultiStreamParametersEx, BufferAttr_HipcPointer> params) {
|
||||
R_TRY(impl.GetWorkBufferSizeForMultiStreamExEx(*params, *out_size));
|
||||
LOG_DEBUG(Service_Audio, "size {:#x}", *out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
53
src/core/hle/service/audio/hardware_opus_decoder_manager.h
Normal file
53
src/core/hle/service/audio/hardware_opus_decoder_manager.h
Normal file
@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/opus/decoder_manager.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IHardwareOpusDecoder;
|
||||
|
||||
using AudioCore::OpusDecoder::OpusMultiStreamParameters;
|
||||
using AudioCore::OpusDecoder::OpusMultiStreamParametersEx;
|
||||
using AudioCore::OpusDecoder::OpusParameters;
|
||||
using AudioCore::OpusDecoder::OpusParametersEx;
|
||||
|
||||
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
|
||||
public:
|
||||
explicit IHardwareOpusDecoderManager(Core::System& system_);
|
||||
~IHardwareOpusDecoderManager() override;
|
||||
|
||||
private:
|
||||
Result OpenHardwareOpusDecoder(Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
|
||||
OpusParameters params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle);
|
||||
Result GetWorkBufferSize(Out<u32> out_size, OpusParameters params);
|
||||
Result OpenHardwareOpusDecoderForMultiStream(
|
||||
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
|
||||
InLargeData<OpusMultiStreamParameters, BufferAttr_HipcPointer> params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle);
|
||||
Result GetWorkBufferSizeForMultiStream(
|
||||
Out<u32> out_size, InLargeData<OpusMultiStreamParameters, BufferAttr_HipcPointer> params);
|
||||
Result OpenHardwareOpusDecoderEx(Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
|
||||
OpusParametersEx params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle);
|
||||
Result GetWorkBufferSizeEx(Out<u32> out_size, OpusParametersEx params);
|
||||
Result OpenHardwareOpusDecoderForMultiStreamEx(
|
||||
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
|
||||
InLargeData<OpusMultiStreamParametersEx, BufferAttr_HipcPointer> params, u32 tmem_size,
|
||||
InCopyHandle<Kernel::KTransferMemory> tmem_handle);
|
||||
Result GetWorkBufferSizeForMultiStreamEx(
|
||||
Out<u32> out_size, InLargeData<OpusMultiStreamParametersEx, BufferAttr_HipcPointer> params);
|
||||
Result GetWorkBufferSizeExEx(Out<u32> out_size, OpusParametersEx params);
|
||||
Result GetWorkBufferSizeForMultiStreamExEx(
|
||||
Out<u32> out_size, InLargeData<OpusMultiStreamParametersEx, BufferAttr_HipcPointer> params);
|
||||
|
||||
Core::System& system;
|
||||
AudioCore::OpusDecoder::OpusDecoderManager impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,502 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/opus/decoder.h"
|
||||
#include "audio_core/opus/parameters.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/hwopus.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
using namespace AudioCore::OpusDecoder;
|
||||
|
||||
class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> {
|
||||
public:
|
||||
explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus)
|
||||
: ServiceFramework{system_, "IHardwareOpusDecoder"},
|
||||
impl{std::make_unique<AudioCore::OpusDecoder::OpusDecoder>(system_, hardware_opus)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IHardwareOpusDecoder::DecodeInterleavedOld, "DecodeInterleavedOld"},
|
||||
{1, &IHardwareOpusDecoder::SetContext, "SetContext"},
|
||||
{2, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld, "DecodeInterleavedForMultiStreamOld"},
|
||||
{3, &IHardwareOpusDecoder::SetContextForMultiStream, "SetContextForMultiStream"},
|
||||
{4, &IHardwareOpusDecoder::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
|
||||
{5, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld, "DecodeInterleavedForMultiStreamWithPerfOld"},
|
||||
{6, &IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld, "DecodeInterleavedWithPerfAndResetOld"},
|
||||
{7, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
|
||||
{8, &IHardwareOpusDecoder::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{9, &IHardwareOpusDecoder::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size) {
|
||||
return impl->Initialize(params, transfer_memory, transfer_memory_size);
|
||||
}
|
||||
|
||||
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||
u64 transfer_memory_size) {
|
||||
return impl->Initialize(params, transfer_memory, transfer_memory_size);
|
||||
}
|
||||
|
||||
private:
|
||||
void DecodeInterleavedOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
auto result =
|
||||
impl->DecodeInterleaved(&size, nullptr, &sample_count, input_data, output_data, false);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
}
|
||||
|
||||
void SetContext(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
auto result = impl->SetContext(input_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
auto result = impl->DecodeInterleavedForMultiStream(&size, nullptr, &sample_count,
|
||||
input_data, output_data, false);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
}
|
||||
|
||||
void SetContextForMultiStream(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
auto result = impl->SetContext(input_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
u64 time_taken{};
|
||||
auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data,
|
||||
output_data, false);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size,
|
||||
sample_count, time_taken);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
rb.Push(time_taken);
|
||||
}
|
||||
|
||||
void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
u64 time_taken{};
|
||||
auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count,
|
||||
input_data, output_data, false);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size,
|
||||
sample_count, time_taken);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
rb.Push(time_taken);
|
||||
}
|
||||
|
||||
void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto reset{rp.Pop<bool>()};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
u64 time_taken{};
|
||||
auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data,
|
||||
output_data, reset);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
|
||||
reset, size, sample_count, time_taken);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
rb.Push(time_taken);
|
||||
}
|
||||
|
||||
void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto reset{rp.Pop<bool>()};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
u64 time_taken{};
|
||||
auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count,
|
||||
input_data, output_data, reset);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
|
||||
reset, size, sample_count, time_taken);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
rb.Push(time_taken);
|
||||
}
|
||||
|
||||
void DecodeInterleaved(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto reset{rp.Pop<bool>()};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
u64 time_taken{};
|
||||
auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data,
|
||||
output_data, reset);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
|
||||
reset, size, sample_count, time_taken);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
rb.Push(time_taken);
|
||||
}
|
||||
|
||||
void DecodeInterleavedForMultiStream(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto reset{rp.Pop<bool>()};
|
||||
|
||||
auto input_data{ctx.ReadBuffer(0)};
|
||||
output_data.resize_destructive(ctx.GetWriteBufferSize());
|
||||
|
||||
u32 size{};
|
||||
u32 sample_count{};
|
||||
u64 time_taken{};
|
||||
auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count,
|
||||
input_data, output_data, reset);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
|
||||
reset, size, sample_count, time_taken);
|
||||
|
||||
ctx.WriteBuffer(output_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
rb.Push(sample_count);
|
||||
rb.Push(time_taken);
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioCore::OpusDecoder::OpusDecoder> impl;
|
||||
Common::ScratchBuffer<u8> output_data;
|
||||
};
|
||||
|
||||
void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto params = rp.PopRaw<OpusParameters>();
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, transfer_memory_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
|
||||
OpusParametersEx ex{
|
||||
.sample_rate = params.sample_rate,
|
||||
.channel_count = params.channel_count,
|
||||
.use_large_frame_size = false,
|
||||
};
|
||||
auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(result);
|
||||
rb.PushIpcInterface(decoder);
|
||||
}
|
||||
|
||||
void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<OpusParameters>();
|
||||
|
||||
u64 size{};
|
||||
auto result = impl.GetWorkBufferSize(params, size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
}
|
||||
|
||||
void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input{ctx.ReadBuffer()};
|
||||
OpusMultiStreamParameters params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters));
|
||||
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
"transfer_memory_size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, params.total_stream_count,
|
||||
params.stereo_stream_count, transfer_memory_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
|
||||
OpusMultiStreamParametersEx ex{
|
||||
.sample_rate = params.sample_rate,
|
||||
.channel_count = params.channel_count,
|
||||
.total_stream_count = params.total_stream_count,
|
||||
.stereo_stream_count = params.stereo_stream_count,
|
||||
.use_large_frame_size = false,
|
||||
.mappings{},
|
||||
};
|
||||
std::memcpy(ex.mappings.data(), params.mappings.data(), sizeof(params.mappings));
|
||||
auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(result);
|
||||
rb.PushIpcInterface(decoder);
|
||||
}
|
||||
|
||||
void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input{ctx.ReadBuffer()};
|
||||
OpusMultiStreamParameters params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters));
|
||||
|
||||
u64 size{};
|
||||
auto result = impl.GetWorkBufferSizeForMultiStream(params, size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
}
|
||||
|
||||
void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto params = rp.PopRaw<OpusParametersEx>();
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, transfer_memory_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
|
||||
auto result =
|
||||
decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(result);
|
||||
rb.PushIpcInterface(decoder);
|
||||
}
|
||||
|
||||
void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<OpusParametersEx>();
|
||||
|
||||
u64 size{};
|
||||
auto result = impl.GetWorkBufferSizeEx(params, size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
}
|
||||
|
||||
void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input{ctx.ReadBuffer()};
|
||||
OpusMultiStreamParametersEx params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx));
|
||||
|
||||
auto transfer_memory_size{rp.Pop<u32>()};
|
||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
"use_large_frame_size {}"
|
||||
"transfer_memory_size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, params.total_stream_count,
|
||||
params.stereo_stream_count, params.use_large_frame_size, transfer_memory_size);
|
||||
|
||||
auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
|
||||
|
||||
auto result =
|
||||
decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(result);
|
||||
rb.PushIpcInterface(decoder);
|
||||
}
|
||||
|
||||
void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input{ctx.ReadBuffer()};
|
||||
OpusMultiStreamParametersEx params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx));
|
||||
|
||||
u64 size{};
|
||||
auto result = impl.GetWorkBufferSizeForMultiStreamEx(params, size);
|
||||
|
||||
LOG_DEBUG(Service_Audio,
|
||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||
"use_large_frame_size {} -- returned size 0x{:X}",
|
||||
params.sample_rate, params.channel_count, params.total_stream_count,
|
||||
params.stereo_stream_count, params.use_large_frame_size, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
}
|
||||
|
||||
void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<OpusParametersEx>();
|
||||
|
||||
u64 size{};
|
||||
auto result = impl.GetWorkBufferSizeExEx(params, size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
}
|
||||
|
||||
void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto input{ctx.ReadBuffer()};
|
||||
OpusMultiStreamParametersEx params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx));
|
||||
|
||||
u64 size{};
|
||||
auto result = impl.GetWorkBufferSizeForMultiStreamExEx(params, size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result);
|
||||
rb.Push(size);
|
||||
}
|
||||
|
||||
HwOpus::HwOpus(Core::System& system_)
|
||||
: ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
|
||||
{1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
|
||||
{2, &HwOpus::OpenHardwareOpusDecoderForMultiStream, "OpenOpusDecoderForMultiStream"},
|
||||
{3, &HwOpus::GetWorkBufferSizeForMultiStream, "GetWorkBufferSizeForMultiStream"},
|
||||
{4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
|
||||
{5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
|
||||
{6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,
|
||||
"OpenHardwareOpusDecoderForMultiStreamEx"},
|
||||
{7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
|
||||
{8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"},
|
||||
{9, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
HwOpus::~HwOpus() = default;
|
||||
|
||||
} // namespace Service::Audio
|
@ -1,36 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/opus/decoder_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class HwOpus final : public ServiceFramework<HwOpus> {
|
||||
public:
|
||||
explicit HwOpus(Core::System& system_);
|
||||
~HwOpus() override;
|
||||
|
||||
private:
|
||||
void OpenHardwareOpusDecoder(HLERequestContext& ctx);
|
||||
void GetWorkBufferSize(HLERequestContext& ctx);
|
||||
void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx);
|
||||
void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeEx(HLERequestContext& ctx);
|
||||
void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeExEx(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx);
|
||||
|
||||
Core::System& system;
|
||||
AudioCore::OpusDecoder::OpusDecoderManager impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
@ -5,6 +5,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
@ -13,9 +14,9 @@
|
||||
|
||||
namespace Service::BtDrv {
|
||||
|
||||
class Bt final : public ServiceFramework<Bt> {
|
||||
class IBluetoothUser final : public ServiceFramework<IBluetoothUser> {
|
||||
public:
|
||||
explicit Bt(Core::System& system_)
|
||||
explicit IBluetoothUser(Core::System& system_)
|
||||
: ServiceFramework{system_, "bt"}, service_context{system_, "bt"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@ -28,7 +29,7 @@ public:
|
||||
{6, nullptr, "SetLeResponse"},
|
||||
{7, nullptr, "LeSendIndication"},
|
||||
{8, nullptr, "GetLeEventInfo"},
|
||||
{9, &Bt::RegisterBleEvent, "RegisterBleEvent"},
|
||||
{9, C<&IBluetoothUser::RegisterBleEvent>, "RegisterBleEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@ -36,17 +37,16 @@ public:
|
||||
register_event = service_context.CreateEvent("BT:RegisterEvent");
|
||||
}
|
||||
|
||||
~Bt() override {
|
||||
~IBluetoothUser() override {
|
||||
service_context.CloseEvent(register_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterBleEvent(HLERequestContext& ctx) {
|
||||
Result RegisterBleEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(register_event->GetReadableEvent());
|
||||
*out_event = ®ister_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
@ -54,9 +54,9 @@ private:
|
||||
Kernel::KEvent* register_event;
|
||||
};
|
||||
|
||||
class BtDrv final : public ServiceFramework<BtDrv> {
|
||||
class IBluetoothDriver final : public ServiceFramework<IBluetoothDriver> {
|
||||
public:
|
||||
explicit BtDrv(Core::System& system_) : ServiceFramework{system_, "btdrv"} {
|
||||
explicit IBluetoothDriver(Core::System& system_) : ServiceFramework{system_, "btdrv"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "InitializeBluetoothDriver"},
|
||||
@ -93,7 +93,7 @@ public:
|
||||
{31, nullptr, "EnableMcMode"},
|
||||
{32, nullptr, "EnableLlrScan"},
|
||||
{33, nullptr, "DisableLlrScan"},
|
||||
{34, nullptr, "EnableRadio"},
|
||||
{34, C<&IBluetoothDriver::EnableRadio>, "EnableRadio"},
|
||||
{35, nullptr, "SetVisibility"},
|
||||
{36, nullptr, "EnableTbfcScan"},
|
||||
{37, nullptr, "RegisterHidReportEvent"},
|
||||
@ -195,13 +195,19 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
Result EnableRadio() {
|
||||
LOG_WARNING(Service_BTDRV, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("btdrv", std::make_shared<BtDrv>(system));
|
||||
server_manager->RegisterNamedService("bt", std::make_shared<Bt>(system));
|
||||
server_manager->RegisterNamedService("btdrv", std::make_shared<IBluetoothDriver>(system));
|
||||
server_manager->RegisterNamedService("bt", std::make_shared<IBluetoothUser>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
@ -415,7 +415,7 @@ void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequ
|
||||
auto& buffer = temp[OutBufferIndex];
|
||||
const size_t size = buffer.size();
|
||||
|
||||
if (ctx.CanWriteBuffer(OutBufferIndex)) {
|
||||
if (size > 0 && ctx.CanWriteBuffer(OutBufferIndex)) {
|
||||
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
||||
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
|
||||
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
||||
|
@ -3,82 +3,34 @@
|
||||
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
template <typename T>
|
||||
static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
|
||||
const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
|
||||
entries.reserve(entries.size() + new_data.size());
|
||||
|
||||
for (const auto& new_entry : new_data) {
|
||||
auto name = new_entry->GetName();
|
||||
|
||||
if (type == FileSys::DirectoryEntryType::File &&
|
||||
name == FileSys::GetSaveDataSizeFileName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.emplace_back(name, static_cast<s8>(type),
|
||||
type == FileSys::DirectoryEntryType::Directory ? 0
|
||||
: new_entry->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
|
||||
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
|
||||
FileSys::OpenDirectoryMode mode)
|
||||
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
|
||||
: ServiceFramework{system_, "IDirectory"},
|
||||
backend(std::make_unique<FileSys::Fsa::IDirectory>(directory_, mode)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDirectory::Read, "Read"},
|
||||
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
|
||||
{0, D<&IDirectory::Read>, "Read"},
|
||||
{1, D<&IDirectory::GetEntryCount>, "GetEntryCount"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
||||
// Build entry index now to save time later.
|
||||
if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
|
||||
BuildEntryIndex(entries, backend->GetSubdirectories(),
|
||||
FileSys::DirectoryEntryType::Directory);
|
||||
}
|
||||
if (True(mode & FileSys::OpenDirectoryMode::File)) {
|
||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
|
||||
}
|
||||
}
|
||||
|
||||
void IDirectory::Read(HLERequestContext& ctx) {
|
||||
Result IDirectory::Read(
|
||||
Out<s64> out_count,
|
||||
const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries) {
|
||||
LOG_DEBUG(Service_FS, "called.");
|
||||
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
|
||||
|
||||
// Cap at total number of entries.
|
||||
const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
|
||||
|
||||
// Determine data start and end
|
||||
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
|
||||
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
|
||||
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
|
||||
|
||||
next_entry_index += actual_entries;
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(begin, range_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(actual_entries);
|
||||
R_RETURN(backend->Read(out_count, out_entries.data(), out_entries.size()));
|
||||
}
|
||||
|
||||
void IDirectory::GetEntryCount(HLERequestContext& ctx) {
|
||||
Result IDirectory::GetEntryCount(Out<s64> out_count) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
u64 count = entries.size() - next_entry_index;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(count);
|
||||
R_RETURN(backend->GetEntryCount(out_count));
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/fsa/fs_i_directory.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@ -15,16 +17,15 @@ namespace Service::FileSystem {
|
||||
|
||||
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||
public:
|
||||
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
|
||||
explicit IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
|
||||
FileSys::OpenDirectoryMode mode);
|
||||
|
||||
private:
|
||||
FileSys::VirtualDir backend;
|
||||
std::vector<FileSys::DirectoryEntry> entries;
|
||||
u64 next_entry_index = 0;
|
||||
std::unique_ptr<FileSys::Fsa::IDirectory> backend;
|
||||
|
||||
void Read(HLERequestContext& ctx);
|
||||
void GetEntryCount(HLERequestContext& ctx);
|
||||
Result Read(Out<s64> out_count,
|
||||
const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries);
|
||||
Result GetEntryCount(Out<s64> out_count);
|
||||
};
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
@ -2,126 +2,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
|
||||
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
|
||||
IFile::IFile(Core::System& system_, FileSys::VirtualFile file_)
|
||||
: ServiceFramework{system_, "IFile"}, backend{std::make_unique<FileSys::Fsa::IFile>(file_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFile::Read, "Read"},
|
||||
{1, &IFile::Write, "Write"},
|
||||
{2, &IFile::Flush, "Flush"},
|
||||
{3, &IFile::SetSize, "SetSize"},
|
||||
{4, &IFile::GetSize, "GetSize"},
|
||||
{0, D<&IFile::Read>, "Read"},
|
||||
{1, D<&IFile::Write>, "Write"},
|
||||
{2, D<&IFile::Flush>, "Flush"},
|
||||
{3, D<&IFile::SetSize>, "SetSize"},
|
||||
{4, D<&IFile::GetSize>, "GetSize"},
|
||||
{5, nullptr, "OperateRange"},
|
||||
{6, nullptr, "OperateRangeWithBuffer"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void IFile::Read(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 option = rp.Pop<u64>();
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(FileSys::ResultInvalidSize);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(FileSys::ResultInvalidOffset);
|
||||
return;
|
||||
}
|
||||
Result IFile::Read(
|
||||
FileSys::ReadOption option, Out<s64> out_size, s64 offset,
|
||||
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
|
||||
s64 size) {
|
||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
|
||||
size);
|
||||
|
||||
// Read the data from the Storage backend
|
||||
std::vector<u8> output = backend->ReadBytes(length, offset);
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(output);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u64>(output.size()));
|
||||
R_RETURN(
|
||||
backend->Read(reinterpret_cast<size_t*>(out_size.Get()), offset, out_buffer.data(), size));
|
||||
}
|
||||
|
||||
void IFile::Write(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 option = rp.Pop<u64>();
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
Result IFile::Write(
|
||||
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
|
||||
FileSys::WriteOption option, s64 offset, s64 size) {
|
||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
|
||||
size);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(FileSys::ResultInvalidSize);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(FileSys::ResultInvalidOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto data = ctx.ReadBuffer();
|
||||
|
||||
ASSERT_MSG(static_cast<s64>(data.size()) <= length,
|
||||
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
|
||||
length, data.size());
|
||||
|
||||
// Write the data to the Storage backend
|
||||
const auto write_size =
|
||||
static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
|
||||
const std::size_t written = backend->Write(data.data(), write_size, offset);
|
||||
|
||||
ASSERT_MSG(static_cast<s64>(written) == length,
|
||||
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
|
||||
written);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_RETURN(backend->Write(offset, buffer.data(), size, option));
|
||||
}
|
||||
|
||||
void IFile::Flush(HLERequestContext& ctx) {
|
||||
Result IFile::Flush() {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
// Exists for SDK compatibiltity -- No need to flush file.
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_RETURN(backend->Flush());
|
||||
}
|
||||
|
||||
void IFile::SetSize(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 size = rp.Pop<u64>();
|
||||
Result IFile::SetSize(s64 size) {
|
||||
LOG_DEBUG(Service_FS, "called, size={}", size);
|
||||
|
||||
backend->Resize(size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_RETURN(backend->SetSize(size));
|
||||
}
|
||||
|
||||
void IFile::GetSize(HLERequestContext& ctx) {
|
||||
const u64 size = backend->GetSize();
|
||||
LOG_DEBUG(Service_FS, "called, size={}", size);
|
||||
Result IFile::GetSize(Out<s64> out_size) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u64>(size);
|
||||
R_RETURN(backend->GetSize(out_size));
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/fsa/fs_i_file.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@ -10,16 +12,21 @@ namespace Service::FileSystem {
|
||||
|
||||
class IFile final : public ServiceFramework<IFile> {
|
||||
public:
|
||||
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_);
|
||||
explicit IFile(Core::System& system_, FileSys::VirtualFile file_);
|
||||
|
||||
private:
|
||||
FileSys::VirtualFile backend;
|
||||
std::unique_ptr<FileSys::Fsa::IFile> backend;
|
||||
|
||||
void Read(HLERequestContext& ctx);
|
||||
void Write(HLERequestContext& ctx);
|
||||
void Flush(HLERequestContext& ctx);
|
||||
void SetSize(HLERequestContext& ctx);
|
||||
void GetSize(HLERequestContext& ctx);
|
||||
Result Read(FileSys::ReadOption option, Out<s64> out_size, s64 offset,
|
||||
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
|
||||
out_buffer,
|
||||
s64 size);
|
||||
Result Write(
|
||||
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
|
||||
FileSys::WriteOption option, s64 offset, s64 size);
|
||||
Result Flush();
|
||||
Result SetSize(s64 size);
|
||||
Result GetSize(Out<s64> out_size);
|
||||
};
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
@ -2,261 +2,172 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/fssrv/fssrv_sf_path.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
|
||||
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
|
||||
#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
|
||||
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
|
||||
size_)} {
|
||||
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
|
||||
: ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
|
||||
dir_)},
|
||||
size_getter{std::move(size_getter_)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||
{1, &IFileSystem::DeleteFile, "DeleteFile"},
|
||||
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
|
||||
{3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
|
||||
{4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
|
||||
{5, &IFileSystem::RenameFile, "RenameFile"},
|
||||
{0, D<&IFileSystem::CreateFile>, "CreateFile"},
|
||||
{1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
|
||||
{2, D<&IFileSystem::CreateDirectory>, "CreateDirectory"},
|
||||
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||
{6, nullptr, "RenameDirectory"},
|
||||
{7, &IFileSystem::GetEntryType, "GetEntryType"},
|
||||
{8, &IFileSystem::OpenFile, "OpenFile"},
|
||||
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
|
||||
{10, &IFileSystem::Commit, "Commit"},
|
||||
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
|
||||
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
|
||||
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
|
||||
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
|
||||
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||
{10, D<&IFileSystem::Commit>, "Commit"},
|
||||
{11, D<&IFileSystem::GetFreeSpaceSize>, "GetFreeSpaceSize"},
|
||||
{12, D<&IFileSystem::GetTotalSpaceSize>, "GetTotalSpaceSize"},
|
||||
{13, D<&IFileSystem::CleanDirectoryRecursively>, "CleanDirectoryRecursively"},
|
||||
{14, D<&IFileSystem::GetFileTimeStampRaw>, "GetFileTimeStampRaw"},
|
||||
{15, nullptr, "QueryEntry"},
|
||||
{16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
|
||||
{16, D<&IFileSystem::GetFileSystemAttribute>, "GetFileSystemAttribute"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void IFileSystem::CreateFile(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||
s32 option, s64 size) {
|
||||
LOG_DEBUG(Service_FS, "called. file={}, option=0x{:X}, size=0x{:08X}", path->str, option, size);
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
const u64 file_mode = rp.Pop<u64>();
|
||||
const u32 file_size = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
|
||||
file_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.CreateFile(name, file_size));
|
||||
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
|
||||
}
|
||||
|
||||
void IFileSystem::DeleteFile(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called. file={}", path->str);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteFile(name));
|
||||
R_RETURN(backend->DeleteFile(FileSys::Path(path->str)));
|
||||
}
|
||||
|
||||
void IFileSystem::CreateDirectory(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
Result IFileSystem::CreateDirectory(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.CreateDirectory(name));
|
||||
R_RETURN(backend->CreateDirectory(FileSys::Path(path->str)));
|
||||
}
|
||||
|
||||
void IFileSystem::DeleteDirectory(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
Result IFileSystem::DeleteDirectory(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteDirectory(name));
|
||||
R_RETURN(backend->DeleteDirectory(FileSys::Path(path->str)));
|
||||
}
|
||||
|
||||
void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
Result IFileSystem::DeleteDirectoryRecursively(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteDirectoryRecursively(name));
|
||||
R_RETURN(backend->DeleteDirectoryRecursively(FileSys::Path(path->str)));
|
||||
}
|
||||
|
||||
void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
Result IFileSystem::CleanDirectoryRecursively(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called. Directory: {}", path->str);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. Directory: {}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.CleanDirectoryRecursively(name));
|
||||
R_RETURN(backend->CleanDirectoryRecursively(FileSys::Path(path->str)));
|
||||
}
|
||||
|
||||
void IFileSystem::RenameFile(HLERequestContext& ctx) {
|
||||
const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
|
||||
const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
|
||||
Result IFileSystem::RenameFile(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
|
||||
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", old_path->str, new_path->str);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.RenameFile(src_name, dst_name));
|
||||
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||
}
|
||||
|
||||
void IFileSystem::OpenFile(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
|
||||
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||
u32 mode) {
|
||||
LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
|
||||
|
||||
FileSys::VirtualFile vfs_file{};
|
||||
auto result = backend.OpenFile(&vfs_file, name, mode);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
|
||||
static_cast<FileSys::OpenMode>(mode)));
|
||||
|
||||
auto file = std::make_shared<IFile>(system, vfs_file);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IFile>(std::move(file));
|
||||
*out_interface = std::make_shared<IFile>(system, vfs_file);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
|
||||
Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||
u32 mode) {
|
||||
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
|
||||
|
||||
FileSys::VirtualDir vfs_dir{};
|
||||
auto result = backend.OpenDirectory(&vfs_dir, name);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
|
||||
static_cast<FileSys::OpenDirectoryMode>(mode)));
|
||||
|
||||
auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDirectory>(std::move(directory));
|
||||
*out_interface = std::make_shared<IDirectory>(system, vfs_dir,
|
||||
static_cast<FileSys::OpenDirectoryMode>(mode));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::GetEntryType(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
||||
Result IFileSystem::GetEntryType(
|
||||
Out<u32> out_type, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called. file={}", path->str);
|
||||
|
||||
FileSys::DirectoryEntryType vfs_entry_type{};
|
||||
auto result = backend.GetEntryType(&vfs_entry_type, name);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(vfs_entry_type));
|
||||
*out_type = static_cast<u32>(vfs_entry_type);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::Commit(HLERequestContext& ctx) {
|
||||
Result IFileSystem::Commit() {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) {
|
||||
Result IFileSystem::GetFreeSpaceSize(
|
||||
Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(size.get_free_size());
|
||||
*out_size = size_getter.get_free_size();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) {
|
||||
Result IFileSystem::GetTotalSpaceSize(
|
||||
Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(size.get_total_size());
|
||||
*out_size = size_getter.get_total_size();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
|
||||
Result IFileSystem::GetFileTimeStampRaw(
|
||||
Out<FileSys::FileTimeStampRaw> out_timestamp,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);
|
||||
|
||||
FileSys::FileTimeStampRaw vfs_timestamp{};
|
||||
auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(vfs_timestamp);
|
||||
*out_timestamp = vfs_timestamp;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
|
||||
Result IFileSystem::GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
struct FileSystemAttribute {
|
||||
u8 dir_entry_name_length_max_defined;
|
||||
u8 file_entry_name_length_max_defined;
|
||||
u8 dir_path_name_length_max_defined;
|
||||
u8 file_path_name_length_max_defined;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x5);
|
||||
u8 utf16_dir_entry_name_length_max_defined;
|
||||
u8 utf16_file_entry_name_length_max_defined;
|
||||
u8 utf16_dir_path_name_length_max_defined;
|
||||
u8 utf16_file_path_name_length_max_defined;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x18);
|
||||
s32 dir_entry_name_length_max;
|
||||
s32 file_entry_name_length_max;
|
||||
s32 dir_path_name_length_max;
|
||||
s32 file_path_name_length_max;
|
||||
INSERT_PADDING_WORDS_NOINIT(0x5);
|
||||
s32 utf16_dir_entry_name_length_max;
|
||||
s32 utf16_file_entry_name_length_max;
|
||||
s32 utf16_dir_path_name_length_max;
|
||||
s32 utf16_file_path_name_length_max;
|
||||
INSERT_PADDING_WORDS_NOINIT(0x18);
|
||||
INSERT_PADDING_WORDS_NOINIT(0x1);
|
||||
};
|
||||
static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
|
||||
|
||||
FileSystemAttribute savedata_attribute{};
|
||||
FileSys::FileSystemAttribute savedata_attribute{};
|
||||
savedata_attribute.dir_entry_name_length_max_defined = true;
|
||||
savedata_attribute.file_entry_name_length_max_defined = true;
|
||||
savedata_attribute.dir_entry_name_length_max = 0x40;
|
||||
savedata_attribute.file_entry_name_length_max = 0x40;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 50};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(savedata_attribute);
|
||||
*out_attribute = savedata_attribute;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
@ -3,36 +3,58 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/fsa/fs_i_filesystem.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp/fsp_util.h"
|
||||
#include "core/hle/service/filesystem/fsp/fsp_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace FileSys::Sf {
|
||||
struct Path;
|
||||
}
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
class IFile;
|
||||
class IDirectory;
|
||||
|
||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||
public:
|
||||
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_);
|
||||
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);
|
||||
|
||||
void CreateFile(HLERequestContext& ctx);
|
||||
void DeleteFile(HLERequestContext& ctx);
|
||||
void CreateDirectory(HLERequestContext& ctx);
|
||||
void DeleteDirectory(HLERequestContext& ctx);
|
||||
void DeleteDirectoryRecursively(HLERequestContext& ctx);
|
||||
void CleanDirectoryRecursively(HLERequestContext& ctx);
|
||||
void RenameFile(HLERequestContext& ctx);
|
||||
void OpenFile(HLERequestContext& ctx);
|
||||
void OpenDirectory(HLERequestContext& ctx);
|
||||
void GetEntryType(HLERequestContext& ctx);
|
||||
void Commit(HLERequestContext& ctx);
|
||||
void GetFreeSpaceSize(HLERequestContext& ctx);
|
||||
void GetTotalSpaceSize(HLERequestContext& ctx);
|
||||
void GetFileTimeStampRaw(HLERequestContext& ctx);
|
||||
void GetFileSystemAttribute(HLERequestContext& ctx);
|
||||
Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
|
||||
s64 size);
|
||||
Result DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result CreateDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result DeleteDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result DeleteDirectoryRecursively(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result CleanDirectoryRecursively(
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||
Result OpenFile(OutInterface<IFile> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||
u32 mode);
|
||||
Result GetEntryType(Out<u32> out_type,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result Commit();
|
||||
Result GetFreeSpaceSize(Out<s64> out_size,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result GetTotalSpaceSize(Out<s64> out_size,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result GetFileTimeStampRaw(Out<FileSys::FileTimeStampRaw> out_timestamp,
|
||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||
Result GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute);
|
||||
|
||||
private:
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
SizeGetter size;
|
||||
std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
|
||||
SizeGetter size_getter;
|
||||
};
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user