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
|
fetch-depth: 0
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
# workaround for https://github.com/actions/setup-python/issues/577
|
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
|
||||||
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
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
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-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
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)
|
if (NOT TARGET SimpleIni::SimpleIni)
|
||||||
add_subdirectory(simpleini)
|
add_subdirectory(simpleini)
|
||||||
endif()
|
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-attributes
|
||||||
-Wno-invalid-offsetof
|
-Wno-invalid-offsetof
|
||||||
-Wno-unused-parameter
|
-Wno-unused-parameter
|
||||||
|
-Wno-missing-field-initializers
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
|
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
|
||||||
@ -164,6 +165,7 @@ else()
|
|||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||||
|
add_compile_options("-msse4.1")
|
||||||
|
|
||||||
if (MINGW_STATIC_BUILD)
|
if (MINGW_STATIC_BUILD)
|
||||||
add_definitions(-DQT_STATICPLUGIN)
|
add_definitions(-DQT_STATICPLUGIN)
|
||||||
|
@ -64,17 +64,17 @@ data class PlayerInput(
|
|||||||
fun hasMapping(): Boolean {
|
fun hasMapping(): Boolean {
|
||||||
var hasMapping = false
|
var hasMapping = false
|
||||||
buttons.forEach {
|
buttons.forEach {
|
||||||
if (it != "[empty]") {
|
if (it != "[empty]" && it.isNotEmpty()) {
|
||||||
hasMapping = true
|
hasMapping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
analogs.forEach {
|
analogs.forEach {
|
||||||
if (it != "[empty]") {
|
if (it != "[empty]" && it.isNotEmpty()) {
|
||||||
hasMapping = true
|
hasMapping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
motions.forEach {
|
motions.forEach {
|
||||||
if (it != "[empty]") {
|
if (it != "[empty]" && it.isNotEmpty()) {
|
||||||
hasMapping = true
|
hasMapping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -780,7 +780,7 @@ class SettingsFragmentPresenter(
|
|||||||
playerIndex: Int,
|
playerIndex: Int,
|
||||||
paramName: String,
|
paramName: String,
|
||||||
stick: NativeAnalog,
|
stick: NativeAnalog,
|
||||||
defaultValue: Int
|
defaultValue: Float
|
||||||
): AbstractIntSetting =
|
): AbstractIntSetting =
|
||||||
object : AbstractIntSetting {
|
object : AbstractIntSetting {
|
||||||
val params get() = NativeInput.getStickParam(playerIndex, stick)
|
val params get() = NativeInput.getStickParam(playerIndex, stick)
|
||||||
@ -788,7 +788,7 @@ class SettingsFragmentPresenter(
|
|||||||
override val key = ""
|
override val key = ""
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int =
|
override fun getInt(needsGlobal: Boolean): Int =
|
||||||
(params.get(paramName, 0.15f) * 100).toInt()
|
(params.get(paramName, defaultValue) * 100).toInt()
|
||||||
|
|
||||||
override fun setInt(value: Int) {
|
override fun setInt(value: Int) {
|
||||||
val tempParams = params
|
val tempParams = params
|
||||||
@ -796,12 +796,12 @@ class SettingsFragmentPresenter(
|
|||||||
NativeInput.setStickParam(playerIndex, stick, tempParams)
|
NativeInput.setStickParam(playerIndex, stick, tempParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val defaultValue = defaultValue
|
override val defaultValue = (defaultValue * 100).toInt()
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
||||||
getInt(needsGlobal).toString()
|
getInt(needsGlobal).toString()
|
||||||
|
|
||||||
override fun reset() = setInt(defaultValue)
|
override fun reset() = setInt(this.defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExtraStickSettings(
|
private fun getExtraStickSettings(
|
||||||
@ -811,11 +811,11 @@ class SettingsFragmentPresenter(
|
|||||||
val stickIsController =
|
val stickIsController =
|
||||||
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
|
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
|
||||||
val modifierRangeSetting =
|
val modifierRangeSetting =
|
||||||
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
|
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
|
||||||
val stickRangeSetting =
|
val stickRangeSetting =
|
||||||
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
|
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
|
||||||
val stickDeadzoneSetting =
|
val stickDeadzoneSetting =
|
||||||
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
|
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
|
||||||
|
|
||||||
val out = mutableListOf<SettingsItem>().apply {
|
val out = mutableListOf<SettingsItem>().apply {
|
||||||
if (stickIsController) {
|
if (stickIsController) {
|
||||||
|
@ -292,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {
|
|||||||
// Unload user input.
|
// Unload user input.
|
||||||
m_system.HIDCore().UnloadInputDevices();
|
m_system.HIDCore().UnloadInputDevices();
|
||||||
|
|
||||||
|
// Enable all controllers
|
||||||
|
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||||
|
|
||||||
// Shutdown the main emulated process
|
// Shutdown the main emulated process
|
||||||
if (m_load_result == Core::SystemResultStatus::Success) {
|
if (m_load_result == Core::SystemResultStatus::Success) {
|
||||||
m_system.DetachDebugger();
|
m_system.DetachDebugger();
|
||||||
@ -665,7 +668,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
|
|||||||
ASSERT(user_id);
|
ASSERT(user_id);
|
||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
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);
|
user_id->AsU128(), 0);
|
||||||
|
|
||||||
const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
|
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);
|
FileSys::OpenMode::Read);
|
||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
{}, vfsNandDir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, program_id,
|
||||||
program_id, user_id->AsU128(), 0);
|
user_id->AsU128(), 0);
|
||||||
return Common::Android::ToJString(env, user_save_data_path);
|
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) {
|
void ConnectController(size_t player_index, bool connected) {
|
||||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
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) {
|
if (player_index == 0) {
|
||||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
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(
|
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
|
||||||
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
||||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
auto supported_styles = GetSupportedStyles(j_player_index);
|
||||||
const auto npad_style_set = hid_core.GetSupportedStyleTag();
|
jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
|
||||||
std::vector<s32> supported_indexes;
|
env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
|
||||||
if (npad_style_set.fullkey == 1) {
|
supported_styles.data());
|
||||||
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());
|
|
||||||
return j_supported_indexes;
|
return j_supported_indexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,16 +73,15 @@ void Manager::BufferReleaseAndRegister() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Manager::GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names,
|
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
|
||||||
[[maybe_unused]] const u32 max_count,
|
|
||||||
[[maybe_unused]] const bool filter) {
|
[[maybe_unused]] const bool filter) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
|
|
||||||
LinkToManager();
|
LinkToManager();
|
||||||
|
|
||||||
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
||||||
if (input_devices.size() > 1) {
|
if (!input_devices.empty() && !names.empty()) {
|
||||||
names.emplace_back("Uac");
|
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -60,13 +60,11 @@ public:
|
|||||||
* Get a list of audio in device names.
|
* Get a list of audio in device names.
|
||||||
*
|
*
|
||||||
* @param names - Output container to write names to.
|
* @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.
|
* @param filter - Should the list be filtered? Unused.
|
||||||
*
|
*
|
||||||
* @return Number of names written.
|
* @return Number of names written.
|
||||||
*/
|
*/
|
||||||
u32 GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, u32 max_count,
|
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
|
||||||
bool filter);
|
|
||||||
|
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
@ -146,7 +146,11 @@ public:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tags[released++] = tag;
|
if (released < tags.size()) {
|
||||||
|
tags[released] = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
released++;
|
||||||
|
|
||||||
if (released >= tags.size()) {
|
if (released >= tags.size()) {
|
||||||
break;
|
break;
|
||||||
|
@ -28,8 +28,8 @@ OpusDecoder::~OpusDecoder() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
Result OpusDecoder::Initialize(const OpusParametersEx& params,
|
||||||
u64 transfer_memory_size) {
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
shared_buffer_size = transfer_memory_size;
|
shared_buffer_size = transfer_memory_size;
|
||||||
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
||||||
@ -59,7 +59,7 @@ Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
|
Result OpusDecoder::Initialize(const OpusMultiStreamParametersEx& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
shared_buffer_size = transfer_memory_size;
|
shared_buffer_size = transfer_memory_size;
|
||||||
|
@ -22,10 +22,10 @@ public:
|
|||||||
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
|
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
|
||||||
~OpusDecoder();
|
~OpusDecoder();
|
||||||
|
|
||||||
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
Result Initialize(const OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||||
u64 transfer_memory_size);
|
|
||||||
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
|
||||||
u64 transfer_memory_size);
|
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,
|
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);
|
std::span<const u8> input_data, std::span<u8> output_data, bool reset);
|
||||||
Result SetContext([[maybe_unused]] std::span<const u8> context);
|
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{
|
OpusParametersEx ex{
|
||||||
.sample_rate = params.sample_rate,
|
.sample_rate = params.sample_rate,
|
||||||
.channel_count = params.channel_count,
|
.channel_count = params.channel_count,
|
||||||
@ -47,11 +47,11 @@ Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_si
|
|||||||
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
|
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));
|
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(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
||||||
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
||||||
|
|
||||||
@ -63,8 +63,8 @@ Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64&
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
|
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(const OpusMultiStreamParameters& params,
|
||||||
u64& out_size) {
|
u32& out_size) {
|
||||||
OpusMultiStreamParametersEx ex{
|
OpusMultiStreamParametersEx ex{
|
||||||
.sample_rate = params.sample_rate,
|
.sample_rate = params.sample_rate,
|
||||||
.channel_count = params.channel_count,
|
.channel_count = params.channel_count,
|
||||||
@ -76,13 +76,13 @@ Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParame
|
|||||||
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
|
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
|
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(
|
||||||
u64& out_size) {
|
const OpusMultiStreamParametersEx& params, u32& out_size) {
|
||||||
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
|
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
|
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(
|
||||||
u64& out_size) {
|
const OpusMultiStreamParametersEx& params, u32& out_size) {
|
||||||
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
||||||
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
||||||
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
|
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
|
||||||
|
@ -22,17 +22,19 @@ public:
|
|||||||
return hardware_opus;
|
return hardware_opus;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
|
Result GetWorkBufferSize(const OpusParameters& params, u32& out_size);
|
||||||
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeEx(const OpusParametersEx& params, u32& out_size);
|
||||||
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeExEx(const OpusParametersEx& params, u32& out_size);
|
||||||
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
|
Result GetWorkBufferSizeForMultiStream(const OpusMultiStreamParameters& params, u32& out_size);
|
||||||
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeForMultiStreamEx(const OpusMultiStreamParametersEx& params,
|
||||||
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
u32& out_size);
|
||||||
|
Result GetWorkBufferSizeForMultiStreamExEx(const OpusMultiStreamParametersEx& params,
|
||||||
|
u32& out_size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
HardwareOpus hardware_opus;
|
HardwareOpus hardware_opus;
|
||||||
std::array<u64, MaxChannels> required_workbuffer_sizes{};
|
std::array<u32, MaxChannels> required_workbuffer_sizes{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -42,7 +42,7 @@ HardwareOpus::HardwareOpus(Core::System& system_)
|
|||||||
opus_decoder.SetSharedMemory(shared_memory);
|
opus_decoder.SetSharedMemory(shared_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||||
if (!opus_decoder.IsRunning()) {
|
if (!opus_decoder.IsRunning()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -55,10 +55,10 @@ u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
|||||||
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
|
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
|
||||||
return 0;
|
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};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = total_stream_count;
|
shared_memory.host_send_data[0] = total_stream_count;
|
||||||
shared_memory.host_send_data[1] = stereo_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);
|
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
|
||||||
return 0;
|
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,
|
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,
|
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||||
u32 total_stream_count,
|
u32 total_stream_count,
|
||||||
u32 stereo_stream_count, void* mappings,
|
u32 stereo_stream_count,
|
||||||
void* buffer, u64 buffer_size) {
|
const void* mappings, void* buffer,
|
||||||
|
u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
|
@ -16,14 +16,14 @@ class HardwareOpus {
|
|||||||
public:
|
public:
|
||||||
HardwareOpus(Core::System& system);
|
HardwareOpus(Core::System& system);
|
||||||
|
|
||||||
u64 GetWorkBufferSize(u32 channel);
|
u32 GetWorkBufferSize(u32 channel);
|
||||||
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
||||||
|
|
||||||
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
||||||
u64 buffer_size);
|
u64 buffer_size);
|
||||||
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||||
u32 totaL_stream_count, u32 stereo_stream_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 ShutdownDecodeObject(void* buffer, u64 buffer_size);
|
||||||
Result ShutdownMultiStreamDecodeObject(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,
|
Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
|
||||||
|
@ -20,7 +20,7 @@ struct OpusParametersEx {
|
|||||||
/* 0x00 */ u32 sample_rate;
|
/* 0x00 */ u32 sample_rate;
|
||||||
/* 0x04 */ u32 channel_count;
|
/* 0x04 */ u32 channel_count;
|
||||||
/* 0x08 */ bool use_large_frame_size;
|
/* 0x08 */ bool use_large_frame_size;
|
||||||
/* 0x09 */ INSERT_PADDING_BYTES(7);
|
/* 0x09 */ INSERT_PADDING_BYTES_NOINIT(7);
|
||||||
}; // size = 0x10
|
}; // size = 0x10
|
||||||
static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!");
|
static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!");
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ struct OpusMultiStreamParametersEx {
|
|||||||
/* 0x08 */ u32 total_stream_count;
|
/* 0x08 */ u32 total_stream_count;
|
||||||
/* 0x0C */ u32 stereo_stream_count;
|
/* 0x0C */ u32 stereo_stream_count;
|
||||||
/* 0x10 */ bool use_large_frame_size;
|
/* 0x10 */ bool use_large_frame_size;
|
||||||
/* 0x11 */ INSERT_PADDING_BYTES(7);
|
/* 0x11 */ INSERT_PADDING_BYTES_NOINIT(7);
|
||||||
/* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings;
|
/* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings;
|
||||||
}; // size = 0x118
|
}; // size = 0x118
|
||||||
static_assert(sizeof(OpusMultiStreamParametersEx) == 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()},
|
: output_sink{system.AudioCore().GetOutputSink()},
|
||||||
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
|
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
|
||||||
|
|
||||||
u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
||||||
const size_t max_count) const {
|
|
||||||
std::span<const AudioDeviceName> names{};
|
std::span<const AudioDeviceName> names{};
|
||||||
|
|
||||||
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
|
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
|
||||||
@ -46,19 +45,18 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
|||||||
names = device_names;
|
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++) {
|
for (u32 i = 0; i < out_count; i++) {
|
||||||
out_buffer.push_back(names[i]);
|
out_buffer[i] = names[i];
|
||||||
}
|
}
|
||||||
return out_count;
|
return out_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
||||||
const size_t max_count) const {
|
const u32 out_count{static_cast<u32>(std::min(out_buffer.size(), output_device_names.size()))};
|
||||||
const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
|
|
||||||
|
|
||||||
for (u32 i = 0; i < out_count; i++) {
|
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;
|
return out_count;
|
||||||
}
|
}
|
||||||
|
@ -36,20 +36,18 @@ public:
|
|||||||
* Get a list of the available output devices.
|
* Get a list of the available output devices.
|
||||||
*
|
*
|
||||||
* @param out_buffer - Output buffer to write the available device names.
|
* @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.
|
* @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.
|
* Get a list of the available output devices.
|
||||||
* Different to above somehow...
|
* Different to above somehow...
|
||||||
*
|
*
|
||||||
* @param out_buffer - Output buffer to write the available device names.
|
* @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.
|
* @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.
|
* 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,
|
Result Renderer::Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory,
|
Kernel::KTransferMemory* transfer_memory,
|
||||||
const u64 transfer_memory_size, const u32 process_handle,
|
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
|
||||||
Kernel::KProcess& process, const u64 applet_resource_user_id,
|
const u64 applet_resource_user_id, const s32 session_id) {
|
||||||
const s32 session_id) {
|
|
||||||
if (params.execution_mode == ExecutionMode::Auto) {
|
if (params.execution_mode == ExecutionMode::Auto) {
|
||||||
if (!manager.AddSystem(system)) {
|
if (!manager.AddSystem(system)) {
|
||||||
LOG_ERROR(Service_Audio,
|
LOG_ERROR(Service_Audio,
|
||||||
@ -30,7 +29,7 @@ Result Renderer::Initialize(const AudioRendererParameterInternal& params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialized = true;
|
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);
|
applet_resource_user_id, session_id);
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
@ -38,14 +38,14 @@ public:
|
|||||||
* @param params - Input parameters to initialize the system with.
|
* @param params - Input parameters to initialize the system with.
|
||||||
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
|
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
|
||||||
* @param transfer_memory_size - Size of the transfer memory. 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 applet_resource_user_id - Applet id for this renderer. Unused.
|
||||||
* @param session_id - Session id of this renderer.
|
* @param session_id - Session id of this renderer.
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result Initialize(const AudioRendererParameterInternal& params,
|
Result Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
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);
|
s32 session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_,
|
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{input_.data() + sizeof(UpdateDataHeader)},
|
||||||
input_origin{input_}, output{output_.data() + sizeof(UpdateDataHeader)},
|
input_origin{input_}, output{output_.data() + sizeof(UpdateDataHeader)},
|
||||||
output_origin{output_}, in_header{reinterpret_cast<const UpdateDataHeader*>(
|
output_origin{output_}, in_header{reinterpret_cast<const UpdateDataHeader*>(
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KProcess;
|
||||||
|
}
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
class BehaviorInfo;
|
class BehaviorInfo;
|
||||||
class VoiceContext;
|
class VoiceContext;
|
||||||
@ -39,8 +43,8 @@ class InfoUpdater {
|
|||||||
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has the wrong size!");
|
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has the wrong size!");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output, u32 process_handle,
|
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output,
|
||||||
BehaviorInfo& behaviour);
|
Kernel::KProcess* process_handle, BehaviorInfo& behaviour);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the voice channel resources.
|
* Update the voice channel resources.
|
||||||
@ -197,7 +201,7 @@ private:
|
|||||||
/// Expected output size, see CheckConsumedSize
|
/// Expected output size, see CheckConsumedSize
|
||||||
u64 expected_output_size;
|
u64 expected_output_size;
|
||||||
/// Unused
|
/// Unused
|
||||||
u32 process_handle;
|
Kernel::KProcess* process_handle;
|
||||||
/// Behaviour
|
/// Behaviour
|
||||||
BehaviorInfo& behaviour;
|
BehaviorInfo& behaviour;
|
||||||
};
|
};
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
namespace AudioCore::Renderer {
|
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_} {}
|
: process_handle{process_handle_}, force_map{force_map_} {}
|
||||||
|
|
||||||
PoolMapper::PoolMapper(u32 process_handle_, std::span<MemoryPoolInfo> pool_infos_, u32 pool_count_,
|
PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, std::span<MemoryPoolInfo> pool_infos_,
|
||||||
bool force_map_)
|
u32 pool_count_, bool force_map_)
|
||||||
: process_handle{process_handle_}, pool_infos{pool_infos_.data()},
|
: process_handle{process_handle_}, pool_infos{pool_infos_.data()},
|
||||||
pool_count{pool_count_}, force_map{force_map_} {}
|
pool_count{pool_count_}, force_map{force_map_} {}
|
||||||
|
|
||||||
@ -106,15 +106,17 @@ bool PoolMapper::IsForceMapEnabled() const {
|
|||||||
return force_map;
|
return force_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 PoolMapper::GetProcessHandle(const MemoryPoolInfo* pool) const {
|
Kernel::KProcess* PoolMapper::GetProcessHandle(const MemoryPoolInfo* pool) const {
|
||||||
switch (pool->GetLocation()) {
|
switch (pool->GetLocation()) {
|
||||||
case MemoryPoolInfo::Location::CPU:
|
case MemoryPoolInfo::Location::CPU:
|
||||||
return process_handle;
|
return process_handle;
|
||||||
case MemoryPoolInfo::Location::DSP:
|
case MemoryPoolInfo::Location::DSP:
|
||||||
return Kernel::Svc::CurrentProcess;
|
// return Kernel::Svc::CurrentProcess;
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
LOG_WARNING(Service_Audio, "Invalid MemoryPoolInfo location!");
|
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,
|
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 {
|
bool PoolMapper::Unmap(MemoryPoolInfo& pool) const {
|
||||||
[[maybe_unused]] u32 handle{0};
|
[[maybe_unused]] Kernel::KProcess* handle{};
|
||||||
|
|
||||||
switch (pool.GetLocation()) {
|
switch (pool.GetLocation()) {
|
||||||
case MemoryPoolInfo::Location::CPU:
|
case MemoryPoolInfo::Location::CPU:
|
||||||
handle = process_handle;
|
handle = process_handle;
|
||||||
break;
|
break;
|
||||||
case MemoryPoolInfo::Location::DSP:
|
case MemoryPoolInfo::Location::DSP:
|
||||||
handle = Kernel::Svc::CurrentProcess;
|
// handle = Kernel::Svc::CurrentProcess;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// nn::audio::dsp::UnmapUserPointer(handle, pool->cpu_address, pool->size);
|
// nn::audio::dsp::UnmapUserPointer(handle, pool->cpu_address, pool->size);
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KProcess;
|
||||||
|
}
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
class AddressInfo;
|
class AddressInfo;
|
||||||
|
|
||||||
@ -18,9 +22,9 @@ class AddressInfo;
|
|||||||
*/
|
*/
|
||||||
class PoolMapper {
|
class PoolMapper {
|
||||||
public:
|
public:
|
||||||
explicit PoolMapper(u32 process_handle, bool force_map);
|
explicit PoolMapper(Kernel::KProcess* process_handle, bool force_map);
|
||||||
explicit PoolMapper(u32 process_handle, std::span<MemoryPoolInfo> pool_infos, u32 pool_count,
|
explicit PoolMapper(Kernel::KProcess* process_handle, std::span<MemoryPoolInfo> pool_infos,
|
||||||
bool force_map);
|
u32 pool_count, bool force_map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the usage state for all given pools.
|
* Clear the usage state for all given pools.
|
||||||
@ -98,7 +102,7 @@ public:
|
|||||||
* @return CurrentProcessHandle if location == DSP,
|
* @return CurrentProcessHandle if location == DSP,
|
||||||
* the PoolMapper's process_handle if location == CPU
|
* 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.
|
* Map the given region with the given handle. This is a no-op.
|
||||||
@ -167,7 +171,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// Process handle for this mapper, used when location == CPU
|
/// Process handle for this mapper, used when location == CPU
|
||||||
u32 process_handle;
|
Kernel::KProcess* process_handle{};
|
||||||
/// List of memory pools assigned to this mapper
|
/// List of memory pools assigned to this mapper
|
||||||
MemoryPoolInfo* pool_infos{};
|
MemoryPoolInfo* pool_infos{};
|
||||||
/// The number of pools
|
/// The number of pools
|
||||||
|
@ -102,8 +102,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
|||||||
|
|
||||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||||
u32 process_handle_, Kernel::KProcess& process_,
|
Kernel::KProcess* process_handle_, u64 applet_resource_user_id_,
|
||||||
u64 applet_resource_user_id_, s32 session_id_) {
|
s32 session_id_) {
|
||||||
if (!CheckValidRevision(params.revision)) {
|
if (!CheckValidRevision(params.revision)) {
|
||||||
return Service::Audio::ResultInvalidRevision;
|
return Service::Audio::ResultInvalidRevision;
|
||||||
}
|
}
|
||||||
@ -119,7 +119,6 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
|||||||
behavior.SetUserLibRevision(params.revision);
|
behavior.SetUserLibRevision(params.revision);
|
||||||
|
|
||||||
process_handle = process_handle_;
|
process_handle = process_handle_;
|
||||||
process = &process_;
|
|
||||||
applet_resource_user_id = applet_resource_user_id_;
|
applet_resource_user_id = applet_resource_user_id_;
|
||||||
session_id = session_id_;
|
session_id = session_id_;
|
||||||
|
|
||||||
@ -132,7 +131,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
|||||||
render_device = params.rendering_device;
|
render_device = params.rendering_device;
|
||||||
execution_mode = params.execution_mode;
|
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.
|
// 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
|
// 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<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
||||||
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
||||||
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
|
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);
|
||||||
reset_command_buffers = false;
|
reset_command_buffers = false;
|
||||||
command_buffer_size = command_size;
|
command_buffer_size = command_size;
|
||||||
|
@ -74,14 +74,14 @@ public:
|
|||||||
* @param params - Input parameters to initialize the system with.
|
* @param params - Input parameters to initialize the system with.
|
||||||
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
|
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
|
||||||
* @param transfer_memory_size - Size of the transfer memory. 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 applet_resource_user_id - Applet id for this renderer. Unused.
|
||||||
* @param session_id - Session id of this renderer.
|
* @param session_id - Session id of this renderer.
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result Initialize(const AudioRendererParameterInternal& params,
|
Result Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
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);
|
s32 session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -278,9 +278,7 @@ private:
|
|||||||
/// Does what locks do
|
/// Does what locks do
|
||||||
std::mutex lock{};
|
std::mutex lock{};
|
||||||
/// Process this audio render is operating within, used for memory reads/writes.
|
/// Process this audio render is operating within, used for memory reads/writes.
|
||||||
Kernel::KProcess* process{};
|
Kernel::KProcess* process_handle{};
|
||||||
/// Handle for the process for this system, unused
|
|
||||||
u32 process_handle{};
|
|
||||||
/// Applet resource id for this system, unused
|
/// Applet resource id for this system, unused
|
||||||
u64 applet_resource_user_id{};
|
u64 applet_resource_user_id{};
|
||||||
/// Controls performance input and output
|
/// Controls performance input and output
|
||||||
|
@ -67,11 +67,15 @@ public:
|
|||||||
oboe::AudioStreamBuilder builder;
|
oboe::AudioStreamBuilder builder;
|
||||||
|
|
||||||
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
|
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:
|
protected:
|
||||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
|
oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
|
||||||
s32 num_buffer_frames) override {
|
s32 num_buffer_frames) override {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
@ -384,6 +384,12 @@ struct Values {
|
|||||||
AstcRecompression::Bc3,
|
AstcRecompression::Bc3,
|
||||||
"astc_recompression",
|
"astc_recompression",
|
||||||
Category::RendererAdvanced};
|
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,
|
SwitchableSetting<bool> async_presentation{linkage,
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
true,
|
true,
|
||||||
|
@ -122,6 +122,8 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
|||||||
|
|
||||||
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||||
|
|
||||||
|
ENUM(VramUsageMode, Conservative, Aggressive);
|
||||||
|
|
||||||
ENUM(RendererBackend, OpenGL, Vulkan, Null);
|
ENUM(RendererBackend, OpenGL, Vulkan, Null);
|
||||||
|
|
||||||
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
|
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'));
|
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.
|
// Turns " hej " into "hej". Also handles tabs.
|
||||||
std::string StripSpaces(const std::string& str) {
|
std::string StripSpaces(const std::string& str) {
|
||||||
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
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 ToUpper(std::string str);
|
||||||
|
|
||||||
[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
|
[[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 StripSpaces(const std::string& s);
|
||||||
[[nodiscard]] std::string StripQuotes(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_operate_range.h
|
||||||
file_sys/fs_path.h
|
file_sys/fs_path.h
|
||||||
file_sys/fs_path_utility.h
|
file_sys/fs_path_utility.h
|
||||||
|
file_sys/fs_save_data_types.h
|
||||||
file_sys/fs_string_util.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.cpp
|
||||||
file_sys/fsmitm_romfsbuild.h
|
file_sys/fsmitm_romfsbuild.h
|
||||||
|
file_sys/fssrv/fssrv_sf_path.h
|
||||||
file_sys/fssystem/fs_i_storage.h
|
file_sys/fssystem/fs_i_storage.h
|
||||||
file_sys/fssystem/fs_types.h
|
file_sys/fssystem/fs_types.h
|
||||||
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
|
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/system_applet_proxy.h
|
||||||
hle/service/am/service/window_controller.cpp
|
hle/service/am/service/window_controller.cpp
|
||||||
hle/service/am/service/window_controller.h
|
hle/service/am/service/window_controller.h
|
||||||
hle/service/aoc/aoc_u.cpp
|
hle/service/aoc/addon_content_manager.cpp
|
||||||
hle/service/aoc/aoc_u.h
|
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.cpp
|
||||||
hle/service/apm/apm.h
|
hle/service/apm/apm.h
|
||||||
hle/service/apm/apm_controller.cpp
|
hle/service/apm/apm_controller.cpp
|
||||||
hle/service/apm/apm_controller.h
|
hle/service/apm/apm_controller.h
|
||||||
hle/service/apm/apm_interface.cpp
|
hle/service/apm/apm_interface.cpp
|
||||||
hle/service/apm/apm_interface.h
|
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.cpp
|
||||||
hle/service/audio/audio_controller.h
|
hle/service/audio/audio_controller.h
|
||||||
hle/service/audio/audout_u.cpp
|
hle/service/audio/audio_device.cpp
|
||||||
hle/service/audio/audout_u.h
|
hle/service/audio/audio_device.h
|
||||||
hle/service/audio/audrec_a.cpp
|
hle/service/audio/audio_in_manager.cpp
|
||||||
hle/service/audio/audrec_a.h
|
hle/service/audio/audio_in_manager.h
|
||||||
hle/service/audio/audrec_u.cpp
|
hle/service/audio/audio_in.cpp
|
||||||
hle/service/audio/audrec_u.h
|
hle/service/audio/audio_in.h
|
||||||
hle/service/audio/audren_u.cpp
|
hle/service/audio/audio_out_manager.cpp
|
||||||
hle/service/audio/audren_u.h
|
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/errors.h
|
||||||
hle/service/audio/hwopus.cpp
|
hle/service/audio/final_output_recorder_manager_for_applet.cpp
|
||||||
hle/service/audio/hwopus.h
|
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.cpp
|
||||||
hle/service/bcat/backend/backend.h
|
hle/service/bcat/backend/backend.h
|
||||||
hle/service/bcat/bcat.cpp
|
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_file.h
|
||||||
hle/service/filesystem/fsp/fs_i_filesystem.cpp
|
hle/service/filesystem/fsp/fs_i_filesystem.cpp
|
||||||
hle/service/filesystem/fsp/fs_i_filesystem.h
|
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.cpp
|
||||||
hle/service/filesystem/fsp/fs_i_storage.h
|
hle/service/filesystem/fsp/fs_i_storage.h
|
||||||
hle/service/filesystem/fsp/fsp_ldr.cpp
|
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_pr.h
|
||||||
hle/service/filesystem/fsp/fsp_srv.cpp
|
hle/service/filesystem/fsp/fsp_srv.cpp
|
||||||
hle/service/filesystem/fsp/fsp_srv.h
|
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.cpp
|
||||||
hle/service/filesystem/romfs_controller.h
|
hle/service/filesystem/romfs_controller.h
|
||||||
hle/service/filesystem/save_data_controller.cpp
|
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.cpp
|
||||||
hle/service/nvnflinger/ui/graphic_buffer.h
|
hle/service/nvnflinger/ui/graphic_buffer.h
|
||||||
hle/service/nvnflinger/window.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.cpp
|
||||||
hle/service/olsc/olsc.h
|
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.cpp
|
||||||
hle/service/omm/omm.h
|
hle/service/omm/omm.h
|
||||||
hle/service/omm/operation_mode_manager.cpp
|
hle/service/omm/operation_mode_manager.cpp
|
||||||
@ -881,16 +916,35 @@ add_library(core STATIC
|
|||||||
hle/service/os/mutex.h
|
hle/service/os/mutex.h
|
||||||
hle/service/pcie/pcie.cpp
|
hle/service/pcie/pcie.cpp
|
||||||
hle/service/pcie/pcie.h
|
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.cpp
|
||||||
hle/service/pctl/pctl.h
|
hle/service/pctl/pctl.h
|
||||||
hle/service/pctl/pctl_module.cpp
|
hle/service/pctl/pctl_results.h
|
||||||
hle/service/pctl/pctl_module.h
|
hle/service/pctl/pctl_types.h
|
||||||
hle/service/pcv/pcv.cpp
|
hle/service/pcv/pcv.cpp
|
||||||
hle/service/pcv/pcv.h
|
hle/service/pcv/pcv.h
|
||||||
hle/service/pm/pm.cpp
|
hle/service/pm/pm.cpp
|
||||||
hle/service/pm/pm.h
|
hle/service/pm/pm.h
|
||||||
hle/service/prepo/prepo.cpp
|
hle/service/prepo/prepo.cpp
|
||||||
hle/service/prepo/prepo.h
|
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.cpp
|
||||||
hle/service/psc/psc.h
|
hle/service/psc/psc.h
|
||||||
hle/service/psc/time/alarms.cpp
|
hle/service/psc/time/alarms.cpp
|
||||||
|
@ -43,6 +43,8 @@ public:
|
|||||||
DeviceMemoryManager(const DeviceMemory& device_memory);
|
DeviceMemoryManager(const DeviceMemory& device_memory);
|
||||||
~DeviceMemoryManager();
|
~DeviceMemoryManager();
|
||||||
|
|
||||||
|
static constexpr bool HAS_FLUSH_INVALIDATION = true;
|
||||||
|
|
||||||
void BindInterface(DeviceInterface* device_inter);
|
void BindInterface(DeviceInterface* device_inter);
|
||||||
|
|
||||||
DAddr Allocate(size_t size);
|
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];
|
auto* memory_device_inter = registered_processes[asid.id];
|
||||||
const auto release_pending = [&] {
|
const auto release_pending = [&] {
|
||||||
if (uncache_bytes > 0) {
|
if (uncache_bytes > 0) {
|
||||||
|
if (memory_device_inter != nullptr) {
|
||||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
||||||
uncache_bytes, false);
|
uncache_bytes, false);
|
||||||
|
}
|
||||||
uncache_bytes = 0;
|
uncache_bytes = 0;
|
||||||
}
|
}
|
||||||
if (cache_bytes > 0) {
|
if (cache_bytes > 0) {
|
||||||
|
if (memory_device_inter != nullptr) {
|
||||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
|
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
|
||||||
cache_bytes, true);
|
cache_bytes, true);
|
||||||
|
}
|
||||||
cache_bytes = 0;
|
cache_bytes = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,8 @@ enum class OpenDirectoryMode : u64 {
|
|||||||
File = (1 << 1),
|
File = (1 << 1),
|
||||||
|
|
||||||
All = (Directory | File),
|
All = (Directory | File),
|
||||||
|
|
||||||
|
NotRequireFileSize = (1ULL << 31),
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
|
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
|
||||||
|
|
||||||
@ -36,4 +38,29 @@ enum class CreateOption : u8 {
|
|||||||
BigFile = (1 << 0),
|
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
|
} // namespace FileSys
|
||||||
|
@ -10,7 +10,7 @@ namespace FileSys {
|
|||||||
|
|
||||||
constexpr size_t RequiredAlignment = alignof(u64);
|
constexpr size_t RequiredAlignment = alignof(u64);
|
||||||
|
|
||||||
void* AllocateUnsafe(size_t size) {
|
inline void* AllocateUnsafe(size_t size) {
|
||||||
// Allocate
|
// Allocate
|
||||||
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
|
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
|
||||||
|
|
||||||
@ -21,16 +21,16 @@ void* AllocateUnsafe(size_t size) {
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeallocateUnsafe(void* ptr, size_t size) {
|
inline void DeallocateUnsafe(void* ptr, size_t size) {
|
||||||
// Deallocate the pointer
|
// Deallocate the pointer
|
||||||
::operator delete(ptr, std::align_val_t{RequiredAlignment});
|
::operator delete(ptr, std::align_val_t{RequiredAlignment});
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Allocate(size_t size) {
|
inline void* Allocate(size_t size) {
|
||||||
return AllocateUnsafe(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 the pointer is non-null, deallocate it
|
||||||
if (ptr != nullptr) {
|
if (ptr != nullptr) {
|
||||||
DeallocateUnsafe(ptr, size);
|
DeallocateUnsafe(ptr, size);
|
||||||
|
@ -381,7 +381,7 @@ public:
|
|||||||
|
|
||||||
// Check that it's possible for us to remove a child
|
// Check that it's possible for us to remove a child
|
||||||
auto* p = m_write_buffer.Get();
|
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);
|
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
|
||||||
|
|
||||||
// Handle a trailing separator
|
// Handle a trailing separator
|
||||||
|
@ -426,8 +426,9 @@ public:
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size,
|
static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
|
||||||
bool is_windows_path, bool is_drive_relative_path,
|
size_t max_out_size, bool is_windows_path,
|
||||||
|
bool is_drive_relative_path,
|
||||||
bool allow_all_characters = false) {
|
bool allow_all_characters = false) {
|
||||||
// Use StringTraits names for remainder of scope
|
// Use StringTraits names for remainder of scope
|
||||||
using namespace StringTraits;
|
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;
|
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>
|
template <typename T>
|
||||||
constexpr int Strnlen(const T* str, int count) {
|
constexpr int Strnlen(const T* str, int count) {
|
||||||
ASSERT(str != nullptr);
|
ASSERT(str != nullptr);
|
||||||
@ -32,6 +37,11 @@ constexpr int Strnlen(const T* str, int count) {
|
|||||||
return length;
|
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>
|
template <typename T>
|
||||||
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
||||||
ASSERT(lhs != nullptr);
|
ASSERT(lhs != nullptr);
|
||||||
@ -51,6 +61,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
|||||||
return l - r;
|
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>
|
template <typename T>
|
||||||
static constexpr int Strlcpy(T* dst, const T* src, int count) {
|
static constexpr int Strlcpy(T* dst, const T* src, int count) {
|
||||||
ASSERT(dst != nullptr);
|
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
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "core/crypto/aes_util.h"
|
#include "core/crypto/aes_util.h"
|
||||||
|
@ -14,48 +14,11 @@ namespace FileSys {
|
|||||||
|
|
||||||
namespace {
|
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) {
|
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) {
|
||||||
return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage ||
|
return attr.type == SaveDataType::Cache || attr.type == SaveDataType::Temporary ||
|
||||||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
|
(space == SaveDataSpaceId::User && ///< Normal Save Data -- Current Title & User
|
||||||
(attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) &&
|
(attr.type == SaveDataType::Account || attr.type == SaveDataType::Device) &&
|
||||||
attr.title_id == 0 && attr.save_id == 0);
|
attr.program_id == 0 && attr.system_save_data_id == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id,
|
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.
|
// Only detect nand user saves.
|
||||||
const auto space_id_path = [space_id]() -> std::string_view {
|
const auto space_id_path = [space_id]() -> std::string_view {
|
||||||
switch (space_id) {
|
switch (space_id) {
|
||||||
case SaveDataSpaceId::NandUser:
|
case SaveDataSpaceId::User:
|
||||||
return "/user/save";
|
return "/user/save";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
@ -79,9 +42,9 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
|||||||
|
|
||||||
// Only detect account/device saves from the future location.
|
// Only detect account/device saves from the future location.
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SaveDataType::SaveData:
|
case SaveDataType::Account:
|
||||||
return fmt::format("{}/account/{}/{:016X}/0", space_id_path, uuid.RawString(), title_id);
|
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);
|
return fmt::format("{}/device/{:016X}/0", space_id_path, title_id);
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
@ -90,13 +53,6 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
|||||||
|
|
||||||
} // Anonymous namespace
|
} // 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_,
|
SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
|
||||||
VirtualDir save_directory_)
|
VirtualDir save_directory_)
|
||||||
: system{system_}, program_id{program_id_}, dir{std::move(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;
|
SaveDataFactory::~SaveDataFactory() = default;
|
||||||
|
|
||||||
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
||||||
PrintSaveDataAttributeWarnings(meta);
|
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
|
||||||
|
meta.user_id, meta.system_save_data_id);
|
||||||
const auto save_directory =
|
|
||||||
GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
|
||||||
|
|
||||||
return dir->CreateDirectoryRelative(save_directory);
|
return dir->CreateDirectoryRelative(save_directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
||||||
|
|
||||||
const auto save_directory =
|
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
|
||||||
GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
meta.user_id, meta.system_save_data_id);
|
||||||
|
|
||||||
auto out = dir->GetDirectoryRelative(save_directory);
|
auto out = dir->GetDirectoryRelative(save_directory);
|
||||||
|
|
||||||
@ -136,11 +90,11 @@ VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) con
|
|||||||
|
|
||||||
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||||
switch (space) {
|
switch (space) {
|
||||||
case SaveDataSpaceId::NandSystem:
|
case SaveDataSpaceId::System:
|
||||||
return "/system/";
|
return "/system/";
|
||||||
case SaveDataSpaceId::NandUser:
|
case SaveDataSpaceId::User:
|
||||||
return "/user/";
|
return "/user/";
|
||||||
case SaveDataSpaceId::TemporaryStorage:
|
case SaveDataSpaceId::Temporary:
|
||||||
return "/temp/";
|
return "/temp/";
|
||||||
default:
|
default:
|
||||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
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) {
|
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
|
// 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.
|
// 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) {
|
if (title_id == 0) {
|
||||||
title_id = program_id;
|
title_id = program_id;
|
||||||
}
|
}
|
||||||
@ -173,16 +127,16 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
|
|||||||
std::string out = GetSaveDataSpaceIdPath(space);
|
std::string out = GetSaveDataSpaceIdPath(space);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SaveDataType::SystemSaveData:
|
case SaveDataType::System:
|
||||||
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
|
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
|
||||||
case SaveDataType::SaveData:
|
case SaveDataType::Account:
|
||||||
case SaveDataType::DeviceSaveData:
|
case SaveDataType::Device:
|
||||||
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
|
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
|
||||||
title_id);
|
title_id);
|
||||||
case SaveDataType::TemporaryStorage:
|
case SaveDataType::Temporary:
|
||||||
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
|
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
|
||||||
title_id);
|
title_id);
|
||||||
case SaveDataType::CacheStorage:
|
case SaveDataType::Cache:
|
||||||
return fmt::format("{}save/cache/{:016X}", out, title_id);
|
return fmt::format("{}save/cache/{:016X}", out, title_id);
|
||||||
default:
|
default:
|
||||||
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
|
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,
|
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||||
u128 user_id) const {
|
u128 user_id) const {
|
||||||
const auto path =
|
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 relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||||
|
|
||||||
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
|
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,
|
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||||
SaveDataSize new_value) const {
|
SaveDataSize new_value) const {
|
||||||
const auto path =
|
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 relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||||
|
|
||||||
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
|
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/file_sys/fs_save_data_types.h"
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
@ -16,73 +17,6 @@ class System;
|
|||||||
|
|
||||||
namespace FileSys {
|
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() {
|
constexpr const char* GetSaveDataSizeFileName() {
|
||||||
return ".yuzu_save_size";
|
return ".yuzu_save_size";
|
||||||
}
|
}
|
||||||
|
@ -44,15 +44,32 @@ public:
|
|||||||
GuestMemory() = delete;
|
GuestMemory() = delete;
|
||||||
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
|
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
|
||||||
Common::ScratchBuffer<T>* backup = nullptr)
|
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);
|
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);
|
Read(addr, size, backup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~GuestMemory() = default;
|
~GuestMemory() = default;
|
||||||
|
|
||||||
|
GuestMemory(GuestMemory&& rhs) = default;
|
||||||
|
GuestMemory& operator=(GuestMemory&& rhs) = default;
|
||||||
|
|
||||||
T* data() noexcept {
|
T* data() noexcept {
|
||||||
return m_data_span.data();
|
return m_data_span.data();
|
||||||
}
|
}
|
||||||
@ -109,8 +126,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->TrySetSpan()) {
|
if (this->TrySetSpan()) {
|
||||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
if constexpr (FLAGS & GuestMemoryFlags::Safe && M::HAS_FLUSH_INVALIDATION) {
|
||||||
m_memory.FlushRegion(m_addr, this->size_bytes());
|
m_memory->FlushRegion(m_addr, this->size_bytes());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (backup) {
|
if (backup) {
|
||||||
@ -123,9 +140,9 @@ public:
|
|||||||
m_is_data_copy = true;
|
m_is_data_copy = true;
|
||||||
m_span_valid = true;
|
m_span_valid = true;
|
||||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
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 {
|
} 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;
|
return m_data_span;
|
||||||
@ -133,18 +150,19 @@ public:
|
|||||||
|
|
||||||
void Write(std::span<T> write_data) noexcept {
|
void Write(std::span<T> write_data) noexcept {
|
||||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
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) {
|
} 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 {
|
} 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 {
|
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_data_span = {reinterpret_cast<T*>(ptr), this->size()};
|
||||||
m_span_valid = true;
|
m_span_valid = true;
|
||||||
|
m_is_data_copy = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -159,7 +177,7 @@ protected:
|
|||||||
return m_addr_changed;
|
return m_addr_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
M& m_memory;
|
M* m_memory;
|
||||||
u64 m_addr{};
|
u64 m_addr{};
|
||||||
size_t m_size{};
|
size_t m_size{};
|
||||||
std::span<T> m_data_span{};
|
std::span<T> m_data_span{};
|
||||||
@ -175,17 +193,7 @@ public:
|
|||||||
GuestMemoryScoped() = delete;
|
GuestMemoryScoped() = delete;
|
||||||
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
|
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
|
||||||
Common::ScratchBuffer<T>* backup = nullptr)
|
Common::ScratchBuffer<T>* backup = nullptr)
|
||||||
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
|
: 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~GuestMemoryScoped() {
|
~GuestMemoryScoped() {
|
||||||
if constexpr (FLAGS & GuestMemoryFlags::Write) {
|
if constexpr (FLAGS & GuestMemoryFlags::Write) {
|
||||||
@ -196,15 +204,17 @@ public:
|
|||||||
if (this->AddressChanged() || this->IsDataCopy()) {
|
if (this->AddressChanged() || this->IsDataCopy()) {
|
||||||
ASSERT(this->m_span_valid);
|
ASSERT(this->m_span_valid);
|
||||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
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) {
|
} 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 {
|
} 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) ||
|
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
|
||||||
(FLAGS & GuestMemoryFlags::Cached)) {
|
(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());
|
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
||||||
|
|
||||||
FileSys::SaveDataAttribute attribute{};
|
FileSys::SaveDataAttribute attribute{};
|
||||||
attribute.title_id = m_applet->program_id;
|
attribute.program_id = m_applet->program_id;
|
||||||
attribute.user_id = user_id.AsU128();
|
attribute.user_id = user_id.AsU128();
|
||||||
attribute.type = FileSys::SaveDataType::SaveData;
|
attribute.type = FileSys::SaveDataType::Account;
|
||||||
|
|
||||||
FileSys::VirtualDir save_data{};
|
FileSys::VirtualDir save_data{};
|
||||||
R_TRY(system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
|
R_TRY(system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
|
||||||
&save_data, FileSys::SaveDataSpaceId::NandUser, attribute));
|
&save_data, FileSys::SaveDataSpaceId::User, attribute));
|
||||||
|
|
||||||
*out_size = 0;
|
*out_size = 0;
|
||||||
R_SUCCEED();
|
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
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "core/core.h"
|
#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.h"
|
||||||
#include "core/hle/service/audio/audio_controller.h"
|
#include "core/hle/service/audio/audio_controller.h"
|
||||||
#include "core/hle/service/audio/audout_u.h"
|
#include "core/hle/service/audio/audio_in_manager.h"
|
||||||
#include "core/hle/service/audio/audrec_a.h"
|
#include "core/hle/service/audio/audio_out_manager.h"
|
||||||
#include "core/hle/service/audio/audrec_u.h"
|
#include "core/hle/service/audio/audio_renderer_manager.h"
|
||||||
#include "core/hle/service/audio/audren_u.h"
|
#include "core/hle/service/audio/final_output_recorder_manager.h"
|
||||||
#include "core/hle/service/audio/hwopus.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/server_manager.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
@ -19,12 +19,16 @@ void LoopProcess(Core::System& system) {
|
|||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(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<IAudioInManager>(system));
|
||||||
server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system));
|
server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system));
|
||||||
server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system));
|
server_manager->RegisterNamedService(
|
||||||
server_manager->RegisterNamedService("audrec:u", std::make_shared<AudRecU>(system));
|
"audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system));
|
||||||
server_manager->RegisterNamedService("audren:u", std::make_shared<AudRenU>(system));
|
server_manager->RegisterNamedService("audrec:u",
|
||||||
server_manager->RegisterNamedService("hwopus", std::make_shared<HwOpus>(system));
|
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));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,27 +16,27 @@ IAudioController::IAudioController(Core::System& system_)
|
|||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "GetTargetVolume"},
|
{0, nullptr, "GetTargetVolume"},
|
||||||
{1, nullptr, "SetTargetVolume"},
|
{1, nullptr, "SetTargetVolume"},
|
||||||
{2, C<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
{2, D<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
||||||
{3, C<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
{3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
||||||
{4, nullptr, "IsTargetMute"},
|
{4, nullptr, "IsTargetMute"},
|
||||||
{5, nullptr, "SetTargetMute"},
|
{5, nullptr, "SetTargetMute"},
|
||||||
{6, nullptr, "IsTargetConnected"},
|
{6, nullptr, "IsTargetConnected"},
|
||||||
{7, nullptr, "SetDefaultTarget"},
|
{7, nullptr, "SetDefaultTarget"},
|
||||||
{8, nullptr, "GetDefaultTarget"},
|
{8, nullptr, "GetDefaultTarget"},
|
||||||
{9, C<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
|
{9, D<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
|
||||||
{10, C<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
|
{10, D<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
|
||||||
{11, nullptr, "SetForceMutePolicy"},
|
{11, nullptr, "SetForceMutePolicy"},
|
||||||
{12, C<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
|
{12, D<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
|
||||||
{13, C<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
|
{13, D<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
|
||||||
{14, C<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
|
{14, D<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
|
||||||
{15, nullptr, "SetOutputTarget"},
|
{15, nullptr, "SetOutputTarget"},
|
||||||
{16, nullptr, "SetInputTargetForceEnabled"},
|
{16, nullptr, "SetInputTargetForceEnabled"},
|
||||||
{17, C<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
|
{17, D<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
|
||||||
{18, C<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
|
{18, D<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
|
||||||
{19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
|
{19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
|
||||||
{20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
|
{20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
|
||||||
{21, nullptr, "GetAudioOutputTargetForPlayReport"},
|
{21, nullptr, "GetAudioOutputTargetForPlayReport"},
|
||||||
{22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
|
{22, D<&IAudioController::NotifyHeadphoneVolumeWarningDisplayedEvent>, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
|
||||||
{23, nullptr, "SetSystemOutputMasterVolume"},
|
{23, nullptr, "SetSystemOutputMasterVolume"},
|
||||||
{24, nullptr, "GetSystemOutputMasterVolume"},
|
{24, nullptr, "GetSystemOutputMasterVolume"},
|
||||||
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
|
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
|
||||||
@ -44,11 +44,11 @@ IAudioController::IAudioController(Core::System& system_)
|
|||||||
{27, nullptr, "SetVolumeMappingTableForDev"},
|
{27, nullptr, "SetVolumeMappingTableForDev"},
|
||||||
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
|
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
|
||||||
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||||
{30, C<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
{30, D<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
||||||
{31, C<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
{31, D<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
||||||
{32, nullptr, "GetActiveOutputTarget"},
|
{32, nullptr, "GetActiveOutputTarget"},
|
||||||
{33, nullptr, "GetTargetDeviceInfo"},
|
{33, nullptr, "GetTargetDeviceInfo"},
|
||||||
{34, C<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
{34, D<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
||||||
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||||
{36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
{36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||||
{37, nullptr, "SetHearingProtectionSafeguardEnabled"},
|
{37, nullptr, "SetHearingProtectionSafeguardEnabled"},
|
||||||
@ -138,7 +138,7 @@ Result IAudioController::SetOutputModeSetting(Set::AudioOutputModeTarget target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result IAudioController::SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode) {
|
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();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +150,11 @@ Result IAudioController::GetHeadphoneOutputLevelMode(
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IAudioController::NotifyHeadphoneVolumeWarningDisplayedEvent() {
|
||||||
|
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) {
|
Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) {
|
||||||
LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", 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);
|
Set::AudioOutputMode output_mode);
|
||||||
Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode);
|
Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode);
|
||||||
Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode);
|
Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode);
|
||||||
|
Result NotifyHeadphoneVolumeWarningDisplayedEvent();
|
||||||
Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled);
|
Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled);
|
||||||
Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled);
|
Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled);
|
||||||
Result AcquireTargetNotification(OutCopyHandle<Kernel::KReadableEvent> out_notification_event);
|
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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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 {
|
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[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "OpenFinalOutputRecorder"},
|
{0, nullptr, "OpenFinalOutputRecorder"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudRecU::~AudRecU() = default;
|
IFinalOutputRecorderManager::~IFinalOutputRecorderManager() = default;
|
||||||
|
|
||||||
} // namespace Service::Audio
|
} // namespace Service::Audio
|
@ -11,10 +11,10 @@ class System;
|
|||||||
|
|
||||||
namespace Service::Audio {
|
namespace Service::Audio {
|
||||||
|
|
||||||
class AudRecA final : public ServiceFramework<AudRecA> {
|
class IFinalOutputRecorderManager final : public ServiceFramework<IFinalOutputRecorderManager> {
|
||||||
public:
|
public:
|
||||||
explicit AudRecA(Core::System& system_);
|
explicit IFinalOutputRecorderManager(Core::System& system_);
|
||||||
~AudRecA() override;
|
~IFinalOutputRecorderManager() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Audio
|
} // namespace Service::Audio
|
@ -1,11 +1,12 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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 {
|
namespace Service::Audio {
|
||||||
|
|
||||||
AudRecA::AudRecA(Core::System& system_) : ServiceFramework{system_, "audrec:a"} {
|
IFinalOutputRecorderManagerForApplet::IFinalOutputRecorderManagerForApplet(Core::System& system_)
|
||||||
|
: ServiceFramework{system_, "audrec:a"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "RequestSuspend"},
|
{0, nullptr, "RequestSuspend"},
|
||||||
@ -16,6 +17,6 @@ AudRecA::AudRecA(Core::System& system_) : ServiceFramework{system_, "audrec:a"}
|
|||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudRecA::~AudRecA() = default;
|
IFinalOutputRecorderManagerForApplet::~IFinalOutputRecorderManagerForApplet() = default;
|
||||||
|
|
||||||
} // namespace Service::Audio
|
} // namespace Service::Audio
|
@ -11,10 +11,11 @@ class System;
|
|||||||
|
|
||||||
namespace Service::Audio {
|
namespace Service::Audio {
|
||||||
|
|
||||||
class AudRecU final : public ServiceFramework<AudRecU> {
|
class IFinalOutputRecorderManagerForApplet final
|
||||||
|
: public ServiceFramework<IFinalOutputRecorderManagerForApplet> {
|
||||||
public:
|
public:
|
||||||
explicit AudRecU(Core::System& system_);
|
explicit IFinalOutputRecorderManagerForApplet(Core::System& system_);
|
||||||
~AudRecU() override;
|
~IFinalOutputRecorderManagerForApplet() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Audio
|
} // 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/core.h"
|
||||||
#include "core/hle/kernel/k_event.h"
|
#include "core/hle/kernel/k_event.h"
|
||||||
#include "core/hle/service/btdrv/btdrv.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/ipc_helpers.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
@ -13,9 +14,9 @@
|
|||||||
|
|
||||||
namespace Service::BtDrv {
|
namespace Service::BtDrv {
|
||||||
|
|
||||||
class Bt final : public ServiceFramework<Bt> {
|
class IBluetoothUser final : public ServiceFramework<IBluetoothUser> {
|
||||||
public:
|
public:
|
||||||
explicit Bt(Core::System& system_)
|
explicit IBluetoothUser(Core::System& system_)
|
||||||
: ServiceFramework{system_, "bt"}, service_context{system_, "bt"} {
|
: ServiceFramework{system_, "bt"}, service_context{system_, "bt"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
@ -28,7 +29,7 @@ public:
|
|||||||
{6, nullptr, "SetLeResponse"},
|
{6, nullptr, "SetLeResponse"},
|
||||||
{7, nullptr, "LeSendIndication"},
|
{7, nullptr, "LeSendIndication"},
|
||||||
{8, nullptr, "GetLeEventInfo"},
|
{8, nullptr, "GetLeEventInfo"},
|
||||||
{9, &Bt::RegisterBleEvent, "RegisterBleEvent"},
|
{9, C<&IBluetoothUser::RegisterBleEvent>, "RegisterBleEvent"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
@ -36,17 +37,16 @@ public:
|
|||||||
register_event = service_context.CreateEvent("BT:RegisterEvent");
|
register_event = service_context.CreateEvent("BT:RegisterEvent");
|
||||||
}
|
}
|
||||||
|
|
||||||
~Bt() override {
|
~IBluetoothUser() override {
|
||||||
service_context.CloseEvent(register_event);
|
service_context.CloseEvent(register_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RegisterBleEvent(HLERequestContext& ctx) {
|
Result RegisterBleEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
*out_event = ®ister_event->GetReadableEvent();
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.PushCopyObjects(register_event->GetReadableEvent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelHelpers::ServiceContext service_context;
|
KernelHelpers::ServiceContext service_context;
|
||||||
@ -54,9 +54,9 @@ private:
|
|||||||
Kernel::KEvent* register_event;
|
Kernel::KEvent* register_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BtDrv final : public ServiceFramework<BtDrv> {
|
class IBluetoothDriver final : public ServiceFramework<IBluetoothDriver> {
|
||||||
public:
|
public:
|
||||||
explicit BtDrv(Core::System& system_) : ServiceFramework{system_, "btdrv"} {
|
explicit IBluetoothDriver(Core::System& system_) : ServiceFramework{system_, "btdrv"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "InitializeBluetoothDriver"},
|
{0, nullptr, "InitializeBluetoothDriver"},
|
||||||
@ -93,7 +93,7 @@ public:
|
|||||||
{31, nullptr, "EnableMcMode"},
|
{31, nullptr, "EnableMcMode"},
|
||||||
{32, nullptr, "EnableLlrScan"},
|
{32, nullptr, "EnableLlrScan"},
|
||||||
{33, nullptr, "DisableLlrScan"},
|
{33, nullptr, "DisableLlrScan"},
|
||||||
{34, nullptr, "EnableRadio"},
|
{34, C<&IBluetoothDriver::EnableRadio>, "EnableRadio"},
|
||||||
{35, nullptr, "SetVisibility"},
|
{35, nullptr, "SetVisibility"},
|
||||||
{36, nullptr, "EnableTbfcScan"},
|
{36, nullptr, "EnableTbfcScan"},
|
||||||
{37, nullptr, "RegisterHidReportEvent"},
|
{37, nullptr, "RegisterHidReportEvent"},
|
||||||
@ -195,13 +195,19 @@ public:
|
|||||||
|
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result EnableRadio() {
|
||||||
|
LOG_WARNING(Service_BTDRV, "(STUBBED) called");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("btdrv", std::make_shared<BtDrv>(system));
|
server_manager->RegisterNamedService("btdrv", std::make_shared<IBluetoothDriver>(system));
|
||||||
server_manager->RegisterNamedService("bt", std::make_shared<Bt>(system));
|
server_manager->RegisterNamedService("bt", std::make_shared<IBluetoothUser>(system));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
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];
|
auto& buffer = temp[OutBufferIndex];
|
||||||
const size_t size = buffer.size();
|
const size_t size = buffer.size();
|
||||||
|
|
||||||
if (ctx.CanWriteBuffer(OutBufferIndex)) {
|
if (size > 0 && ctx.CanWriteBuffer(OutBufferIndex)) {
|
||||||
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
|
||||||
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
|
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
|
||||||
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
|
||||||
|
@ -3,82 +3,34 @@
|
|||||||
|
|
||||||
#include "core/file_sys/fs_filesystem.h"
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
#include "core/file_sys/savedata_factory.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/filesystem/fsp/fs_i_directory.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
|
||||||
|
|
||||||
namespace Service::FileSystem {
|
namespace Service::FileSystem {
|
||||||
|
|
||||||
template <typename T>
|
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
|
||||||
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_,
|
|
||||||
FileSys::OpenDirectoryMode mode)
|
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[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IDirectory::Read, "Read"},
|
{0, D<&IDirectory::Read>, "Read"},
|
||||||
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
|
{1, D<&IDirectory::GetEntryCount>, "GetEntryCount"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
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.");
|
LOG_DEBUG(Service_FS, "called.");
|
||||||
|
|
||||||
// Calculate how many entries we can fit in the output buffer
|
R_RETURN(backend->Read(out_count, out_entries.data(), out_entries.size()));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDirectory::GetEntryCount(HLERequestContext& ctx) {
|
Result IDirectory::GetEntryCount(Out<s64> out_count) {
|
||||||
LOG_DEBUG(Service_FS, "called");
|
LOG_DEBUG(Service_FS, "called");
|
||||||
|
|
||||||
u64 count = entries.size() - next_entry_index;
|
R_RETURN(backend->GetEntryCount(out_count));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push(count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::FileSystem
|
} // namespace Service::FileSystem
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_sys/fsa/fs_i_directory.h"
|
||||||
#include "core/file_sys/vfs/vfs.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/filesystem.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
@ -15,16 +17,15 @@ namespace Service::FileSystem {
|
|||||||
|
|
||||||
class IDirectory final : public ServiceFramework<IDirectory> {
|
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||||
public:
|
public:
|
||||||
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
|
explicit IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
|
||||||
FileSys::OpenDirectoryMode mode);
|
FileSys::OpenDirectoryMode mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileSys::VirtualDir backend;
|
std::unique_ptr<FileSys::Fsa::IDirectory> backend;
|
||||||
std::vector<FileSys::DirectoryEntry> entries;
|
|
||||||
u64 next_entry_index = 0;
|
|
||||||
|
|
||||||
void Read(HLERequestContext& ctx);
|
Result Read(Out<s64> out_count,
|
||||||
void GetEntryCount(HLERequestContext& ctx);
|
const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries);
|
||||||
|
Result GetEntryCount(Out<s64> out_count);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::FileSystem
|
} // namespace Service::FileSystem
|
||||||
|
@ -2,126 +2,64 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "core/file_sys/errors.h"
|
#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/filesystem/fsp/fs_i_file.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
|
||||||
|
|
||||||
namespace Service::FileSystem {
|
namespace Service::FileSystem {
|
||||||
|
|
||||||
IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
|
IFile::IFile(Core::System& system_, FileSys::VirtualFile file_)
|
||||||
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
|
: ServiceFramework{system_, "IFile"}, backend{std::make_unique<FileSys::Fsa::IFile>(file_)} {
|
||||||
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IFile::Read, "Read"},
|
{0, D<&IFile::Read>, "Read"},
|
||||||
{1, &IFile::Write, "Write"},
|
{1, D<&IFile::Write>, "Write"},
|
||||||
{2, &IFile::Flush, "Flush"},
|
{2, D<&IFile::Flush>, "Flush"},
|
||||||
{3, &IFile::SetSize, "SetSize"},
|
{3, D<&IFile::SetSize>, "SetSize"},
|
||||||
{4, &IFile::GetSize, "GetSize"},
|
{4, D<&IFile::GetSize>, "GetSize"},
|
||||||
{5, nullptr, "OperateRange"},
|
{5, nullptr, "OperateRange"},
|
||||||
{6, nullptr, "OperateRangeWithBuffer"},
|
{6, nullptr, "OperateRangeWithBuffer"},
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFile::Read(HLERequestContext& ctx) {
|
Result IFile::Read(
|
||||||
IPC::RequestParser rp{ctx};
|
FileSys::ReadOption option, Out<s64> out_size, s64 offset,
|
||||||
const u64 option = rp.Pop<u64>();
|
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
|
||||||
const s64 offset = rp.Pop<s64>();
|
s64 size) {
|
||||||
const s64 length = rp.Pop<s64>();
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the data from the Storage backend
|
// Read the data from the Storage backend
|
||||||
std::vector<u8> output = backend->ReadBytes(length, offset);
|
R_RETURN(
|
||||||
|
backend->Read(reinterpret_cast<size_t*>(out_size.Get()), offset, out_buffer.data(), size));
|
||||||
// Write the data to memory
|
|
||||||
ctx.WriteBuffer(output);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push(static_cast<u64>(output.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFile::Write(HLERequestContext& ctx) {
|
Result IFile::Write(
|
||||||
IPC::RequestParser rp{ctx};
|
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
|
||||||
const u64 option = rp.Pop<u64>();
|
FileSys::WriteOption option, s64 offset, s64 size) {
|
||||||
const s64 offset = rp.Pop<s64>();
|
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
|
||||||
const s64 length = rp.Pop<s64>();
|
size);
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
|
R_RETURN(backend->Write(offset, buffer.data(), size, option));
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFile::Flush(HLERequestContext& ctx) {
|
Result IFile::Flush() {
|
||||||
LOG_DEBUG(Service_FS, "called");
|
LOG_DEBUG(Service_FS, "called");
|
||||||
|
|
||||||
// Exists for SDK compatibiltity -- No need to flush file.
|
R_RETURN(backend->Flush());
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFile::SetSize(HLERequestContext& ctx) {
|
Result IFile::SetSize(s64 size) {
|
||||||
IPC::RequestParser rp{ctx};
|
|
||||||
const u64 size = rp.Pop<u64>();
|
|
||||||
LOG_DEBUG(Service_FS, "called, size={}", size);
|
LOG_DEBUG(Service_FS, "called, size={}", size);
|
||||||
|
|
||||||
backend->Resize(size);
|
R_RETURN(backend->SetSize(size));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFile::GetSize(HLERequestContext& ctx) {
|
Result IFile::GetSize(Out<s64> out_size) {
|
||||||
const u64 size = backend->GetSize();
|
LOG_DEBUG(Service_FS, "called");
|
||||||
LOG_DEBUG(Service_FS, "called, size={}", size);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
R_RETURN(backend->GetSize(out_size));
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push<u64>(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::FileSystem
|
} // namespace Service::FileSystem
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#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/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
@ -10,16 +12,21 @@ namespace Service::FileSystem {
|
|||||||
|
|
||||||
class IFile final : public ServiceFramework<IFile> {
|
class IFile final : public ServiceFramework<IFile> {
|
||||||
public:
|
public:
|
||||||
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_);
|
explicit IFile(Core::System& system_, FileSys::VirtualFile file_);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileSys::VirtualFile backend;
|
std::unique_ptr<FileSys::Fsa::IFile> backend;
|
||||||
|
|
||||||
void Read(HLERequestContext& ctx);
|
Result Read(FileSys::ReadOption option, Out<s64> out_size, s64 offset,
|
||||||
void Write(HLERequestContext& ctx);
|
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
|
||||||
void Flush(HLERequestContext& ctx);
|
out_buffer,
|
||||||
void SetSize(HLERequestContext& ctx);
|
s64 size);
|
||||||
void GetSize(HLERequestContext& ctx);
|
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
|
} // namespace Service::FileSystem
|
||||||
|
@ -2,261 +2,172 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/string_util.h"
|
#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_directory.h"
|
||||||
#include "core/hle/service/filesystem/fsp/fs_i_file.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/filesystem/fsp/fs_i_filesystem.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
|
||||||
|
|
||||||
namespace Service::FileSystem {
|
namespace Service::FileSystem {
|
||||||
|
|
||||||
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
|
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
|
||||||
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
|
: ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
|
||||||
size_)} {
|
dir_)},
|
||||||
|
size_getter{std::move(size_getter_)} {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
{0, D<&IFileSystem::CreateFile>, "CreateFile"},
|
||||||
{1, &IFileSystem::DeleteFile, "DeleteFile"},
|
{1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
|
||||||
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
|
{2, D<&IFileSystem::CreateDirectory>, "CreateDirectory"},
|
||||||
{3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
|
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||||
{4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
|
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||||
{5, &IFileSystem::RenameFile, "RenameFile"},
|
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||||
{6, nullptr, "RenameDirectory"},
|
{6, nullptr, "RenameDirectory"},
|
||||||
{7, &IFileSystem::GetEntryType, "GetEntryType"},
|
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||||
{8, &IFileSystem::OpenFile, "OpenFile"},
|
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||||
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
|
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||||
{10, &IFileSystem::Commit, "Commit"},
|
{10, D<&IFileSystem::Commit>, "Commit"},
|
||||||
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
|
{11, D<&IFileSystem::GetFreeSpaceSize>, "GetFreeSpaceSize"},
|
||||||
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
|
{12, D<&IFileSystem::GetTotalSpaceSize>, "GetTotalSpaceSize"},
|
||||||
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
|
{13, D<&IFileSystem::CleanDirectoryRecursively>, "CleanDirectoryRecursively"},
|
||||||
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
|
{14, D<&IFileSystem::GetFileTimeStampRaw>, "GetFileTimeStampRaw"},
|
||||||
{15, nullptr, "QueryEntry"},
|
{15, nullptr, "QueryEntry"},
|
||||||
{16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
|
{16, D<&IFileSystem::GetFileSystemAttribute>, "GetFileSystemAttribute"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::CreateFile(HLERequestContext& ctx) {
|
Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
IPC::RequestParser rp{ctx};
|
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();
|
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::DeleteFile(HLERequestContext& ctx) {
|
Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
LOG_DEBUG(Service_FS, "called. file={}", path->str);
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
R_RETURN(backend->DeleteFile(FileSys::Path(path->str)));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(backend.DeleteFile(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::CreateDirectory(HLERequestContext& ctx) {
|
Result IFileSystem::CreateDirectory(
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
R_RETURN(backend->CreateDirectory(FileSys::Path(path->str)));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(backend.CreateDirectory(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::DeleteDirectory(HLERequestContext& ctx) {
|
Result IFileSystem::DeleteDirectory(
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
R_RETURN(backend->DeleteDirectory(FileSys::Path(path->str)));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(backend.DeleteDirectory(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) {
|
Result IFileSystem::DeleteDirectoryRecursively(
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
R_RETURN(backend->DeleteDirectoryRecursively(FileSys::Path(path->str)));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(backend.DeleteDirectoryRecursively(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) {
|
Result IFileSystem::CleanDirectoryRecursively(
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
LOG_DEBUG(Service_FS, "called. Directory: {}", path->str);
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. Directory: {}", name);
|
R_RETURN(backend->CleanDirectoryRecursively(FileSys::Path(path->str)));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(backend.CleanDirectoryRecursively(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::RenameFile(HLERequestContext& ctx) {
|
Result IFileSystem::RenameFile(
|
||||||
const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||||
const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
|
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);
|
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(backend.RenameFile(src_name, dst_name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::OpenFile(HLERequestContext& ctx) {
|
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||||
IPC::RequestParser rp{ctx};
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
|
u32 mode) {
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
|
||||||
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);
|
|
||||||
|
|
||||||
FileSys::VirtualFile vfs_file{};
|
FileSys::VirtualFile vfs_file{};
|
||||||
auto result = backend.OpenFile(&vfs_file, name, mode);
|
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
|
||||||
if (result != ResultSuccess) {
|
static_cast<FileSys::OpenMode>(mode)));
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto file = std::make_shared<IFile>(system, vfs_file);
|
*out_interface = std::make_shared<IFile>(system, vfs_file);
|
||||||
|
R_SUCCEED();
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.PushIpcInterface<IFile>(std::move(file));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
|
Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||||
IPC::RequestParser rp{ctx};
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
|
u32 mode) {
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
|
||||||
const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
|
|
||||||
|
|
||||||
FileSys::VirtualDir vfs_dir{};
|
FileSys::VirtualDir vfs_dir{};
|
||||||
auto result = backend.OpenDirectory(&vfs_dir, name);
|
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
|
||||||
if (result != ResultSuccess) {
|
static_cast<FileSys::OpenDirectoryMode>(mode)));
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
|
*out_interface = std::make_shared<IDirectory>(system, vfs_dir,
|
||||||
|
static_cast<FileSys::OpenDirectoryMode>(mode));
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
R_SUCCEED();
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.PushIpcInterface<IDirectory>(std::move(directory));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::GetEntryType(HLERequestContext& ctx) {
|
Result IFileSystem::GetEntryType(
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
Out<u32> out_type, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
LOG_DEBUG(Service_FS, "called. file={}", path->str);
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
|
||||||
|
|
||||||
FileSys::DirectoryEntryType vfs_entry_type{};
|
FileSys::DirectoryEntryType vfs_entry_type{};
|
||||||
auto result = backend.GetEntryType(&vfs_entry_type, name);
|
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
|
||||||
if (result != ResultSuccess) {
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
*out_type = static_cast<u32>(vfs_entry_type);
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.Push<u32>(static_cast<u32>(vfs_entry_type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::Commit(HLERequestContext& ctx) {
|
Result IFileSystem::Commit() {
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
R_SUCCEED();
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
LOG_DEBUG(Service_FS, "called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
*out_size = size_getter.get_free_size();
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.Push(size.get_free_size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
LOG_DEBUG(Service_FS, "called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
*out_size = size_getter.get_total_size();
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.Push(size.get_total_size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) {
|
Result IFileSystem::GetFileTimeStampRaw(
|
||||||
const auto file_buffer = ctx.ReadBuffer();
|
Out<FileSys::FileTimeStampRaw> out_timestamp,
|
||||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
|
||||||
|
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);
|
||||||
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
|
|
||||||
|
|
||||||
FileSys::FileTimeStampRaw vfs_timestamp{};
|
FileSys::FileTimeStampRaw vfs_timestamp{};
|
||||||
auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
|
R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));
|
||||||
if (result != ResultSuccess) {
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 10};
|
*out_timestamp = vfs_timestamp;
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.PushRaw(vfs_timestamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
|
Result IFileSystem::GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute) {
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||||
|
|
||||||
struct FileSystemAttribute {
|
FileSys::FileSystemAttribute savedata_attribute{};
|
||||||
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{};
|
|
||||||
savedata_attribute.dir_entry_name_length_max_defined = true;
|
savedata_attribute.dir_entry_name_length_max_defined = true;
|
||||||
savedata_attribute.file_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.dir_entry_name_length_max = 0x40;
|
||||||
savedata_attribute.file_entry_name_length_max = 0x40;
|
savedata_attribute.file_entry_name_length_max = 0x40;
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 50};
|
*out_attribute = savedata_attribute;
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.PushRaw(savedata_attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::FileSystem
|
} // namespace Service::FileSystem
|
||||||
|
@ -3,36 +3,58 @@
|
|||||||
|
|
||||||
#pragma once
|
#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/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/hle/service/cmif_types.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.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"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace FileSys::Sf {
|
||||||
|
struct Path;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Service::FileSystem {
|
namespace Service::FileSystem {
|
||||||
|
|
||||||
|
class IFile;
|
||||||
|
class IDirectory;
|
||||||
|
|
||||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||||
public:
|
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);
|
Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
|
||||||
void DeleteFile(HLERequestContext& ctx);
|
s64 size);
|
||||||
void CreateDirectory(HLERequestContext& ctx);
|
Result DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
void DeleteDirectory(HLERequestContext& ctx);
|
Result CreateDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
void DeleteDirectoryRecursively(HLERequestContext& ctx);
|
Result DeleteDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
void CleanDirectoryRecursively(HLERequestContext& ctx);
|
Result DeleteDirectoryRecursively(
|
||||||
void RenameFile(HLERequestContext& ctx);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
void OpenFile(HLERequestContext& ctx);
|
Result CleanDirectoryRecursively(
|
||||||
void OpenDirectory(HLERequestContext& ctx);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
void GetEntryType(HLERequestContext& ctx);
|
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||||
void Commit(HLERequestContext& ctx);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||||
void GetFreeSpaceSize(HLERequestContext& ctx);
|
Result OpenFile(OutInterface<IFile> out_interface,
|
||||||
void GetTotalSpaceSize(HLERequestContext& ctx);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||||
void GetFileTimeStampRaw(HLERequestContext& ctx);
|
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||||
void GetFileSystemAttribute(HLERequestContext& ctx);
|
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:
|
private:
|
||||||
VfsDirectoryServiceWrapper backend;
|
std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
|
||||||
SizeGetter size;
|
SizeGetter size_getter;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::FileSystem
|
} // 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