Compare commits
3 commits
feature/da
...
master
Author | SHA1 | Date | |
---|---|---|---|
a3ef2cc183 | |||
1e1b8ad33f | |||
91493fa39b |
45
.reuse/dep5
|
@ -13,13 +13,12 @@ Copyright: yuzu Emulator Project
|
||||||
License: GPL-2.0-or-later
|
License: GPL-2.0-or-later
|
||||||
|
|
||||||
Files: dist/qt_themes/default/icons/256x256/eden.png
|
Files: dist/qt_themes/default/icons/256x256/eden.png
|
||||||
dist/qt_themes/default/icons/256x256/eden_named.png
|
|
||||||
dist/yuzu.bmp
|
dist/yuzu.bmp
|
||||||
dist/eden.icns
|
dist/yuzu.icns
|
||||||
dist/eden.ico
|
dist/eden.ico
|
||||||
dist/dev.eden_emu.eden.svg
|
dist/eden.svg
|
||||||
Copyright: 2025 Eden Emulator Project
|
Copyright: yuzu Emulator Project
|
||||||
License: GPL-3.0-or-later
|
License: GPL-2.0-or-later
|
||||||
|
|
||||||
Files: dist/qt_themes/qdarkstyle*/LICENSE.*
|
Files: dist/qt_themes/qdarkstyle*/LICENSE.*
|
||||||
dist/qt_themes/qdarkstyle*/style.qrc
|
dist/qt_themes/qdarkstyle*/style.qrc
|
||||||
|
@ -156,39 +155,3 @@ License: BSD-3-Clause
|
||||||
Files: src/android/app/debug.keystore
|
Files: src/android/app/debug.keystore
|
||||||
Copyright: 2023 yuzu Emulator Project
|
Copyright: 2023 yuzu Emulator Project
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
|
|
||||||
Files: dist/qt_themes/colorful/icons/48x48/user-trash.png
|
|
||||||
dist/qt_themes/colorful/icons/48x48/upload.png
|
|
||||||
dist/qt_themes/colorful/icons/48x48/download.png
|
|
||||||
Copyright: 2014 Uri Herrera
|
|
||||||
1996-2025 KDE Software Foundation
|
|
||||||
License: LGPL-2.0-or-later
|
|
||||||
|
|
||||||
Files: dist/qt_themes/default/icons/48x48/user-trash.png
|
|
||||||
dist/qt_themes/default/icons/48x48/upload.png
|
|
||||||
dist/qt_themes/default/icons/48x48/download.png
|
|
||||||
dist/qt_themes/default_dark/icons/48x48/user-trash.png
|
|
||||||
dist/qt_themes/default_dark/icons/48x48/upload.png
|
|
||||||
dist/qt_themes/default_dark/icons/48x48/download.png
|
|
||||||
Copyright: 2025 Fonticons, Inc.
|
|
||||||
License: CC-BY-4.0
|
|
||||||
Comment: All of these icons have been modified by crueter <crueter@crueter.xyz>
|
|
||||||
|
|
||||||
Files: CMakeModules/CPM.cmake
|
|
||||||
Copyright: 2019-2023 Lars Melchior
|
|
||||||
License: MIT
|
|
||||||
|
|
||||||
Files: CMakeModules/CPMUtil.cmake
|
|
||||||
CMakeModules/CPM.cmake
|
|
||||||
CMakeModules/GetSCMRev.cmake
|
|
||||||
CMakeModules/DetectArchitecture.cmake
|
|
||||||
tools/cpm/*
|
|
||||||
tools/update-cpm.sh
|
|
||||||
tools/shellcheck.sh
|
|
||||||
docs/CPMUtil.md
|
|
||||||
**cpmfile.json
|
|
||||||
Copyright: 2025 crueter <crueter@crueter.xyz>
|
|
||||||
License: GPL-3.0-or-later
|
|
||||||
Comment: CPM.cmake has had additional modifications from crueter to better work with CPMUtil
|
|
||||||
https://git.crueter.xyz/CMake/CPMUtil
|
|
||||||
https://git.crueter.xyz/CMake/Modules
|
|
||||||
|
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/download.png
vendored
Before Width: | Height: | Size: 1.4 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/upload.png
vendored
Before Width: | Height: | Size: 1.4 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/user-trash.png
vendored
Before Width: | Height: | Size: 1.4 KiB |
3
dist/qt_themes/colorful/style.qrc
vendored
|
@ -18,9 +18,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||||
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
|
|
||||||
<file alias="48x48/download.png">icons/48x48/download.png</file>
|
|
||||||
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
|
|
||||||
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||||
|
|
|
@ -11,9 +11,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
||||||
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
|
|
||||||
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
|
|
||||||
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
|
|
||||||
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
||||||
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
|
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
|
||||||
|
|
3
dist/qt_themes/default/default.qrc
vendored
|
@ -14,9 +14,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||||
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
|
|
||||||
<file alias="48x48/download.png">icons/48x48/download.png</file>
|
|
||||||
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
|
|
||||||
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||||
<file alias="48x48/star.png">icons/48x48/star.png</file>
|
<file alias="48x48/star.png">icons/48x48/star.png</file>
|
||||||
|
|
BIN
dist/qt_themes/default/icons/256x256/eden.png
vendored
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
dist/qt_themes/default/icons/48x48/download.png
vendored
Before Width: | Height: | Size: 853 B |
BIN
dist/qt_themes/default/icons/48x48/upload.png
vendored
Before Width: | Height: | Size: 820 B |
BIN
dist/qt_themes/default/icons/48x48/user-trash.png
vendored
Before Width: | Height: | Size: 584 B |
3
dist/qt_themes/default_dark/style.qrc
vendored
|
@ -13,9 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
||||||
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
|
|
||||||
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
|
|
||||||
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
|
|
||||||
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
||||||
|
|
BIN
dist/qt_themes/qdarkstyle/icons/48x48/download.png
vendored
Before Width: | Height: | Size: 883 B |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/upload.png
vendored
Before Width: | Height: | Size: 853 B |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/user-trash.png
vendored
Before Width: | Height: | Size: 584 B |
3
dist/qt_themes/qdarkstyle/style.qrc
vendored
|
@ -9,9 +9,6 @@
|
||||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||||
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
|
|
||||||
<file alias="48x48/download.png">icons/48x48/download.png</file>
|
|
||||||
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
|
|
||||||
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
<file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
|
||||||
<file alias="48x48/user-trash.png">../qdarkstyle/icons/48x48/user-trash.png</file>
|
|
||||||
<file alias="48x48/download.png">../qdarkstyle/icons/48x48/download.png</file>
|
|
||||||
<file alias="48x48/upload.png">../qdarkstyle/icons/48x48/upload.png</file>
|
|
||||||
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>
|
||||||
|
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 438 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
@ -16,7 +16,7 @@
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
constexpr u32 CurrentRevision = 16;
|
constexpr u32 CurrentRevision = 15;
|
||||||
|
|
||||||
enum class SupportTags {
|
enum class SupportTags {
|
||||||
CommandProcessingTimeEstimatorVersion4,
|
CommandProcessingTimeEstimatorVersion4,
|
||||||
|
@ -47,6 +47,10 @@ enum class SupportTags {
|
||||||
DelayChannelMappingChange,
|
DelayChannelMappingChange,
|
||||||
ReverbChannelMappingChange,
|
ReverbChannelMappingChange,
|
||||||
I3dl2ReverbChannelMappingChange,
|
I3dl2ReverbChannelMappingChange,
|
||||||
|
SplitterPrevVolumeReset,
|
||||||
|
SplitterBiquadFilterParameter,
|
||||||
|
SplitterDestinationV2b,
|
||||||
|
VoiceInParameterV2,
|
||||||
|
|
||||||
// Not a real tag, just here to get the count.
|
// Not a real tag, just here to get the count.
|
||||||
Size
|
Size
|
||||||
|
@ -91,6 +95,10 @@ constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) {
|
||||||
{SupportTags::DelayChannelMappingChange, 11},
|
{SupportTags::DelayChannelMappingChange, 11},
|
||||||
{SupportTags::ReverbChannelMappingChange, 11},
|
{SupportTags::ReverbChannelMappingChange, 11},
|
||||||
{SupportTags::I3dl2ReverbChannelMappingChange, 11},
|
{SupportTags::I3dl2ReverbChannelMappingChange, 11},
|
||||||
|
{SupportTags::SplitterBiquadFilterParameter, 12},
|
||||||
|
{SupportTags::SplitterPrevVolumeReset, 13},
|
||||||
|
{SupportTags::SplitterDestinationV2b, 15},
|
||||||
|
{SupportTags::VoiceInParameterV2, 15},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const auto& feature =
|
const auto& feature =
|
||||||
|
|
|
@ -193,4 +193,20 @@ bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const {
|
||||||
return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision);
|
return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BehaviorInfo::IsSplitterPrevVolumeResetSupported() const {
|
||||||
|
return CheckFeatureSupported(SupportTags::SplitterPrevVolumeReset, user_revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BehaviorInfo::IsSplitterDestinationV2bSupported() const {
|
||||||
|
return CheckFeatureSupported(SupportTags::SplitterDestinationV2b, user_revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BehaviorInfo::IsVoiceInParameterV2Supported() const {
|
||||||
|
return CheckFeatureSupported(SupportTags::VoiceInParameterV2, user_revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BehaviorInfo::IsBiquadFilterParameterForSplitterEnabled() const {
|
||||||
|
return CheckFeatureSupported(SupportTags::SplitterBiquadFilterParameter, user_revision);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -361,6 +364,38 @@ public:
|
||||||
*/
|
*/
|
||||||
bool IsI3dl2ReverbChannelMappingChanged() const;
|
bool IsI3dl2ReverbChannelMappingChanged() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if explicit previous mix volume reset is supported for splitters.
|
||||||
|
* This allows splitters to explicitly reset their previous mix volumes instead of
|
||||||
|
* doing so implicitly on first use.
|
||||||
|
*
|
||||||
|
* @return True if supported, otherwise false.
|
||||||
|
*/
|
||||||
|
bool IsSplitterPrevVolumeResetSupported() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if splitter destination v2b parameter format is supported (revision 15+).
|
||||||
|
* This uses the extended parameter format with biquad filter fields.
|
||||||
|
*
|
||||||
|
* @return True if supported, otherwise false.
|
||||||
|
*/
|
||||||
|
bool IsSplitterDestinationV2bSupported() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if voice input parameter v2 format is supported (revision 15+).
|
||||||
|
* This uses the extended parameter format with float biquad filters.
|
||||||
|
*
|
||||||
|
* @return True if supported, otherwise false.
|
||||||
|
*/
|
||||||
|
bool IsVoiceInParameterV2Supported() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if splitter destinations can carry biquad filter parameters (revision 12+).
|
||||||
|
*
|
||||||
|
* @return True if supported, otherwise false.
|
||||||
|
*/
|
||||||
|
bool IsBiquadFilterParameterForSplitterEnabled() const;
|
||||||
|
|
||||||
/// Host version
|
/// Host version
|
||||||
u32 process_revision;
|
u32 process_revision;
|
||||||
/// User version
|
/// User version
|
||||||
|
|
|
@ -64,8 +64,6 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
const PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
const PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
||||||
behaviour.IsMemoryForceMappingEnabled());
|
behaviour.IsMemoryForceMappingEnabled());
|
||||||
const auto voice_count{voice_context.GetCount()};
|
const auto voice_count{voice_context.GetCount()};
|
||||||
std::span<const VoiceInfo::InParameter> in_params{
|
|
||||||
reinterpret_cast<const VoiceInfo::InParameter*>(input), voice_count};
|
|
||||||
std::span<VoiceInfo::OutStatus> out_params{reinterpret_cast<VoiceInfo::OutStatus*>(output),
|
std::span<VoiceInfo::OutStatus> out_params{reinterpret_cast<VoiceInfo::OutStatus*>(output),
|
||||||
voice_count};
|
voice_count};
|
||||||
|
|
||||||
|
@ -76,8 +74,104 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
|
|
||||||
u32 new_voice_count{0};
|
u32 new_voice_count{0};
|
||||||
|
|
||||||
|
// Two input formats exist: legacy (0x170) and v2 with float biquad (0x188).
|
||||||
|
const bool use_v2 = behaviour.IsVoiceInParameterV2Supported();
|
||||||
|
const u32 in_stride = use_v2 ? 0x188u : static_cast<u32>(sizeof(VoiceInfo::InParameter));
|
||||||
|
|
||||||
for (u32 i = 0; i < voice_count; i++) {
|
for (u32 i = 0; i < voice_count; i++) {
|
||||||
const auto& in_param{in_params[i]};
|
VoiceInfo::InParameter local_in{};
|
||||||
|
std::array<VoiceInfo::BiquadFilterParameter2, MaxBiquadFilters> float_biquads{};
|
||||||
|
|
||||||
|
if (!use_v2) {
|
||||||
|
const auto* in_param_ptr = reinterpret_cast<const VoiceInfo::InParameter*>(
|
||||||
|
input + i * sizeof(VoiceInfo::InParameter));
|
||||||
|
local_in = *in_param_ptr;
|
||||||
|
} else {
|
||||||
|
struct VoiceInParameterV2 {
|
||||||
|
u32 id;
|
||||||
|
u32 node_id;
|
||||||
|
bool is_new;
|
||||||
|
bool in_use;
|
||||||
|
PlayState play_state;
|
||||||
|
SampleFormat sample_format;
|
||||||
|
u32 sample_rate;
|
||||||
|
u32 priority;
|
||||||
|
u32 sort_order;
|
||||||
|
u32 channel_count;
|
||||||
|
f32 pitch;
|
||||||
|
f32 volume;
|
||||||
|
// Two BiquadFilterParameter2 (0x18 each) -> ignored/converted
|
||||||
|
struct BiquadV2 {
|
||||||
|
bool enable;
|
||||||
|
u8 r1;
|
||||||
|
u8 r2;
|
||||||
|
u8 r3;
|
||||||
|
std::array<f32, 3> b;
|
||||||
|
std::array<f32, 2> a;
|
||||||
|
} biquads[2];
|
||||||
|
u32 wave_buffer_count;
|
||||||
|
u32 wave_buffer_index;
|
||||||
|
u32 reserved1;
|
||||||
|
u64 src_data_address;
|
||||||
|
u64 src_data_size;
|
||||||
|
s32 mix_id;
|
||||||
|
u32 splitter_id;
|
||||||
|
std::array<VoiceInfo::WaveBufferInternal, MaxWaveBuffers> wavebuffers;
|
||||||
|
std::array<u32, MaxChannels> channel_resource_ids;
|
||||||
|
bool clear_voice_drop;
|
||||||
|
u8 flush_wave_buffer_count;
|
||||||
|
u16 reserved2;
|
||||||
|
VoiceInfo::Flags flags;
|
||||||
|
SrcQuality src_quality;
|
||||||
|
u32 external_ctx;
|
||||||
|
u32 external_ctx_size;
|
||||||
|
u32 reserved3[2];
|
||||||
|
};
|
||||||
|
const auto* vin = reinterpret_cast<const VoiceInParameterV2*>(input + i * in_stride);
|
||||||
|
local_in.id = vin->id;
|
||||||
|
local_in.node_id = vin->node_id;
|
||||||
|
local_in.is_new = vin->is_new;
|
||||||
|
local_in.in_use = vin->in_use;
|
||||||
|
local_in.play_state = vin->play_state;
|
||||||
|
local_in.sample_format = vin->sample_format;
|
||||||
|
local_in.sample_rate = vin->sample_rate;
|
||||||
|
local_in.priority = static_cast<s32>(vin->priority);
|
||||||
|
local_in.sort_order = static_cast<s32>(vin->sort_order);
|
||||||
|
local_in.channel_count = vin->channel_count;
|
||||||
|
local_in.pitch = vin->pitch;
|
||||||
|
local_in.volume = vin->volume;
|
||||||
|
|
||||||
|
// For REV15+, we keep float coefficients separate and only convert for compatibility
|
||||||
|
for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
|
||||||
|
const auto& src = vin->biquads[filter_idx];
|
||||||
|
auto& dst = local_in.biquads[filter_idx];
|
||||||
|
dst.enabled = src.enable;
|
||||||
|
// Convert float coefficients to fixed-point Q2.14 for legacy path
|
||||||
|
dst.b[0] = static_cast<s16>(std::clamp(src.b[0] * 16384.0f, -32768.0f, 32767.0f));
|
||||||
|
dst.b[1] = static_cast<s16>(std::clamp(src.b[1] * 16384.0f, -32768.0f, 32767.0f));
|
||||||
|
dst.b[2] = static_cast<s16>(std::clamp(src.b[2] * 16384.0f, -32768.0f, 32767.0f));
|
||||||
|
dst.a[0] = static_cast<s16>(std::clamp(src.a[0] * 16384.0f, -32768.0f, 32767.0f));
|
||||||
|
dst.a[1] = static_cast<s16>(std::clamp(src.a[1] * 16384.0f, -32768.0f, 32767.0f));
|
||||||
|
|
||||||
|
// Also store the native float version
|
||||||
|
float_biquads[filter_idx].enabled = src.enable;
|
||||||
|
float_biquads[filter_idx].numerator = src.b;
|
||||||
|
float_biquads[filter_idx].denominator = src.a;
|
||||||
|
}
|
||||||
|
local_in.wave_buffer_count = vin->wave_buffer_count;
|
||||||
|
local_in.wave_buffer_index = static_cast<u16>(vin->wave_buffer_index);
|
||||||
|
local_in.src_data_address = static_cast<CpuAddr>(vin->src_data_address);
|
||||||
|
local_in.src_data_size = vin->src_data_size;
|
||||||
|
local_in.mix_id = static_cast<u32>(vin->mix_id);
|
||||||
|
local_in.splitter_id = vin->splitter_id;
|
||||||
|
local_in.wave_buffer_internal = vin->wavebuffers;
|
||||||
|
local_in.channel_resource_ids = vin->channel_resource_ids;
|
||||||
|
local_in.clear_voice_drop = vin->clear_voice_drop;
|
||||||
|
local_in.flush_buffer_count = vin->flush_wave_buffer_count;
|
||||||
|
local_in.flags = vin->flags;
|
||||||
|
local_in.src_quality = vin->src_quality;
|
||||||
|
}
|
||||||
|
const auto& in_param = local_in;
|
||||||
std::array<VoiceState*, MaxChannels> voice_states{};
|
std::array<VoiceState*, MaxChannels> voice_states{};
|
||||||
|
|
||||||
if (!in_param.in_use) {
|
if (!in_param.in_use) {
|
||||||
|
@ -101,6 +195,14 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
BehaviorInfo::ErrorInfo update_error{};
|
BehaviorInfo::ErrorInfo update_error{};
|
||||||
voice_info.UpdateParameters(update_error, in_param, pool_mapper, behaviour);
|
voice_info.UpdateParameters(update_error, in_param, pool_mapper, behaviour);
|
||||||
|
|
||||||
|
// For REV15+, store the native float biquad coefficients
|
||||||
|
if (use_v2) {
|
||||||
|
voice_info.use_float_biquads = true;
|
||||||
|
voice_info.biquads_float = float_biquads;
|
||||||
|
} else {
|
||||||
|
voice_info.use_float_biquads = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!update_error.error_code.IsSuccess()) {
|
if (!update_error.error_code.IsSuccess()) {
|
||||||
behaviour.AppendError(update_error);
|
behaviour.AppendError(update_error);
|
||||||
}
|
}
|
||||||
|
@ -121,7 +223,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
new_voice_count += in_param.channel_count;
|
new_voice_count += in_param.channel_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto consumed_input_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::InParameter))};
|
auto consumed_input_size{voice_count * in_stride};
|
||||||
auto consumed_output_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::OutStatus))};
|
auto consumed_output_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::OutStatus))};
|
||||||
if (consumed_input_size != in_header->voices_size) {
|
if (consumed_input_size != in_header->voices_size) {
|
||||||
LOG_ERROR(Service_Audio, "Consumed an incorrect voices size, header size={}, consumed={}",
|
LOG_ERROR(Service_Audio, "Consumed an incorrect voices size, header size={}, consumed={}",
|
||||||
|
@ -257,18 +359,31 @@ Result InfoUpdater::UpdateMixes(MixContext& mix_context, const u32 mix_buffer_co
|
||||||
EffectContext& effect_context, SplitterContext& splitter_context) {
|
EffectContext& effect_context, SplitterContext& splitter_context) {
|
||||||
s32 mix_count{0};
|
s32 mix_count{0};
|
||||||
u32 consumed_input_size{0};
|
u32 consumed_input_size{0};
|
||||||
|
u32 input_mix_size{0};
|
||||||
|
|
||||||
if (behaviour.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
if (behaviour.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
||||||
auto in_dirty_params{reinterpret_cast<const MixInfo::InDirtyParameter*>(input)};
|
auto in_dirty_params{reinterpret_cast<const MixInfo::InDirtyParameter*>(input)};
|
||||||
mix_count = in_dirty_params->count;
|
mix_count = in_dirty_params->count;
|
||||||
|
|
||||||
|
// Validate against expected header size to ensure structure is correct
|
||||||
|
if (mix_count < 0 || mix_count > 0x100) {
|
||||||
|
LOG_ERROR(
|
||||||
|
Service_Audio,
|
||||||
|
"Invalid mix count from dirty parameter: count={}, magic=0x{:X}, expected_size={}",
|
||||||
|
mix_count, in_dirty_params->magic, in_header->mix_size);
|
||||||
|
return Service::Audio::ResultInvalidUpdateInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumed_input_size += static_cast<u32>(sizeof(MixInfo::InDirtyParameter));
|
||||||
input += sizeof(MixInfo::InDirtyParameter);
|
input += sizeof(MixInfo::InDirtyParameter);
|
||||||
consumed_input_size = static_cast<u32>(sizeof(MixInfo::InDirtyParameter) +
|
|
||||||
mix_count * sizeof(MixInfo::InParameter));
|
|
||||||
} else {
|
} else {
|
||||||
mix_count = mix_context.GetCount();
|
mix_count = mix_context.GetCount();
|
||||||
consumed_input_size = static_cast<u32>(mix_count * sizeof(MixInfo::InParameter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_mix_size = static_cast<u32>(mix_count * sizeof(MixInfo::InParameter));
|
||||||
|
consumed_input_size += input_mix_size;
|
||||||
|
|
||||||
|
|
||||||
if (mix_buffer_count == 0) {
|
if (mix_buffer_count == 0) {
|
||||||
return Service::Audio::ResultInvalidUpdateInfo;
|
return Service::Audio::ResultInvalidUpdateInfo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,6 +237,13 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, VoiceInfo& vo
|
||||||
|
|
||||||
cmd.biquad = voice_info.biquads[biquad_index];
|
cmd.biquad = voice_info.biquads[biquad_index];
|
||||||
|
|
||||||
|
if (voice_info.use_float_biquads) {
|
||||||
|
cmd.biquad_float = voice_info.biquads_float[biquad_index];
|
||||||
|
cmd.use_float_coefficients = true;
|
||||||
|
} else {
|
||||||
|
cmd.use_float_coefficients = false;
|
||||||
|
}
|
||||||
|
|
||||||
cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()),
|
cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()),
|
||||||
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
||||||
|
|
||||||
|
@ -263,6 +270,9 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
|
||||||
cmd.biquad.b = parameter.b;
|
cmd.biquad.b = parameter.b;
|
||||||
cmd.biquad.a = parameter.a;
|
cmd.biquad.a = parameter.a;
|
||||||
|
|
||||||
|
// Effects use legacy fixed-point format
|
||||||
|
cmd.use_float_coefficients = false;
|
||||||
|
|
||||||
cmd.state = memory_pool->Translate(CpuAddr(state),
|
cmd.state = memory_pool->Translate(CpuAddr(state),
|
||||||
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
||||||
|
|
||||||
|
@ -658,6 +668,13 @@ void CommandBuffer::GenerateMultitapBiquadFilterCommand(const s32 node_id, Voice
|
||||||
cmd.output = buffer_count + channel;
|
cmd.output = buffer_count + channel;
|
||||||
cmd.biquads = voice_info.biquads;
|
cmd.biquads = voice_info.biquads;
|
||||||
|
|
||||||
|
if (voice_info.use_float_biquads) {
|
||||||
|
cmd.biquads_float = voice_info.biquads_float;
|
||||||
|
cmd.use_float_coefficients = true;
|
||||||
|
} else {
|
||||||
|
cmd.use_float_coefficients = false;
|
||||||
|
}
|
||||||
|
|
||||||
cmd.states[0] =
|
cmd.states[0] =
|
||||||
memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()),
|
memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()),
|
||||||
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
||||||
|
|
|
@ -51,6 +51,40 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
|
||||||
state.s3 = Common::BitCast<s64>(s[3]);
|
state.s3 = Common::BitCast<s64>(s[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Biquad filter float implementation with native float coefficients.
|
||||||
|
*/
|
||||||
|
void ApplyBiquadFilterFloat2(std::span<s32> output, std::span<const s32> input,
|
||||||
|
std::array<f32, 3>& b, std::array<f32, 2>& a,
|
||||||
|
VoiceState::BiquadFilterState& state, const u32 sample_count) {
|
||||||
|
constexpr f64 min{std::numeric_limits<s32>::min()};
|
||||||
|
constexpr f64 max{std::numeric_limits<s32>::max()};
|
||||||
|
|
||||||
|
std::array<f64, 3> b_double{static_cast<f64>(b[0]), static_cast<f64>(b[1]),
|
||||||
|
static_cast<f64>(b[2])};
|
||||||
|
std::array<f64, 2> a_double{static_cast<f64>(a[0]), static_cast<f64>(a[1])};
|
||||||
|
std::array<f64, 4> s{Common::BitCast<f64>(state.s0), Common::BitCast<f64>(state.s1),
|
||||||
|
Common::BitCast<f64>(state.s2), Common::BitCast<f64>(state.s3)};
|
||||||
|
|
||||||
|
for (u32 i = 0; i < sample_count; i++) {
|
||||||
|
f64 in_sample{static_cast<f64>(input[i])};
|
||||||
|
auto sample{in_sample * b_double[0] + s[0] * b_double[1] + s[1] * b_double[2] +
|
||||||
|
s[2] * a_double[0] + s[3] * a_double[1]};
|
||||||
|
|
||||||
|
output[i] = static_cast<s32>(std::clamp(sample, min, max));
|
||||||
|
|
||||||
|
s[1] = s[0];
|
||||||
|
s[0] = in_sample;
|
||||||
|
s[3] = s[2];
|
||||||
|
s[2] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.s0 = Common::BitCast<s64>(s[0]);
|
||||||
|
state.s1 = Common::BitCast<s64>(s[1]);
|
||||||
|
state.s2 = Common::BitCast<s64>(s[2]);
|
||||||
|
state.s3 = Common::BitCast<s64>(s[3]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Biquad filter s32 implementation.
|
* Biquad filter s32 implementation.
|
||||||
*
|
*
|
||||||
|
@ -98,8 +132,14 @@ void BiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& pro
|
||||||
processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
|
processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
|
||||||
|
|
||||||
if (use_float_processing) {
|
if (use_float_processing) {
|
||||||
|
// REV15+: Use native float coefficients if available
|
||||||
|
if (use_float_coefficients) {
|
||||||
|
ApplyBiquadFilterFloat2(output_buffer, input_buffer, biquad_float.numerator,
|
||||||
|
biquad_float.denominator, *state_, processor.sample_count);
|
||||||
|
} else {
|
||||||
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
|
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
|
||||||
processor.sample_count);
|
processor.sample_count);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ApplyBiquadFilterInt(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
|
ApplyBiquadFilterInt(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
|
||||||
processor.sample_count);
|
processor.sample_count);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -50,12 +53,16 @@ struct BiquadFilterCommand : ICommand {
|
||||||
s16 output;
|
s16 output;
|
||||||
/// Input parameters for biquad
|
/// Input parameters for biquad
|
||||||
VoiceInfo::BiquadFilterParameter biquad;
|
VoiceInfo::BiquadFilterParameter biquad;
|
||||||
|
/// Input parameters for biquad (REV15+ native float)
|
||||||
|
VoiceInfo::BiquadFilterParameter2 biquad_float;
|
||||||
/// Biquad state, updated each call
|
/// Biquad state, updated each call
|
||||||
CpuAddr state;
|
CpuAddr state;
|
||||||
/// If true, reset the state
|
/// If true, reset the state
|
||||||
bool needs_init;
|
bool needs_init;
|
||||||
/// If true, use float processing rather than int
|
/// If true, use float processing rather than int
|
||||||
bool use_float_processing;
|
bool use_float_processing;
|
||||||
|
/// If true, use native float coefficients (REV15+)
|
||||||
|
bool use_float_coefficients;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,4 +79,18 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
|
||||||
std::array<s16, 3>& b, std::array<s16, 2>& a,
|
std::array<s16, 3>& b, std::array<s16, 2>& a,
|
||||||
VoiceState::BiquadFilterState& state, const u32 sample_count);
|
VoiceState::BiquadFilterState& state, const u32 sample_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Biquad filter float implementation with native float coefficients (SDK REV15+).
|
||||||
|
*
|
||||||
|
* @param output - Output container for filtered samples.
|
||||||
|
* @param input - Input container for samples to be filtered.
|
||||||
|
* @param b - Feedforward coefficients (float).
|
||||||
|
* @param a - Feedback coefficients (float).
|
||||||
|
* @param state - State to track previous samples.
|
||||||
|
* @param sample_count - Number of samples to process.
|
||||||
|
*/
|
||||||
|
void ApplyBiquadFilterFloat2(std::span<s32> output, std::span<const s32> input,
|
||||||
|
std::array<f32, 3>& b, std::array<f32, 2>& a,
|
||||||
|
VoiceState::BiquadFilterState& state, const u32 sample_count);
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -33,9 +36,15 @@ void MultiTapBiquadFilterCommand::Process(const AudioRenderer::CommandListProces
|
||||||
*state = {};
|
*state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REV15+: Use native float coefficients if available
|
||||||
|
if (use_float_coefficients) {
|
||||||
|
ApplyBiquadFilterFloat2(output_buffer, input_buffer, biquads_float[i].numerator,
|
||||||
|
biquads_float[i].denominator, *state, processor.sample_count);
|
||||||
|
} else {
|
||||||
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
|
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
|
||||||
processor.sample_count);
|
processor.sample_count);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
|
bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -49,12 +52,16 @@ struct MultiTapBiquadFilterCommand : ICommand {
|
||||||
s16 output;
|
s16 output;
|
||||||
/// Biquad parameters
|
/// Biquad parameters
|
||||||
std::array<VoiceInfo::BiquadFilterParameter, MaxBiquadFilters> biquads;
|
std::array<VoiceInfo::BiquadFilterParameter, MaxBiquadFilters> biquads;
|
||||||
|
/// Biquad parameters (REV15+ native float)
|
||||||
|
std::array<VoiceInfo::BiquadFilterParameter2, MaxBiquadFilters> biquads_float;
|
||||||
/// Biquad states, updated each call
|
/// Biquad states, updated each call
|
||||||
std::array<CpuAddr, MaxBiquadFilters> states;
|
std::array<CpuAddr, MaxBiquadFilters> states;
|
||||||
/// If each biquad needs initialisation
|
/// If each biquad needs initialisation
|
||||||
std::array<bool, MaxBiquadFilters> needs_init;
|
std::array<bool, MaxBiquadFilters> needs_init;
|
||||||
/// Number of active biquads
|
/// Number of active biquads
|
||||||
u8 filter_tap_count;
|
u8 filter_tap_count;
|
||||||
|
/// If true, use native float coefficients (REV15+)
|
||||||
|
bool use_float_coefficients;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|
|
@ -35,12 +35,16 @@ SplitterDestinationData& SplitterContext::GetData(const u32 index) {
|
||||||
|
|
||||||
void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_,
|
void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_,
|
||||||
SplitterDestinationData* splitter_destinations_,
|
SplitterDestinationData* splitter_destinations_,
|
||||||
const u32 destination_count_, const bool splitter_bug_fixed_) {
|
const u32 destination_count_, const bool splitter_bug_fixed_,
|
||||||
|
const BehaviorInfo& behavior) {
|
||||||
splitter_infos = splitter_infos_;
|
splitter_infos = splitter_infos_;
|
||||||
info_count = splitter_info_count_;
|
info_count = splitter_info_count_;
|
||||||
splitter_destinations = splitter_destinations_;
|
splitter_destinations = splitter_destinations_;
|
||||||
destinations_count = destination_count_;
|
destinations_count = destination_count_;
|
||||||
splitter_bug_fixed = splitter_bug_fixed_;
|
splitter_bug_fixed = splitter_bug_fixed_;
|
||||||
|
splitter_prev_volume_reset_supported = behavior.IsSplitterPrevVolumeResetSupported();
|
||||||
|
splitter_biquad_param_supported = behavior.IsBiquadFilterParameterForSplitterEnabled();
|
||||||
|
splitter_float_coeff_supported = behavior.IsSplitterDestinationV2bSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SplitterContext::UsingSplitter() const {
|
bool SplitterContext::UsingSplitter() const {
|
||||||
|
@ -84,7 +88,7 @@ bool SplitterContext::Initialize(const BehaviorInfo& behavior,
|
||||||
}
|
}
|
||||||
|
|
||||||
Setup(splitter_infos, params.splitter_infos, splitter_destinations,
|
Setup(splitter_infos, params.splitter_infos, splitter_destinations,
|
||||||
params.splitter_destinations, behavior.IsSplitterBugFixed());
|
params.splitter_destinations, behavior.IsSplitterBugFixed(), behavior);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -137,19 +141,104 @@ u32 SplitterContext::UpdateInfo(const u8* input, u32 offset, const u32 splitter_
|
||||||
|
|
||||||
u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) {
|
u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) {
|
||||||
for (u32 i = 0; i < count; i++) {
|
for (u32 i = 0; i < count; i++) {
|
||||||
auto data_header{
|
// Version selection based on feature flags:
|
||||||
reinterpret_cast<const SplitterDestinationData::InParameter*>(input + offset)};
|
// - REV12: integer biquad params (Version2a)
|
||||||
|
// - REV15: float coeff/biquad v2b
|
||||||
|
// - older: no biquad fields
|
||||||
|
if (!splitter_biquad_param_supported) {
|
||||||
|
const auto* data_header =
|
||||||
|
reinterpret_cast<const SplitterDestinationData::InParameter*>(input + offset);
|
||||||
|
|
||||||
if (data_header->magic != GetSplitterSendDataMagic()) {
|
if (data_header->magic != GetSplitterSendDataMagic()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_header->id < 0 || data_header->id > destinations_count) {
|
if (data_header->id < 0 || data_header->id > destinations_count) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
splitter_destinations[data_header->id].Update(*data_header);
|
auto modified_params = *data_header;
|
||||||
|
if (!splitter_prev_volume_reset_supported) {
|
||||||
|
modified_params.reset_prev_volume = false;
|
||||||
|
}
|
||||||
|
splitter_destinations[data_header->id].Update(modified_params);
|
||||||
offset += sizeof(SplitterDestinationData::InParameter);
|
offset += sizeof(SplitterDestinationData::InParameter);
|
||||||
|
} else if (!splitter_float_coeff_supported) {
|
||||||
|
// Version 2a: struct contains legacy fixed-point biquad filter fields (REV12+)
|
||||||
|
const auto* data_header_v2a =
|
||||||
|
reinterpret_cast<const SplitterDestinationData::InParameterVersion2a*>(input +
|
||||||
|
offset);
|
||||||
|
|
||||||
|
if (data_header_v2a->magic != GetSplitterSendDataMagic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (data_header_v2a->id < 0 || data_header_v2a->id > destinations_count) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map common fields to the base format
|
||||||
|
SplitterDestinationData::InParameter mapped{};
|
||||||
|
mapped.magic = data_header_v2a->magic;
|
||||||
|
mapped.id = data_header_v2a->id;
|
||||||
|
mapped.mix_volumes = data_header_v2a->mix_volumes;
|
||||||
|
mapped.mix_id = data_header_v2a->mix_id;
|
||||||
|
mapped.in_use = data_header_v2a->in_use;
|
||||||
|
mapped.reset_prev_volume =
|
||||||
|
splitter_prev_volume_reset_supported ? data_header_v2a->reset_prev_volume : false;
|
||||||
|
|
||||||
|
auto& destination = splitter_destinations[data_header_v2a->id];
|
||||||
|
destination.Update(mapped);
|
||||||
|
|
||||||
|
// Convert legacy fixed-point biquad params into float representation
|
||||||
|
auto biquad_filters = destination.GetBiquadFilters();
|
||||||
|
for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
|
||||||
|
const auto& legacy = data_header_v2a->biquad_filters[filter_idx];
|
||||||
|
auto& out = biquad_filters[filter_idx];
|
||||||
|
out.enabled = legacy.enabled;
|
||||||
|
// s16 fixed-point scale: use Q14 like voices (b and a are s16, 1.0 ~= 1<<14)
|
||||||
|
constexpr float scale = 1.0f / static_cast<float>(1 << 14);
|
||||||
|
out.numerator[0] = static_cast<float>(legacy.b[0]) * scale;
|
||||||
|
out.numerator[1] = static_cast<float>(legacy.b[1]) * scale;
|
||||||
|
out.numerator[2] = static_cast<float>(legacy.b[2]) * scale;
|
||||||
|
out.denominator[0] = static_cast<float>(legacy.a[0]) * scale;
|
||||||
|
out.denominator[1] = static_cast<float>(legacy.a[1]) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += static_cast<u32>(sizeof(SplitterDestinationData::InParameterVersion2a));
|
||||||
|
} else {
|
||||||
|
// Version 2b: struct contains extra biquad filter fields with float coeffs
|
||||||
|
const auto* data_header_v2b =
|
||||||
|
reinterpret_cast<const SplitterDestinationData::InParameterVersion2b*>(input +
|
||||||
|
offset);
|
||||||
|
|
||||||
|
if (data_header_v2b->magic != GetSplitterSendDataMagic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (data_header_v2b->id < 0 || data_header_v2b->id > destinations_count) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map common fields to the old format
|
||||||
|
SplitterDestinationData::InParameter mapped{};
|
||||||
|
mapped.magic = data_header_v2b->magic;
|
||||||
|
mapped.id = data_header_v2b->id;
|
||||||
|
mapped.mix_volumes = data_header_v2b->mix_volumes;
|
||||||
|
mapped.mix_id = data_header_v2b->mix_id;
|
||||||
|
mapped.in_use = data_header_v2b->in_use;
|
||||||
|
mapped.reset_prev_volume =
|
||||||
|
splitter_prev_volume_reset_supported ? data_header_v2b->reset_prev_volume : false;
|
||||||
|
|
||||||
|
// Store biquad filters from V2b (REV15+)
|
||||||
|
auto& destination = splitter_destinations[data_header_v2b->id];
|
||||||
|
destination.Update(mapped);
|
||||||
|
|
||||||
|
// Copy biquad filter parameters
|
||||||
|
auto biquad_filters = destination.GetBiquadFilters();
|
||||||
|
for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
|
||||||
|
biquad_filters[filter_idx] = data_header_v2b->biquad_filters[filter_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += static_cast<u32>(sizeof(SplitterDestinationData::InParameterVersion2b));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -168,10 +171,11 @@ private:
|
||||||
* @param splitter_destinations - Workbuffer for splitter destinations.
|
* @param splitter_destinations - Workbuffer for splitter destinations.
|
||||||
* @param destination_count - Number of destinations in the workbuffer.
|
* @param destination_count - Number of destinations in the workbuffer.
|
||||||
* @param splitter_bug_fixed - Is the splitter bug fixed?
|
* @param splitter_bug_fixed - Is the splitter bug fixed?
|
||||||
|
* @param behavior - Behavior info for feature support.
|
||||||
*/
|
*/
|
||||||
void Setup(std::span<SplitterInfo> splitter_infos, u32 splitter_info_count,
|
void Setup(std::span<SplitterInfo> splitter_infos, u32 splitter_info_count,
|
||||||
SplitterDestinationData* splitter_destinations, u32 destination_count,
|
SplitterDestinationData* splitter_destinations, u32 destination_count,
|
||||||
bool splitter_bug_fixed);
|
bool splitter_bug_fixed, const BehaviorInfo& behavior);
|
||||||
|
|
||||||
/// Workbuffer for splitters
|
/// Workbuffer for splitters
|
||||||
std::span<SplitterInfo> splitter_infos{};
|
std::span<SplitterInfo> splitter_infos{};
|
||||||
|
@ -183,6 +187,12 @@ private:
|
||||||
s32 destinations_count{};
|
s32 destinations_count{};
|
||||||
/// Is the splitter bug fixed?
|
/// Is the splitter bug fixed?
|
||||||
bool splitter_bug_fixed{};
|
bool splitter_bug_fixed{};
|
||||||
|
/// Is explicit previous mix volume reset supported?
|
||||||
|
bool splitter_prev_volume_reset_supported{};
|
||||||
|
/// Is biquad filter parameter for splitter (REV12) supported?
|
||||||
|
bool splitter_biquad_param_supported{};
|
||||||
|
/// Is float coefficient/biquad filter v2b parameter supported?
|
||||||
|
bool splitter_float_coeff_supported{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Renderer
|
} // namespace Renderer
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -84,4 +87,14 @@ void SplitterDestinationData::SetNext(SplitterDestinationData* next_) {
|
||||||
next = next_;
|
next = next_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<SplitterDestinationData::BiquadFilterParameter2>
|
||||||
|
SplitterDestinationData::GetBiquadFilters() {
|
||||||
|
return biquad_filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<const SplitterDestinationData::BiquadFilterParameter2>
|
||||||
|
SplitterDestinationData::GetBiquadFilters() const {
|
||||||
|
return biquad_filters;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -16,16 +19,72 @@ namespace AudioCore::Renderer {
|
||||||
*/
|
*/
|
||||||
class SplitterDestinationData {
|
class SplitterDestinationData {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Biquad filter parameter with float coefficients (SDK REV15+).
|
||||||
|
* Defined here to avoid circular dependency with VoiceInfo.
|
||||||
|
*/
|
||||||
|
struct BiquadFilterParameter2 {
|
||||||
|
/* 0x00 */ bool enabled;
|
||||||
|
/* 0x01 */ u8 reserved1;
|
||||||
|
/* 0x02 */ u8 reserved2;
|
||||||
|
/* 0x03 */ u8 reserved3;
|
||||||
|
/* 0x04 */ std::array<f32, 3> numerator; // b0, b1, b2
|
||||||
|
/* 0x10 */ std::array<f32, 2> denominator; // a1, a2 (a0 = 1)
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BiquadFilterParameter2) == 0x18,
|
||||||
|
"BiquadFilterParameter2 has the wrong size!");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy biquad filter parameter with fixed-point coefficients (SDK REV12+ for splitters).
|
||||||
|
* Matches the old voice biquad format.
|
||||||
|
*/
|
||||||
|
struct BiquadFilterParameterLegacy {
|
||||||
|
/* 0x00 */ bool enabled;
|
||||||
|
/* 0x02 */ std::array<s16, 3> b; // numerator
|
||||||
|
/* 0x08 */ std::array<s16, 2> a; // denominator (a0 = 1)
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BiquadFilterParameterLegacy) == 0xC,
|
||||||
|
"BiquadFilterParameterLegacy has the wrong size!");
|
||||||
|
|
||||||
struct InParameter {
|
struct InParameter {
|
||||||
/* 0x00 */ u32 magic; // 'SNDD'
|
/* 0x00 */ u32 magic; // 'SNDD'
|
||||||
/* 0x04 */ s32 id;
|
/* 0x04 */ s32 id;
|
||||||
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
|
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
|
||||||
/* 0x68 */ u32 mix_id;
|
/* 0x68 */ u32 mix_id;
|
||||||
/* 0x6C */ bool in_use;
|
/* 0x6C */ bool in_use;
|
||||||
|
/* 0x6D */ bool reset_prev_volume;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(InParameter) == 0x70,
|
static_assert(sizeof(InParameter) == 0x70,
|
||||||
"SplitterDestinationData::InParameter has the wrong size!");
|
"SplitterDestinationData::InParameter has the wrong size!");
|
||||||
|
|
||||||
|
struct InParameterVersion2a {
|
||||||
|
/* 0x00 */ u32 magic; // 'SNDD'
|
||||||
|
/* 0x04 */ s32 id;
|
||||||
|
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
|
||||||
|
/* 0x68 */ u32 mix_id;
|
||||||
|
/* 0x6C */ std::array<SplitterDestinationData::BiquadFilterParameterLegacy, MaxBiquadFilters>
|
||||||
|
biquad_filters;
|
||||||
|
/* 0x84 */ bool in_use;
|
||||||
|
/* 0x85 */ bool reset_prev_volume; // only effective if supported
|
||||||
|
/* 0x86 */ u8 reserved[10];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(InParameterVersion2a) == 0x90,
|
||||||
|
"SplitterDestinationData::InParameterVersion2a has the wrong size!");
|
||||||
|
|
||||||
|
struct InParameterVersion2b {
|
||||||
|
/* 0x00 */ u32 magic; // 'SNDD'
|
||||||
|
/* 0x04 */ s32 id;
|
||||||
|
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
|
||||||
|
/* 0x68 */ u32 mix_id;
|
||||||
|
/* 0x6C */ std::array<SplitterDestinationData::BiquadFilterParameter2, MaxBiquadFilters>
|
||||||
|
biquad_filters;
|
||||||
|
/* 0x9C */ bool in_use;
|
||||||
|
/* 0x9D */ bool reset_prev_volume;
|
||||||
|
/* 0x9E */ u8 reserved[10];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(InParameterVersion2b) == 0xA8,
|
||||||
|
"SplitterDestinationData::InParameterVersion2b has the wrong size!");
|
||||||
|
|
||||||
SplitterDestinationData(s32 id);
|
SplitterDestinationData(s32 id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +137,7 @@ public:
|
||||||
f32 GetMixVolumePrev(u32 index) const;
|
f32 GetMixVolumePrev(u32 index) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the previous mix volumes for all mix buffers in this destination.
|
* Get the previous mix volumes for all mix buffers.
|
||||||
*
|
*
|
||||||
* @return Span of previous mix buffer volumes.
|
* @return Span of previous mix buffer volumes.
|
||||||
*/
|
*/
|
||||||
|
@ -115,6 +174,20 @@ public:
|
||||||
*/
|
*/
|
||||||
void SetNext(SplitterDestinationData* next);
|
void SetNext(SplitterDestinationData* next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get biquad filter parameters for this destination (REV15+ or mapped from REV12).
|
||||||
|
*
|
||||||
|
* @return Span of biquad filter parameters.
|
||||||
|
*/
|
||||||
|
std::span<BiquadFilterParameter2> GetBiquadFilters();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get const biquad filter parameters for this destination (REV15+ or mapped from REV12).
|
||||||
|
*
|
||||||
|
* @return Const span of biquad filter parameters.
|
||||||
|
*/
|
||||||
|
std::span<const BiquadFilterParameter2> GetBiquadFilters() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Id of this destination
|
/// Id of this destination
|
||||||
const s32 id;
|
const s32 id;
|
||||||
|
@ -124,6 +197,8 @@ private:
|
||||||
std::array<f32, MaxMixBuffers> mix_volumes{0.0f};
|
std::array<f32, MaxMixBuffers> mix_volumes{0.0f};
|
||||||
/// Previous mix volumes
|
/// Previous mix volumes
|
||||||
std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f};
|
std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f};
|
||||||
|
/// Biquad filter parameters (REV15+ or mapped from REV12)
|
||||||
|
std::array<BiquadFilterParameter2, MaxBiquadFilters> biquad_filters{};
|
||||||
/// Next destination in the mix chain
|
/// Next destination in the mix chain
|
||||||
SplitterDestinationData* next{};
|
SplitterDestinationData* next{};
|
||||||
/// Is this destination in use?
|
/// Is this destination in use?
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -135,6 +138,17 @@ public:
|
||||||
static_assert(sizeof(BiquadFilterParameter) == 0xC,
|
static_assert(sizeof(BiquadFilterParameter) == 0xC,
|
||||||
"VoiceInfo::BiquadFilterParameter has the wrong size!");
|
"VoiceInfo::BiquadFilterParameter has the wrong size!");
|
||||||
|
|
||||||
|
struct BiquadFilterParameter2 {
|
||||||
|
/* 0x00 */ bool enabled;
|
||||||
|
/* 0x01 */ u8 reserved1;
|
||||||
|
/* 0x02 */ u8 reserved2;
|
||||||
|
/* 0x03 */ u8 reserved3;
|
||||||
|
/* 0x04 */ std::array<f32, 3> numerator; // b0, b1, b2
|
||||||
|
/* 0x10 */ std::array<f32, 2> denominator; // a1, a2 (a0 = 1)
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BiquadFilterParameter2) == 0x18,
|
||||||
|
"VoiceInfo::BiquadFilterParameter2 has the wrong size!");
|
||||||
|
|
||||||
struct InParameter {
|
struct InParameter {
|
||||||
/* 0x000 */ u32 id;
|
/* 0x000 */ u32 id;
|
||||||
/* 0x004 */ u32 node_id;
|
/* 0x004 */ u32 node_id;
|
||||||
|
@ -168,6 +182,43 @@ public:
|
||||||
};
|
};
|
||||||
static_assert(sizeof(InParameter) == 0x170, "VoiceInfo::InParameter has the wrong size!");
|
static_assert(sizeof(InParameter) == 0x170, "VoiceInfo::InParameter has the wrong size!");
|
||||||
|
|
||||||
|
struct InParameter2 {
|
||||||
|
/* 0x000 */ u32 id;
|
||||||
|
/* 0x004 */ u32 node_id;
|
||||||
|
/* 0x008 */ bool is_new;
|
||||||
|
/* 0x009 */ bool in_use;
|
||||||
|
/* 0x00A */ PlayState play_state;
|
||||||
|
/* 0x00B */ SampleFormat sample_format;
|
||||||
|
/* 0x00C */ u32 sample_rate;
|
||||||
|
/* 0x010 */ s32 priority;
|
||||||
|
/* 0x014 */ s32 sort_order;
|
||||||
|
/* 0x018 */ u32 channel_count;
|
||||||
|
/* 0x01C */ f32 pitch;
|
||||||
|
/* 0x020 */ f32 volume;
|
||||||
|
/* 0x024 */ std::array<BiquadFilterParameter2, MaxBiquadFilters> biquads;
|
||||||
|
/* 0x054 */ u32 wave_buffer_count;
|
||||||
|
/* 0x058 */ u32 wave_buffer_index;
|
||||||
|
/* 0x05C */ u32 reserved1;
|
||||||
|
/* 0x060 */ CpuAddr src_data_address;
|
||||||
|
/* 0x068 */ u64 src_data_size;
|
||||||
|
/* 0x070 */ u32 mix_id;
|
||||||
|
/* 0x074 */ u32 splitter_id;
|
||||||
|
/* 0x078 */ std::array<WaveBufferInternal, MaxWaveBuffers> wave_buffer_internal;
|
||||||
|
/* 0x158 */ std::array<s32, MaxChannels> channel_resource_ids;
|
||||||
|
/* 0x170 */ bool clear_voice_drop;
|
||||||
|
/* 0x171 */ u8 flush_buffer_count;
|
||||||
|
/* 0x172 */ u16 reserved2;
|
||||||
|
/* 0x174 */ Flags flags;
|
||||||
|
/* 0x175 */ u8 reserved3;
|
||||||
|
/* 0x176 */ SrcQuality src_quality;
|
||||||
|
/* 0x177 */ u8 reserved4;
|
||||||
|
/* 0x178 */ u32 external_context;
|
||||||
|
/* 0x17C */ u32 external_context_size;
|
||||||
|
/* 0x180 */ u32 reserved5;
|
||||||
|
/* 0x184 */ u32 reserved6;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(InParameter2) == 0x188, "VoiceInfo::InParameter2 has the wrong size!");
|
||||||
|
|
||||||
struct OutStatus {
|
struct OutStatus {
|
||||||
/* 0x00 */ u64 played_sample_count;
|
/* 0x00 */ u64 played_sample_count;
|
||||||
/* 0x08 */ u32 wave_buffers_consumed;
|
/* 0x08 */ u32 wave_buffers_consumed;
|
||||||
|
@ -349,6 +400,10 @@ public:
|
||||||
f32 prev_volume{};
|
f32 prev_volume{};
|
||||||
/// Biquad filters for generating filter commands on this voice
|
/// Biquad filters for generating filter commands on this voice
|
||||||
std::array<BiquadFilterParameter, MaxBiquadFilters> biquads{};
|
std::array<BiquadFilterParameter, MaxBiquadFilters> biquads{};
|
||||||
|
/// Float biquad filters for REV15+ (native float coefficients)
|
||||||
|
std::array<BiquadFilterParameter2, MaxBiquadFilters> biquads_float{};
|
||||||
|
/// Use float biquad coefficients (REV15+)
|
||||||
|
bool use_float_biquads{};
|
||||||
/// Number of active wavebuffers
|
/// Number of active wavebuffers
|
||||||
u32 wave_buffer_count{};
|
u32 wave_buffer_count{};
|
||||||
/// Current playing wavebuffer index
|
/// Current playing wavebuffer index
|
||||||
|
|
|
@ -161,7 +161,7 @@ struct Values {
|
||||||
Category::LibraryApplet};
|
Category::LibraryApplet};
|
||||||
Setting<AppletMode> photo_viewer_applet_mode{
|
Setting<AppletMode> photo_viewer_applet_mode{
|
||||||
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
|
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
|
||||||
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::HLE, "offline_web_applet_mode",
|
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
|
||||||
Category::LibraryApplet};
|
Category::LibraryApplet};
|
||||||
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
|
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
|
||||||
Category::LibraryApplet};
|
Category::LibraryApplet};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -68,42 +71,6 @@ std::string ResolveURL(const std::string& url) {
|
||||||
return url.substr(0, index) + "lp1" + url.substr(index + 1);
|
return url.substr(0, index) + "lp1" + url.substr(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
|
|
||||||
std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
|
|
||||||
|
|
||||||
if (web_arg.size() == sizeof(WebArgHeader)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
WebArgInputTLVMap input_tlv_map;
|
|
||||||
|
|
||||||
u64 current_offset = sizeof(WebArgHeader);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) {
|
|
||||||
if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) {
|
|
||||||
return input_tlv_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebArgInputTLV input_tlv;
|
|
||||||
std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV));
|
|
||||||
|
|
||||||
current_offset += sizeof(WebArgInputTLV);
|
|
||||||
|
|
||||||
if (web_arg.size() < current_offset + input_tlv.arg_data_size) {
|
|
||||||
return input_tlv_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> data(input_tlv.arg_data_size);
|
|
||||||
std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size);
|
|
||||||
|
|
||||||
current_offset += input_tlv.arg_data_size;
|
|
||||||
|
|
||||||
input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
return input_tlv_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
|
FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
|
||||||
FileSys::ContentRecordType nca_type) {
|
FileSys::ContentRecordType nca_type) {
|
||||||
if (nca_type == FileSys::ContentRecordType::Data) {
|
if (nca_type == FileSys::ContentRecordType::Data) {
|
||||||
|
@ -144,6 +111,43 @@ FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||||
|
WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
|
||||||
|
std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
|
||||||
|
|
||||||
|
if (web_arg.size() == sizeof(WebArgHeader)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
WebArgInputTLVMap input_tlv_map;
|
||||||
|
|
||||||
|
u64 current_offset = sizeof(WebArgHeader);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) {
|
||||||
|
if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) {
|
||||||
|
return input_tlv_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebArgInputTLV input_tlv;
|
||||||
|
std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV));
|
||||||
|
|
||||||
|
current_offset += sizeof(WebArgInputTLV);
|
||||||
|
|
||||||
|
if (web_arg.size() < current_offset + input_tlv.arg_data_size) {
|
||||||
|
return input_tlv_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> data(input_tlv.arg_data_size);
|
||||||
|
std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size);
|
||||||
|
|
||||||
|
current_offset += input_tlv.arg_data_size;
|
||||||
|
|
||||||
|
input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return input_tlv_map;
|
||||||
|
}
|
||||||
|
|
||||||
void ExtractSharedFonts(Core::System& system) {
|
void ExtractSharedFonts(Core::System& system) {
|
||||||
static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{
|
static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{
|
||||||
"FontStandard.ttf",
|
"FontStandard.ttf",
|
||||||
|
@ -221,6 +225,7 @@ void ExtractSharedFonts(Core::System& system) {
|
||||||
FileSys::VfsRawCopy(decrypted_font, out_file);
|
FileSys::VfsRawCopy(decrypted_font, out_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -232,6 +237,7 @@ WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
|
||||||
WebBrowser::~WebBrowser() = default;
|
WebBrowser::~WebBrowser() = default;
|
||||||
|
|
||||||
void WebBrowser::Initialize() {
|
void WebBrowser::Initialize() {
|
||||||
|
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||||
FrontendApplet::Initialize();
|
FrontendApplet::Initialize();
|
||||||
|
|
||||||
LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
|
LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
|
||||||
|
@ -284,6 +290,7 @@ void WebBrowser::Initialize() {
|
||||||
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
|
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WebBrowser::GetStatus() const {
|
Result WebBrowser::GetStatus() const {
|
||||||
|
@ -295,6 +302,7 @@ void WebBrowser::ExecuteInteractive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebBrowser::Execute() {
|
void WebBrowser::Execute() {
|
||||||
|
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||||
switch (web_arg_header.shim_kind) {
|
switch (web_arg_header.shim_kind) {
|
||||||
case ShimKind::Shop:
|
case ShimKind::Shop:
|
||||||
ExecuteShop();
|
ExecuteShop();
|
||||||
|
@ -322,6 +330,10 @@ void WebBrowser::Execute() {
|
||||||
WebBrowserExit(WebExitReason::EndButtonPressed);
|
WebBrowserExit(WebExitReason::EndButtonPressed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
LOG_INFO(Service_AM, "Web Browser Applet disabled, skipping.");
|
||||||
|
WebBrowserExit(WebExitReason::EndButtonPressed);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebBrowser::ExtractOfflineRomFS() {
|
void WebBrowser::ExtractOfflineRomFS() {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -175,6 +178,7 @@ ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_
|
||||||
{0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},
|
{0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},
|
||||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||||
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
||||||
|
{3, D<&ILibraryAppletCreator::CreateLibraryAppletEx>, "CreateLibraryAppletEx"},
|
||||||
{10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"},
|
{10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"},
|
||||||
{11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"},
|
{11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"},
|
||||||
{12, D<&ILibraryAppletCreator::CreateHandleStorage>, "CreateHandleStorage"},
|
{12, D<&ILibraryAppletCreator::CreateHandleStorage>, "CreateHandleStorage"},
|
||||||
|
@ -210,6 +214,32 @@ Result ILibraryAppletCreator::CreateLibraryApplet(
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ILibraryAppletCreator::CreateLibraryAppletEx(
|
||||||
|
Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
|
||||||
|
LibraryAppletMode library_applet_mode, u64 thread_id) {
|
||||||
|
LOG_DEBUG(Service_AM, "called with applet_id={} applet_mode={} thread_id={}", applet_id,
|
||||||
|
library_applet_mode, thread_id);
|
||||||
|
|
||||||
|
std::shared_ptr<ILibraryAppletAccessor> library_applet;
|
||||||
|
if (ShouldCreateGuestApplet(applet_id)) {
|
||||||
|
library_applet =
|
||||||
|
CreateGuestApplet(system, m_window_system, m_applet, applet_id, library_applet_mode);
|
||||||
|
}
|
||||||
|
if (!library_applet) {
|
||||||
|
library_applet =
|
||||||
|
CreateFrontendApplet(system, m_window_system, m_applet, applet_id, library_applet_mode);
|
||||||
|
}
|
||||||
|
if (!library_applet) {
|
||||||
|
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
|
||||||
|
R_THROW(ResultUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applet is created, can now be launched.
|
||||||
|
m_applet->library_applet_launchable_event.Signal();
|
||||||
|
*out_library_applet_accessor = library_applet;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result ILibraryAppletCreator::CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size) {
|
Result ILibraryAppletCreator::CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size) {
|
||||||
LOG_DEBUG(Service_AM, "called, size={}", size);
|
LOG_DEBUG(Service_AM, "called, size={}", size);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -24,6 +27,9 @@ private:
|
||||||
Result CreateLibraryApplet(
|
Result CreateLibraryApplet(
|
||||||
Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
|
Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
|
||||||
LibraryAppletMode library_applet_mode);
|
LibraryAppletMode library_applet_mode);
|
||||||
|
Result CreateLibraryAppletEx(
|
||||||
|
Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
|
||||||
|
LibraryAppletMode library_applet_mode, u64 thread_id);
|
||||||
Result CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size);
|
Result CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size);
|
||||||
Result CreateTransferMemoryStorage(
|
Result CreateTransferMemoryStorage(
|
||||||
Out<SharedPointer<IStorage>> out_storage, bool is_writable, s64 size,
|
Out<SharedPointer<IStorage>> out_storage, bool is_writable, s64 size,
|
||||||
|
|
|
@ -128,6 +128,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||||
{406, nullptr, "GetApplicationControlProperty"},
|
{406, nullptr, "GetApplicationControlProperty"},
|
||||||
{407, nullptr, "ListApplicationTitle"},
|
{407, nullptr, "ListApplicationTitle"},
|
||||||
{408, nullptr, "ListApplicationIcon"},
|
{408, nullptr, "ListApplicationIcon"},
|
||||||
|
{419, D<&IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground>, "RequestDownloadApplicationControlDataInBackground"},
|
||||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||||
{504, nullptr, "RequestRegisterGameCard"},
|
{504, nullptr, "RequestRegisterGameCard"},
|
||||||
|
@ -210,6 +211,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||||
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
||||||
{1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"},
|
{1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"},
|
||||||
{1705, nullptr, "IsPatchAutoDeletableApplication"},
|
{1705, nullptr, "IsPatchAutoDeletableApplication"},
|
||||||
|
{1706, D<&IApplicationManagerInterface::Unknown1706>, "Unknown1706"},
|
||||||
{1800, nullptr, "IsNotificationSetupCompleted"},
|
{1800, nullptr, "IsNotificationSetupCompleted"},
|
||||||
{1801, nullptr, "GetLastNotificationInfoCount"},
|
{1801, nullptr, "GetLastNotificationInfoCount"},
|
||||||
{1802, nullptr, "ListLastNotificationInfo"},
|
{1802, nullptr, "ListLastNotificationInfo"},
|
||||||
|
@ -309,6 +311,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||||
{4022, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4022"},
|
{4022, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4022"},
|
||||||
{4023, D<&IApplicationManagerInterface::Unknown4023>, "Unknown4023"},
|
{4023, D<&IApplicationManagerInterface::Unknown4023>, "Unknown4023"},
|
||||||
{4088, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4088"},
|
{4088, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4088"},
|
||||||
|
{4053, D<&IApplicationManagerInterface::Unknown4053>, "Unknown4053"},
|
||||||
{9999, nullptr, "GetApplicationCertificate"},
|
{9999, nullptr, "GetApplicationCertificate"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@ -526,6 +529,37 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> o
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground(
|
||||||
|
u64 unk, u64 application_id) {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED), app={:016X} unk={}", application_id, unk);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IApplicationManagerInterface::Unknown1706(
|
||||||
|
OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_58,
|
||||||
|
InBuffer<BufferAttr_HipcMapAlias> in_buffer_8) {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED) Unknown1706 called: out_size={} in_size={}",
|
||||||
|
out_buffer_58.size(), in_buffer_8.size());
|
||||||
|
|
||||||
|
if (out_buffer_58.size() < 0x58 || in_buffer_8.size() < 0x8) {
|
||||||
|
R_THROW(ResultUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 application_id = 0;
|
||||||
|
std::memcpy(&application_id, in_buffer_8.data(), sizeof(u64));
|
||||||
|
|
||||||
|
ApplicationView view{};
|
||||||
|
view.application_id = application_id;
|
||||||
|
view.unk = 0x70000;
|
||||||
|
view.flags = 0x401f17;
|
||||||
|
|
||||||
|
std::memset(out_buffer_58.data(), 0, out_buffer_58.size());
|
||||||
|
std::memcpy(out_buffer_58.data(), &view, sizeof(ApplicationView));
|
||||||
|
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IApplicationManagerInterface::Unknown4022(
|
Result IApplicationManagerInterface::Unknown4022(
|
||||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||||
|
@ -539,4 +573,9 @@ Result IApplicationManagerInterface::Unknown4023(Out<u64> out_result) {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IApplicationManagerInterface::Unknown4053() {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
@ -55,6 +55,12 @@ public:
|
||||||
Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
|
Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
|
||||||
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||||
Result Unknown4023(Out<u64> out_result);
|
Result Unknown4023(Out<u64> out_result);
|
||||||
|
Result Unknown4053();
|
||||||
|
|
||||||
|
Result RequestDownloadApplicationControlDataInBackground(u64 unk,
|
||||||
|
u64 application_id);
|
||||||
|
Result Unknown1706(OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_58,
|
||||||
|
InBuffer<BufferAttr_HipcMapAlias> in_buffer_8);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KernelHelpers::ServiceContext service_context;
|
KernelHelpers::ServiceContext service_context;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -83,8 +86,9 @@ static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size."
|
||||||
struct ApplicationViewWithPromotionInfo {
|
struct ApplicationViewWithPromotionInfo {
|
||||||
ApplicationView view; ///< \ref NsApplicationView
|
ApplicationView view; ///< \ref NsApplicationView
|
||||||
PromotionInfo promotion; ///< \ref NsPromotionInfo
|
PromotionInfo promotion; ///< \ref NsPromotionInfo
|
||||||
|
std::array<u8, 0x8> padding{}; ///< Extra padding for newer HOS versions
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70,
|
static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x78,
|
||||||
"ApplicationViewWithPromotionInfo has incorrect size.");
|
"ApplicationViewWithPromotionInfo has incorrect size.");
|
||||||
|
|
||||||
struct ApplicationOccupiedSizeEntity {
|
struct ApplicationOccupiedSizeEntity {
|
||||||
|
@ -113,4 +117,10 @@ struct Uid {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Uid) == 0x10, "Uid has incorrect size.");
|
static_assert(sizeof(Uid) == 0x10, "Uid has incorrect size.");
|
||||||
|
|
||||||
|
struct ApplicationDisplayData {
|
||||||
|
std::array<char, 0x200> application_name;
|
||||||
|
std::array<char, 0x100> developer_name;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ApplicationDisplayData) == 0x300, "ApplicationDisplayData has incorrect size.");
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -7,6 +10,7 @@
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
#include "core/hle/service/ns/ns_results.h"
|
#include "core/hle/service/ns/ns_results.h"
|
||||||
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
||||||
#include "core/hle/service/set/settings_server.h"
|
#include "core/hle/service/set/settings_server.h"
|
||||||
|
@ -23,6 +27,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||||
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
||||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||||
|
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDisplayData>, "GetApplicationDisplayData"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -119,4 +124,33 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IReadOnlyApplicationControlDataInterface::GetApplicationDisplayData(
|
||||||
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u64> out_size, u64 language_code,
|
||||||
|
u64 application_id) {
|
||||||
|
LOG_INFO(Service_NS, "called with application_id={:016X}, language_code={:016X}",
|
||||||
|
application_id, language_code);
|
||||||
|
|
||||||
|
constexpr u64 payload_size = sizeof(ApplicationDisplayData);
|
||||||
|
|
||||||
|
if (out_buffer.size() < payload_size) {
|
||||||
|
LOG_ERROR(Service_NS, "output buffer is too small! (actual={}, expected_min={})",
|
||||||
|
out_buffer.size(), payload_size);
|
||||||
|
R_THROW(ResultUnknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||||
|
system.GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
|
||||||
|
ApplicationDisplayData display_data{};
|
||||||
|
|
||||||
|
std::memset(display_data.application_name.data(), 0, display_data.application_name.size());
|
||||||
|
std::memset(display_data.developer_name.data(), 0, display_data.developer_name.size());
|
||||||
|
|
||||||
|
std::memcpy(out_buffer.data(), &display_data, payload_size);
|
||||||
|
*out_size = payload_size;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
#include "core/hle/service/cmif_types.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
#include "core/hle/service/ns/ns_types.h"
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Service::NS {
|
namespace Service::NS {
|
||||||
|
|
||||||
|
@ -16,7 +19,6 @@ public:
|
||||||
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
||||||
~IReadOnlyApplicationControlDataInterface() override;
|
~IReadOnlyApplicationControlDataInterface() override;
|
||||||
|
|
||||||
public:
|
|
||||||
Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
Out<u32> out_actual_size,
|
Out<u32> out_actual_size,
|
||||||
ApplicationControlSource application_control_source,
|
ApplicationControlSource application_control_source,
|
||||||
|
@ -25,6 +27,10 @@ public:
|
||||||
u32 supported_languages);
|
u32 supported_languages);
|
||||||
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
||||||
ApplicationLanguage application_language);
|
ApplicationLanguage application_language);
|
||||||
|
|
||||||
|
Result GetApplicationDisplayData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
Out<u64> out_size, u64 language_code,
|
||||||
|
u64 application_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/ns/read_only_application_record_interface.h"
|
#include "core/hle/service/ns/read_only_application_record_interface.h"
|
||||||
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
|
#include "core/hle/service/ns/application_manager_interface.h"
|
||||||
|
|
||||||
namespace Service::NS {
|
namespace Service::NS {
|
||||||
|
|
||||||
|
@ -13,6 +18,8 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
|
||||||
{1, nullptr, "NotifyApplicationFailure"},
|
{1, nullptr, "NotifyApplicationFailure"},
|
||||||
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
|
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
|
||||||
"IsDataCorruptedResult"},
|
"IsDataCorruptedResult"},
|
||||||
|
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
|
||||||
|
"ListApplicationRecord"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -35,4 +42,14 @@ Result IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IReadOnlyApplicationRecordInterface::ListApplicationRecord(
|
||||||
|
OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
|
||||||
|
s32 entry_offset) {
|
||||||
|
LOG_DEBUG(Service_NS, "delegating to IApplicationManagerInterface::ListApplicationRecord, offset={} limit={}",
|
||||||
|
entry_offset, out_records.size());
|
||||||
|
|
||||||
|
R_RETURN(IApplicationManagerInterface(system).ListApplicationRecord(out_records, out_count,
|
||||||
|
entry_offset));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -5,6 +8,7 @@
|
||||||
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
#include "core/hle/service/cmif_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
|
|
||||||
namespace Service::NS {
|
namespace Service::NS {
|
||||||
|
|
||||||
|
@ -17,6 +21,9 @@ public:
|
||||||
private:
|
private:
|
||||||
Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id);
|
Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id);
|
||||||
Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result);
|
Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result);
|
||||||
|
Result ListApplicationRecord(
|
||||||
|
OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
|
||||||
|
s32 entry_offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
@ -38,6 +38,7 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili
|
||||||
{1016, nullptr, "ConfirmShowNewsPermission"},
|
{1016, nullptr, "ConfirmShowNewsPermission"},
|
||||||
{1017, D<&IParentalControlService::EndFreeCommunication>, "EndFreeCommunication"},
|
{1017, D<&IParentalControlService::EndFreeCommunication>, "EndFreeCommunication"},
|
||||||
{1018, D<&IParentalControlService::IsFreeCommunicationAvailable>, "IsFreeCommunicationAvailable"},
|
{1018, D<&IParentalControlService::IsFreeCommunicationAvailable>, "IsFreeCommunicationAvailable"},
|
||||||
|
{1019, D<&IParentalControlService::ConfirmLaunchApplicationPermission>, "ConfirmLaunchApplicationPermission"},
|
||||||
{1031, D<&IParentalControlService::IsRestrictionEnabled>, "IsRestrictionEnabled"},
|
{1031, D<&IParentalControlService::IsRestrictionEnabled>, "IsRestrictionEnabled"},
|
||||||
{1032, D<&IParentalControlService::GetSafetyLevel>, "GetSafetyLevel"},
|
{1032, D<&IParentalControlService::GetSafetyLevel>, "GetSafetyLevel"},
|
||||||
{1033, nullptr, "SetSafetyLevel"},
|
{1033, nullptr, "SetSafetyLevel"},
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -10,7 +7,6 @@ add_library(frontend_common STATIC
|
||||||
content_manager.h
|
content_manager.h
|
||||||
firmware_manager.h
|
firmware_manager.h
|
||||||
firmware_manager.cpp
|
firmware_manager.cpp
|
||||||
data_manager.h data_manager.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
create_target_directory_groups(frontend_common)
|
create_target_directory_groups(frontend_common)
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "data_manager.h"
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/fs/path_util.h"
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
namespace FrontendCommon::DataManager {
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
const std::string GetDataDir(DataDir dir, const std::string &user_id)
|
|
||||||
{
|
|
||||||
const fs::path nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
|
|
||||||
|
|
||||||
switch (dir) {
|
|
||||||
case DataDir::Saves:
|
|
||||||
return (nand_dir / "user" / "save" / "0000000000000000" / user_id).string();
|
|
||||||
case DataDir::UserNand:
|
|
||||||
return (nand_dir / "user" / "Contents" / "registered").string();
|
|
||||||
case DataDir::SysNand:
|
|
||||||
// NB: do NOT delete save
|
|
||||||
// that contains profile data and other stuff
|
|
||||||
return (nand_dir / "system" / "Contents" / "registered").string();
|
|
||||||
case DataDir::Mods:
|
|
||||||
return Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir);
|
|
||||||
case DataDir::Shaders:
|
|
||||||
return Common::FS::GetEdenPathString(Common::FS::EdenPath::ShaderDir);
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 ClearDir(DataDir dir, const std::string &user_id)
|
|
||||||
{
|
|
||||||
fs::path data_dir = GetDataDir(dir, user_id);
|
|
||||||
u64 result = fs::remove_all(data_dir);
|
|
||||||
|
|
||||||
// mkpath at the end just so it actually exists
|
|
||||||
fs::create_directories(data_dir);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string ReadableBytesSize(u64 size)
|
|
||||||
{
|
|
||||||
static constexpr std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
|
|
||||||
if (size == 0) {
|
|
||||||
return "0 B";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int digit_groups = (std::min) (static_cast<int>(std::log10(size) / std::log10(1024)),
|
|
||||||
static_cast<int>(units.size()));
|
|
||||||
return fmt::format("{:.1f} {}", size / std::pow(1024, digit_groups), units[digit_groups]);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 DataDirSize(DataDir dir)
|
|
||||||
{
|
|
||||||
fs::path data_dir = GetDataDir(dir);
|
|
||||||
u64 size = 0;
|
|
||||||
|
|
||||||
if (!fs::exists(data_dir))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (const auto& entry : fs::recursive_directory_iterator(data_dir)) {
|
|
||||||
if (!entry.is_directory()) {
|
|
||||||
size += entry.file_size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#ifndef DATA_MANAGER_H
|
|
||||||
#define DATA_MANAGER_H
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace FrontendCommon::DataManager {
|
|
||||||
|
|
||||||
enum class DataDir { Saves, UserNand, SysNand, Mods, Shaders };
|
|
||||||
|
|
||||||
const std::string GetDataDir(DataDir dir, const std::string &user_id = "");
|
|
||||||
|
|
||||||
u64 ClearDir(DataDir dir, const std::string &user_id = "");
|
|
||||||
|
|
||||||
const std::string ReadableBytesSize(u64 size);
|
|
||||||
|
|
||||||
u64 DataDirSize(DataDir dir);
|
|
||||||
|
|
||||||
}; // namespace FrontendCommon::DataManager
|
|
||||||
|
|
||||||
#endif // DATA_MANAGER_H
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -159,6 +162,17 @@ struct NpadGcTriggerState {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
|
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::hid::NpadCondition (global controller condition structure)
|
||||||
|
struct NpadCondition {
|
||||||
|
u32 _00{};
|
||||||
|
u32 is_initialized{1};
|
||||||
|
u32 hold_type{static_cast<u32>(NpadJoyHoldType::Horizontal)};
|
||||||
|
u32 is_valid{1};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NpadCondition) == 0x10, "NpadCondition is an invalid size");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This is nn::hid::NpadSystemProperties
|
// This is nn::hid::NpadSystemProperties
|
||||||
struct NPadSystemProperties {
|
struct NPadSystemProperties {
|
||||||
union {
|
union {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -44,8 +47,7 @@ struct Lifo {
|
||||||
buffer_count++;
|
buffer_count++;
|
||||||
}
|
}
|
||||||
buffer_tail = GetNextEntryIndex();
|
buffer_tail = GetNextEntryIndex();
|
||||||
const auto& previous_entry = ReadPreviousEntry();
|
entries[buffer_tail].sampling_number = new_state.sampling_number << 1;
|
||||||
entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
|
|
||||||
entries[buffer_tail].state = new_state;
|
entries[buffer_tail].state = new_state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -201,7 +204,9 @@ static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
|
||||||
|
|
||||||
// This is nn::hid::detail::SharedMemoryFormat
|
// This is nn::hid::detail::SharedMemoryFormat
|
||||||
struct SharedMemoryFormat {
|
struct SharedMemoryFormat {
|
||||||
void Initialize() {}
|
void Initialize() {
|
||||||
|
npad_condition = NpadCondition{};
|
||||||
|
}
|
||||||
|
|
||||||
DebugPadSharedMemoryFormat debug_pad;
|
DebugPadSharedMemoryFormat debug_pad;
|
||||||
TouchScreenSharedMemoryFormat touch_screen;
|
TouchScreenSharedMemoryFormat touch_screen;
|
||||||
|
@ -218,7 +223,9 @@ struct SharedMemoryFormat {
|
||||||
ConsoleSixAxisSensorSharedMemoryFormat console;
|
ConsoleSixAxisSensorSharedMemoryFormat console;
|
||||||
INSERT_PADDING_BYTES(0x19E0);
|
INSERT_PADDING_BYTES(0x19E0);
|
||||||
MouseSharedMemoryFormat debug_mouse;
|
MouseSharedMemoryFormat debug_mouse;
|
||||||
INSERT_PADDING_BYTES(0x2000);
|
INSERT_PADDING_BYTES(0x200);
|
||||||
|
NpadCondition npad_condition;
|
||||||
|
INSERT_PADDING_BYTES(0x1DF0);
|
||||||
};
|
};
|
||||||
static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
|
static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
|
||||||
static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
|
static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
|
||||||
|
@ -236,6 +243,8 @@ static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offs
|
||||||
static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
|
static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
|
||||||
static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
|
static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
|
||||||
static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
|
static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
|
||||||
|
static_assert(offsetof(SharedMemoryFormat, npad_condition) == 0x3E200,
|
||||||
|
"npad_condition has wrong offset");
|
||||||
static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");
|
static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||||
|
|
||||||
add_library(qt_common STATIC
|
add_library(qt_common STATIC
|
||||||
qt_common.h
|
qt_common.h
|
||||||
qt_common.cpp
|
qt_common.cpp
|
||||||
|
@ -24,8 +27,6 @@ add_library(qt_common STATIC
|
||||||
qt_rom_util.h qt_rom_util.cpp
|
qt_rom_util.h qt_rom_util.cpp
|
||||||
qt_applet_util.h qt_applet_util.cpp
|
qt_applet_util.h qt_applet_util.cpp
|
||||||
qt_progress_dialog.h qt_progress_dialog.cpp
|
qt_progress_dialog.h qt_progress_dialog.cpp
|
||||||
qt_string_lookup.h
|
|
||||||
qt_compress.h qt_compress.cpp
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,29 +37,9 @@ if (ENABLE_QT)
|
||||||
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
|
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(qt_common PUBLIC
|
|
||||||
# Use QStringBuilder for string concatenation to reduce
|
|
||||||
# the overall number of temporary strings created.
|
|
||||||
QT_USE_QSTRINGBUILDER
|
|
||||||
|
|
||||||
# Disable implicit conversions from/to C strings
|
|
||||||
QT_NO_CAST_FROM_ASCII
|
|
||||||
QT_NO_CAST_TO_ASCII
|
|
||||||
|
|
||||||
# Disable implicit type narrowing in signal/slot connect() calls.
|
|
||||||
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
|
||||||
|
|
||||||
# Disable unsafe overloads of QProcess' start() function.
|
|
||||||
QT_NO_PROCESS_COMBINED_ARGUMENT_START
|
|
||||||
|
|
||||||
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
|
||||||
QT_NO_URL_CAST_FROM_STRING
|
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
|
|
||||||
target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip)
|
target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip)
|
||||||
target_link_libraries(qt_common PUBLIC frozen::frozen)
|
|
||||||
|
|
||||||
if (NOT APPLE AND ENABLE_OPENGL)
|
if (NOT APPLE AND ENABLE_OPENGL)
|
||||||
target_compile_definitions(qt_common PUBLIC HAS_OPENGL)
|
target_compile_definitions(qt_common PUBLIC HAS_OPENGL)
|
||||||
|
|
3
src/qt_common/externals/CMakeLists.txt
vendored
|
@ -16,4 +16,5 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||||
AddJsonPackage(quazip)
|
AddJsonPackage(quazip)
|
||||||
|
|
||||||
# frozen
|
# frozen
|
||||||
AddJsonPackage(frozen)
|
# TODO(crueter): Qt String Lookup
|
||||||
|
# AddJsonPackage(frozen)
|
||||||
|
|
|
@ -1,354 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "qt_compress.h"
|
|
||||||
#include "quazipfileinfo.h"
|
|
||||||
|
|
||||||
#include <QDirIterator>
|
|
||||||
|
|
||||||
/** This is a modified version of JlCompress **/
|
|
||||||
namespace QtCommon::Compress {
|
|
||||||
|
|
||||||
bool compressDir(QString fileCompressed,
|
|
||||||
QString dir,
|
|
||||||
const JlCompress::Options &options,
|
|
||||||
QtCommon::QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
// Create zip
|
|
||||||
QuaZip zip(fileCompressed);
|
|
||||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
|
||||||
if (!zip.open(QuaZip::mdCreate)) {
|
|
||||||
QFile::remove(fileCompressed);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See how big the overall fs structure is
|
|
||||||
// good approx. of total progress
|
|
||||||
// TODO(crueter): QDirListing impl
|
|
||||||
QDirIterator iter(dir,
|
|
||||||
QDir::NoDotAndDotDot | QDir::Hidden | QDir::Files,
|
|
||||||
QDirIterator::Subdirectories);
|
|
||||||
|
|
||||||
std::size_t total = 0;
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
total += iter.nextFileInfo().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t progress = 0;
|
|
||||||
callback(total, progress);
|
|
||||||
|
|
||||||
// Add the files and subdirectories
|
|
||||||
if (!compressSubDir(&zip, dir, dir, options, total, progress, callback)) {
|
|
||||||
QFile::remove(fileCompressed);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close zip
|
|
||||||
zip.close();
|
|
||||||
if (zip.getZipError() != 0) {
|
|
||||||
QFile::remove(fileCompressed);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compressSubDir(QuaZip *zip,
|
|
||||||
QString dir,
|
|
||||||
QString origDir,
|
|
||||||
const JlCompress::Options &options,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
// zip: object where to add the file
|
|
||||||
// dir: current real directory
|
|
||||||
// origDir: original real directory
|
|
||||||
// (path(dir)-path(origDir)) = path inside the zip object
|
|
||||||
|
|
||||||
if (!zip)
|
|
||||||
return false;
|
|
||||||
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
|
|
||||||
&& zip->getMode() != QuaZip::mdAdd)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QDir directory(dir);
|
|
||||||
if (!directory.exists())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
|
||||||
QDir origDirectory(origDir);
|
|
||||||
if (dir != origDir) {
|
|
||||||
QuaZipFile dirZipFile(zip);
|
|
||||||
std::unique_ptr<QuaZipNewInfo> qzni;
|
|
||||||
if (options.getDateTime().isNull()) {
|
|
||||||
qzni = std::make_unique<QuaZipNewInfo>(origDirectory.relativeFilePath(dir)
|
|
||||||
+ QLatin1String("/"),
|
|
||||||
dir);
|
|
||||||
} else {
|
|
||||||
qzni = std::make_unique<QuaZipNewInfo>(origDirectory.relativeFilePath(dir)
|
|
||||||
+ QLatin1String("/"),
|
|
||||||
dir,
|
|
||||||
options.getDateTime());
|
|
||||||
}
|
|
||||||
if (!dirZipFile.open(QIODevice::WriteOnly, *qzni, nullptr, 0, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dirZipFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each subfolder
|
|
||||||
QFileInfoList subfiles = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot
|
|
||||||
| QDir::Hidden | QDir::Dirs);
|
|
||||||
for (const auto &file : std::as_const(subfiles)) {
|
|
||||||
if (!compressSubDir(
|
|
||||||
zip, file.absoluteFilePath(), origDir, options, total, progress, callback)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each file in directory
|
|
||||||
QFileInfoList files = directory.entryInfoList(QDir::Hidden | QDir::Files);
|
|
||||||
for (const auto &file : std::as_const(files)) {
|
|
||||||
// If it's not a file or it's the compressed file being created
|
|
||||||
if (!file.isFile() || file.absoluteFilePath() == zip->getZipName())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Create relative name for the compressed file
|
|
||||||
QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
|
|
||||||
|
|
||||||
// Compress the file
|
|
||||||
if (!compressFile(zip, file.absoluteFilePath(), filename, options, total, progress, callback)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compressFile(QuaZip *zip,
|
|
||||||
QString fileName,
|
|
||||||
QString fileDest,
|
|
||||||
const JlCompress::Options &options,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtCommon::QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
// zip: object where to add the file
|
|
||||||
// fileName: real file name
|
|
||||||
// fileDest: file name inside the zip object
|
|
||||||
|
|
||||||
if (!zip)
|
|
||||||
return false;
|
|
||||||
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
|
|
||||||
&& zip->getMode() != QuaZip::mdAdd)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QuaZipFile outFile(zip);
|
|
||||||
if (options.getDateTime().isNull()) {
|
|
||||||
if (!outFile.open(QIODevice::WriteOnly,
|
|
||||||
QuaZipNewInfo(fileDest, fileName),
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
options.getCompressionMethod(),
|
|
||||||
options.getCompressionLevel()))
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (!outFile.open(QIODevice::WriteOnly,
|
|
||||||
QuaZipNewInfo(fileDest, fileName, options.getDateTime()),
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
options.getCompressionMethod(),
|
|
||||||
options.getCompressionLevel()))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFileInfo input(fileName);
|
|
||||||
if (quazip_is_symlink(input)) {
|
|
||||||
// Not sure if we should use any specialized codecs here.
|
|
||||||
// After all, a symlink IS just a byte array. And
|
|
||||||
// this is mostly for Linux, where UTF-8 is ubiquitous these days.
|
|
||||||
QString path = quazip_symlink_target(input);
|
|
||||||
QString relativePath = input.dir().relativeFilePath(path);
|
|
||||||
outFile.write(QFile::encodeName(relativePath));
|
|
||||||
} else {
|
|
||||||
QFile inFile;
|
|
||||||
inFile.setFileName(fileName);
|
|
||||||
if (!inFile.open(QIODevice::ReadOnly)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!copyData(inFile, outFile, total, progress, callback) || outFile.getZipError() != UNZ_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
inFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
outFile.close();
|
|
||||||
return outFile.getZipError() == UNZ_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool copyData(QIODevice &inFile,
|
|
||||||
QIODevice &outFile,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
while (!inFile.atEnd()) {
|
|
||||||
char buf[4096];
|
|
||||||
qint64 readLen = inFile.read(buf, 4096);
|
|
||||||
if (readLen <= 0)
|
|
||||||
return false;
|
|
||||||
if (outFile.write(buf, readLen) != readLen)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
progress += readLen;
|
|
||||||
if (!callback(total, progress)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
// Open zip
|
|
||||||
QuaZip zip(fileCompressed);
|
|
||||||
return extractDir(zip, dir, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList extractDir(QuaZip &zip, const QString &dir, QtCommon::QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
if (!zip.open(QuaZip::mdUnzip)) {
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
QString cleanDir = QDir::cleanPath(dir);
|
|
||||||
QDir directory(cleanDir);
|
|
||||||
QString absCleanDir = directory.absolutePath();
|
|
||||||
if (!absCleanDir.endsWith(QLatin1Char('/'))) // It only ends with / if it's the FS root.
|
|
||||||
absCleanDir += QLatin1Char('/');
|
|
||||||
QStringList extracted;
|
|
||||||
if (!zip.goToFirstFile()) {
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t total = 0;
|
|
||||||
for (const QuaZipFileInfo64 &info : zip.getFileInfoList64()) {
|
|
||||||
total += info.uncompressedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t progress = 0;
|
|
||||||
callback(total, progress);
|
|
||||||
|
|
||||||
do {
|
|
||||||
QString name = zip.getCurrentFileName();
|
|
||||||
QString absFilePath = directory.absoluteFilePath(name);
|
|
||||||
QString absCleanPath = QDir::cleanPath(absFilePath);
|
|
||||||
if (!absCleanPath.startsWith(absCleanDir))
|
|
||||||
continue;
|
|
||||||
if (!extractFile(&zip, QLatin1String(""), absFilePath, total, progress, callback)) {
|
|
||||||
removeFile(extracted);
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
extracted.append(absFilePath);
|
|
||||||
} while (zip.goToNextFile());
|
|
||||||
|
|
||||||
// Close zip
|
|
||||||
zip.close();
|
|
||||||
if (zip.getZipError() != 0) {
|
|
||||||
removeFile(extracted);
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return extracted;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool extractFile(QuaZip *zip,
|
|
||||||
QString fileName,
|
|
||||||
QString fileDest,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtCommon::QtProgressCallback callback)
|
|
||||||
{
|
|
||||||
// zip: object where to add the file
|
|
||||||
// filename: real file name
|
|
||||||
// fileincompress: file name of the compressed file
|
|
||||||
|
|
||||||
if (!zip)
|
|
||||||
return false;
|
|
||||||
if (zip->getMode() != QuaZip::mdUnzip)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!fileName.isEmpty())
|
|
||||||
zip->setCurrentFile(fileName);
|
|
||||||
QuaZipFile inFile(zip);
|
|
||||||
if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError() != UNZ_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check existence of resulting file
|
|
||||||
QDir curDir;
|
|
||||||
if (fileDest.endsWith(QLatin1String("/"))) {
|
|
||||||
if (!curDir.mkpath(fileDest)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QuaZipFileInfo64 info;
|
|
||||||
if (!zip->getCurrentFileInfo(&info))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QFile::Permissions srcPerm = info.getPermissions();
|
|
||||||
if (fileDest.endsWith(QLatin1String("/")) && QFileInfo(fileDest).isDir()) {
|
|
||||||
if (srcPerm != 0) {
|
|
||||||
QFile(fileDest).setPermissions(srcPerm);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.isSymbolicLink()) {
|
|
||||||
QString target = QFile::decodeName(inFile.readAll());
|
|
||||||
return QFile::link(target, fileDest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open resulting file
|
|
||||||
QFile outFile;
|
|
||||||
outFile.setFileName(fileDest);
|
|
||||||
if (!outFile.open(QIODevice::WriteOnly))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Copy data
|
|
||||||
if (!copyData(inFile, outFile, total, progress, callback) || inFile.getZipError() != UNZ_OK) {
|
|
||||||
outFile.close();
|
|
||||||
removeFile(QStringList(fileDest));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
outFile.close();
|
|
||||||
|
|
||||||
// Close file
|
|
||||||
inFile.close();
|
|
||||||
if (inFile.getZipError() != UNZ_OK) {
|
|
||||||
removeFile(QStringList(fileDest));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPerm != 0) {
|
|
||||||
outFile.setPermissions(srcPerm);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool removeFile(QStringList listFile)
|
|
||||||
{
|
|
||||||
bool ret = true;
|
|
||||||
// For each file
|
|
||||||
for (int i = 0; i < listFile.count(); i++) {
|
|
||||||
// Remove
|
|
||||||
ret = ret && QFile::remove(listFile.at(i));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtCommon::Compress
|
|
|
@ -1,71 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QString>
|
|
||||||
#include <JlCompress.h>
|
|
||||||
#include "qt_common/qt_common.h"
|
|
||||||
|
|
||||||
/** This is a modified version of JlCompress **/
|
|
||||||
namespace QtCommon::Compress {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compress an entire directory and report its progress.
|
|
||||||
* @param fileCompressed Destination file
|
|
||||||
* @param dir The directory to compress
|
|
||||||
* @param options Compression level, etc
|
|
||||||
* @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
|
|
||||||
*/
|
|
||||||
bool compressDir(QString fileCompressed,
|
|
||||||
QString dir,
|
|
||||||
const JlCompress::Options& options = JlCompress::Options(),
|
|
||||||
QtCommon::QtProgressCallback callback = {});
|
|
||||||
|
|
||||||
// Internal //
|
|
||||||
bool compressSubDir(QuaZip *zip,
|
|
||||||
QString dir,
|
|
||||||
QString origDir,
|
|
||||||
const JlCompress::Options &options,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtCommon::QtProgressCallback callback);
|
|
||||||
|
|
||||||
bool compressFile(QuaZip *zip,
|
|
||||||
QString fileName,
|
|
||||||
QString fileDest,
|
|
||||||
const JlCompress::Options &options,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtCommon::QtProgressCallback callback);
|
|
||||||
|
|
||||||
bool copyData(QIODevice &inFile,
|
|
||||||
QIODevice &outFile,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtCommon::QtProgressCallback callback);
|
|
||||||
|
|
||||||
// Extract //
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Extract a zip file and report its progress.
|
|
||||||
* @param fileCompressed Compressed file
|
|
||||||
* @param dir The directory to push the results to
|
|
||||||
* @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
|
|
||||||
*/
|
|
||||||
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback = {});
|
|
||||||
|
|
||||||
// Internal //
|
|
||||||
QStringList extractDir(QuaZip &zip, const QString &dir, QtCommon::QtProgressCallback callback);
|
|
||||||
|
|
||||||
bool extractFile(QuaZip *zip,
|
|
||||||
QString fileName,
|
|
||||||
QString fileDest,
|
|
||||||
std::size_t total,
|
|
||||||
std::size_t &progress,
|
|
||||||
QtCommon::QtProgressCallback callback);
|
|
||||||
|
|
||||||
bool removeFile(QStringList listFile);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,22 +1,17 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "qt_common/qt_game_util.h"
|
||||||
#include "qt_content_util.h"
|
#include "qt_content_util.h"
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "frontend_common/content_manager.h"
|
#include "frontend_common/content_manager.h"
|
||||||
#include "frontend_common/data_manager.h"
|
|
||||||
#include "frontend_common/firmware_manager.h"
|
#include "frontend_common/firmware_manager.h"
|
||||||
#include "qt_common/qt_common.h"
|
#include "qt_common/qt_common.h"
|
||||||
#include "qt_common/qt_compress.h"
|
|
||||||
#include "qt_common/qt_game_util.h"
|
|
||||||
#include "qt_common/qt_progress_dialog.h"
|
#include "qt_common/qt_progress_dialog.h"
|
||||||
#include "qt_frontend_util.h"
|
#include "qt_frontend_util.h"
|
||||||
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include <JlCompress.h>
|
#include <JlCompress.h>
|
||||||
#include <qfuturewatcher.h>
|
|
||||||
|
|
||||||
namespace QtCommon::Content {
|
namespace QtCommon::Content {
|
||||||
|
|
||||||
|
@ -26,10 +21,10 @@ bool CheckGameFirmware(u64 program_id, QObject* parent)
|
||||||
&& !FirmwareManager::CheckFirmwarePresence(*system)) {
|
&& !FirmwareManager::CheckFirmwarePresence(*system)) {
|
||||||
auto result = QtCommon::Frontend::ShowMessage(
|
auto result = QtCommon::Frontend::ShowMessage(
|
||||||
QMessageBox::Warning,
|
QMessageBox::Warning,
|
||||||
tr("Game Requires Firmware"),
|
"Game Requires Firmware",
|
||||||
tr("The game you are trying to launch requires firmware to boot or to get past the "
|
"The game you are trying to launch requires firmware to boot or to get past the "
|
||||||
"opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>"
|
"opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>"
|
||||||
"dump and install firmware</a>, or press \"OK\" to launch anyways."),
|
"dump and install firmware</a>, or press \"OK\" to launch anyways.",
|
||||||
QMessageBox::Ok | QMessageBox::Cancel,
|
QMessageBox::Ok | QMessageBox::Cancel,
|
||||||
parent);
|
parent);
|
||||||
|
|
||||||
|
@ -65,8 +60,8 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
|
|
||||||
const auto ShowMessage = [&]() {
|
const auto ShowMessage = [&]() {
|
||||||
QtCommon::Frontend::ShowMessage(icon,
|
QtCommon::Frontend::ShowMessage(icon,
|
||||||
tr(failedTitle),
|
failedTitle,
|
||||||
tr(GetFirmwareInstallResultString(result)));
|
GetFirmwareInstallResultString(result));
|
||||||
};
|
};
|
||||||
|
|
||||||
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
|
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
|
||||||
|
@ -130,8 +125,8 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
i++;
|
i++;
|
||||||
auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
|
auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
|
||||||
FileSys::OpenMode::Read);
|
FileSys::OpenMode::Read);
|
||||||
auto firmware_dst_vfile = firmware_vdir->CreateFileRelative(
|
auto firmware_dst_vfile = firmware_vdir
|
||||||
firmware_src_path.filename().string());
|
->CreateFileRelative(firmware_src_path.filename().string());
|
||||||
|
|
||||||
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
|
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
|
||||||
LOG_ERROR(Frontend,
|
LOG_ERROR(Frontend,
|
||||||
|
@ -173,9 +168,9 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
const auto failed_names = QString::fromStdString(
|
const auto failed_names = QString::fromStdString(
|
||||||
fmt::format("{}", fmt::join(results, "\n")));
|
fmt::format("{}", fmt::join(results, "\n")));
|
||||||
progress.close();
|
progress.close();
|
||||||
QtCommon::Frontend::Critical(
|
QtCommon::Frontend::Critical(tr("Firmware integrity verification failed!"),
|
||||||
tr("Firmware integrity verification failed!"),
|
tr("Verification failed for the following files:\n\n%1")
|
||||||
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
|
.arg(failed_names));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,10 +181,10 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
const std::string display_version(firmware_data.display_version.data());
|
const std::string display_version(firmware_data.display_version.data());
|
||||||
|
|
||||||
result = FirmwareInstallResult::Success;
|
result = FirmwareInstallResult::Success;
|
||||||
QtCommon::Frontend::Information(
|
QtCommon::Frontend::Information(rootObject,
|
||||||
rootObject,
|
|
||||||
tr(successTitle),
|
tr(successTitle),
|
||||||
tr(GetFirmwareInstallResultString(result)).arg(QString::fromStdString(display_version)));
|
tr(GetFirmwareInstallResultString(result))
|
||||||
|
.arg(QString::fromStdString(display_version)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UnzipFirmwareToTmp(const QString& location)
|
QString UnzipFirmwareToTmp(const QString& location)
|
||||||
|
@ -198,7 +193,7 @@ QString UnzipFirmwareToTmp(const QString& location)
|
||||||
fs::path tmp{fs::temp_directory_path()};
|
fs::path tmp{fs::temp_directory_path()};
|
||||||
|
|
||||||
if (!fs::create_directories(tmp / "eden" / "firmware")) {
|
if (!fs::create_directories(tmp / "eden" / "firmware")) {
|
||||||
return QString();
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp /= "eden";
|
tmp /= "eden";
|
||||||
|
@ -210,7 +205,7 @@ QString UnzipFirmwareToTmp(const QString& location)
|
||||||
|
|
||||||
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
|
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
return QString();
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return qCacheDir;
|
return qCacheDir;
|
||||||
|
@ -269,8 +264,9 @@ void InstallKeys()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FirmwareManager::KeyInstallResult result
|
FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location
|
||||||
= FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys");
|
.toStdString(),
|
||||||
|
"keys");
|
||||||
|
|
||||||
system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
||||||
|
|
||||||
|
@ -286,14 +282,9 @@ void InstallKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyInstalledContents()
|
void VerifyInstalledContents() {
|
||||||
{
|
|
||||||
// Initialize a progress dialog.
|
// Initialize a progress dialog.
|
||||||
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
|
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, QtCommon::rootObject);
|
||||||
tr("Cancel"),
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
QtCommon::rootObject);
|
|
||||||
progress.setWindowModality(Qt::WindowModal);
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
progress.setMinimumDuration(100);
|
progress.setMinimumDuration(100);
|
||||||
progress.setAutoClose(false);
|
progress.setAutoClose(false);
|
||||||
|
@ -305,17 +296,16 @@ void VerifyInstalledContents()
|
||||||
return progress.wasCanceled();
|
return progress.wasCanceled();
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<std::string> result
|
const std::vector<std::string> result =
|
||||||
= ContentManager::VerifyInstalledContents(*QtCommon::system,
|
ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback);
|
||||||
*QtCommon::provider,
|
|
||||||
QtProgressCallback);
|
|
||||||
progress.close();
|
progress.close();
|
||||||
|
|
||||||
if (result.empty()) {
|
if (result.empty()) {
|
||||||
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
|
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
|
||||||
tr("The operation completed successfully."));
|
tr("The operation completed successfully."));
|
||||||
} else {
|
} else {
|
||||||
const auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
|
const auto failed_names =
|
||||||
|
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
|
||||||
QtCommon::Frontend::Critical(
|
QtCommon::Frontend::Critical(
|
||||||
tr("Integrity verification failed!"),
|
tr("Integrity verification failed!"),
|
||||||
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
|
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
|
||||||
|
@ -342,7 +332,7 @@ void FixProfiles()
|
||||||
qorphaned.reserve(8 * 33);
|
qorphaned.reserve(8 * 33);
|
||||||
|
|
||||||
for (const std::string& s : orphaned) {
|
for (const std::string& s : orphaned) {
|
||||||
qorphaned = qorphaned % QStringLiteral("\n") % QString::fromStdString(s);
|
qorphaned += "\n" + QString::fromStdString(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
QtCommon::Frontend::Critical(
|
QtCommon::Frontend::Critical(
|
||||||
|
@ -358,183 +348,4 @@ void FixProfiles()
|
||||||
QtCommon::Game::OpenSaveFolder();
|
QtCommon::Game::OpenSaveFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id)
|
|
||||||
{
|
|
||||||
auto result = QtCommon::Frontend::Warning(tr("Really clear data?"),
|
|
||||||
tr("Important data may be lost!"),
|
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
|
||||||
|
|
||||||
if (result != QMessageBox::Yes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
result = QtCommon::Frontend::Warning(
|
|
||||||
tr("Are you REALLY sure?"),
|
|
||||||
tr("Once deleted, your data will NOT come back!\n"
|
|
||||||
"Only do this if you're 100% sure you want to delete this data."),
|
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
|
||||||
|
|
||||||
if (result != QMessageBox::Yes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QtCommon::Frontend::QtProgressDialog dialog(tr("Clearing..."), QString(), 0, 0);
|
|
||||||
dialog.show();
|
|
||||||
|
|
||||||
FrontendCommon::DataManager::ClearDir(dir, user_id);
|
|
||||||
|
|
||||||
dialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir,
|
|
||||||
const std::string& user_id,
|
|
||||||
std::function<void()> callback)
|
|
||||||
{
|
|
||||||
using namespace QtCommon::Frontend;
|
|
||||||
const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir, user_id);
|
|
||||||
|
|
||||||
const QString zip_dump_location = GetSaveFileName(tr("Select Export Location"),
|
|
||||||
QStringLiteral("export.zip"),
|
|
||||||
tr("Zipped Archives (*.zip)"));
|
|
||||||
|
|
||||||
if (zip_dump_location.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QtProgressDialog* progress = new QtProgressDialog(
|
|
||||||
tr("Exporting data. This may take a while..."), tr("Cancel"), 0, 100, rootObject);
|
|
||||||
|
|
||||||
progress->setWindowTitle(tr("Exporting"));
|
|
||||||
progress->setWindowModality(Qt::WindowModal);
|
|
||||||
progress->setMinimumDuration(100);
|
|
||||||
progress->setAutoClose(false);
|
|
||||||
progress->setAutoReset(false);
|
|
||||||
progress->show();
|
|
||||||
|
|
||||||
QGuiApplication::processEvents();
|
|
||||||
|
|
||||||
auto progress_callback = [=](size_t total_size, size_t processed_size) {
|
|
||||||
QMetaObject::invokeMethod(progress,
|
|
||||||
&QtProgressDialog::setValue,
|
|
||||||
static_cast<int>((processed_size * 100) / total_size));
|
|
||||||
|
|
||||||
return !progress->wasCanceled();
|
|
||||||
};
|
|
||||||
|
|
||||||
QFuture<bool> future = QtConcurrent::run([=]() {
|
|
||||||
return QtCommon::Compress::compressDir(zip_dump_location,
|
|
||||||
QString::fromStdString(dir),
|
|
||||||
JlCompress::Options(),
|
|
||||||
progress_callback);
|
|
||||||
});
|
|
||||||
|
|
||||||
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
|
|
||||||
|
|
||||||
QObject::connect(watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
|
|
||||||
progress->close();
|
|
||||||
|
|
||||||
if (watcher->result()) {
|
|
||||||
Information(tr("Exported Successfully"), tr("Data was exported successfully."));
|
|
||||||
} else if (progress->wasCanceled()) {
|
|
||||||
Information(tr("Export Cancelled"), tr("Export was cancelled by the user."));
|
|
||||||
} else {
|
|
||||||
Critical(
|
|
||||||
tr("Export Failed"),
|
|
||||||
tr("Ensure you have write permissions on the targeted directory and try again."));
|
|
||||||
}
|
|
||||||
|
|
||||||
progress->deleteLater();
|
|
||||||
watcher->deleteLater();
|
|
||||||
if (callback)
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher->setFuture(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir,
|
|
||||||
const std::string& user_id,
|
|
||||||
std::function<void()> callback)
|
|
||||||
{
|
|
||||||
const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir, user_id);
|
|
||||||
|
|
||||||
using namespace QtCommon::Frontend;
|
|
||||||
|
|
||||||
const QString zip_dump_location = GetOpenFileName(tr("Select Import Location"),
|
|
||||||
{},
|
|
||||||
tr("Zipped Archives (*.zip)"));
|
|
||||||
|
|
||||||
if (zip_dump_location.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
StandardButton button = Warning(
|
|
||||||
tr("Import Warning"),
|
|
||||||
tr("All previous data in this directory will be deleted. Are you sure you wish to "
|
|
||||||
"proceed?"),
|
|
||||||
StandardButton::Yes | StandardButton::No);
|
|
||||||
|
|
||||||
if (button != QMessageBox::Yes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QtProgressDialog* progress = new QtProgressDialog(
|
|
||||||
tr("Importing data. This may take a while..."), tr("Cancel"), 0, 100, rootObject);
|
|
||||||
|
|
||||||
progress->setWindowTitle(tr("Importing"));
|
|
||||||
progress->setWindowModality(Qt::WindowModal);
|
|
||||||
progress->setMinimumDuration(100);
|
|
||||||
progress->setAutoClose(false);
|
|
||||||
progress->setAutoReset(false);
|
|
||||||
progress->show();
|
|
||||||
progress->setValue(0);
|
|
||||||
|
|
||||||
QGuiApplication::processEvents();
|
|
||||||
|
|
||||||
// to prevent GUI mangling we have to run this in a thread as well
|
|
||||||
QFuture<bool> delete_future = QtConcurrent::run([=]() {
|
|
||||||
FrontendCommon::DataManager::ClearDir(data_dir, user_id);
|
|
||||||
return !progress->wasCanceled();
|
|
||||||
});
|
|
||||||
|
|
||||||
QFutureWatcher<bool>* delete_watcher = new QFutureWatcher<bool>(rootObject);
|
|
||||||
delete_watcher->setFuture(delete_future);
|
|
||||||
|
|
||||||
QObject::connect(delete_watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
|
|
||||||
auto progress_callback = [=](size_t total_size, size_t processed_size) {
|
|
||||||
QMetaObject::invokeMethod(progress,
|
|
||||||
&QtProgressDialog::setValue,
|
|
||||||
static_cast<int>((processed_size * 100) / total_size));
|
|
||||||
|
|
||||||
return !progress->wasCanceled();
|
|
||||||
};
|
|
||||||
|
|
||||||
QFuture<bool> future = QtConcurrent::run([=]() {
|
|
||||||
return !QtCommon::Compress::extractDir(zip_dump_location,
|
|
||||||
QString::fromStdString(dir),
|
|
||||||
progress_callback)
|
|
||||||
.empty();
|
|
||||||
});
|
|
||||||
|
|
||||||
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
|
|
||||||
|
|
||||||
QObject::connect(watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
|
|
||||||
progress->close();
|
|
||||||
|
|
||||||
// this sucks
|
|
||||||
if (watcher->result()) {
|
|
||||||
Information(tr("Imported Successfully"), tr("Data was imported successfully."));
|
|
||||||
} else if (progress->wasCanceled()) {
|
|
||||||
Information(tr("Import Cancelled"), tr("Import was cancelled by the user."));
|
|
||||||
} else {
|
|
||||||
Critical(tr("Import Failed"),
|
|
||||||
tr("Ensure you have read permissions on the targeted directory and try "
|
|
||||||
"again."));
|
|
||||||
}
|
|
||||||
|
|
||||||
progress->deleteLater();
|
|
||||||
watcher->deleteLater();
|
|
||||||
if (callback)
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher->setFuture(future);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtCommon::Content
|
} // namespace QtCommon::Content
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend_common/data_manager.h"
|
|
||||||
|
|
||||||
namespace QtCommon::Content {
|
namespace QtCommon::Content {
|
||||||
|
|
||||||
|
@ -47,10 +46,6 @@ void InstallKeys();
|
||||||
void VerifyGameContents(const std::string &game_path);
|
void VerifyGameContents(const std::string &game_path);
|
||||||
void VerifyInstalledContents();
|
void VerifyInstalledContents();
|
||||||
|
|
||||||
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "");
|
|
||||||
void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
|
|
||||||
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
|
|
||||||
|
|
||||||
// Profiles //
|
// Profiles //
|
||||||
void FixProfiles();
|
void FixProfiles();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,15 +32,4 @@ const QString GetOpenFileName(const QString &title,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString GetSaveFileName(const QString &title,
|
|
||||||
const QString &dir,
|
|
||||||
const QString &filter,
|
|
||||||
QString *selectedFilter,
|
|
||||||
Options options)
|
|
||||||
{
|
|
||||||
#ifdef YUZU_QT_WIDGETS
|
|
||||||
return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtCommon::Frontend
|
} // namespace QtCommon::Frontend
|
||||||
|
|
|
@ -110,6 +110,21 @@ StandardButton ShowMessage(Icon icon,
|
||||||
{ \
|
{ \
|
||||||
return ShowMessage(Icon::level, title, text, buttons, parent); \
|
return ShowMessage(Icon::level, title, text, buttons, parent); \
|
||||||
} \
|
} \
|
||||||
|
inline StandardButton level(QObject *parent, \
|
||||||
|
const char *title, \
|
||||||
|
const char *text, \
|
||||||
|
StandardButtons buttons \
|
||||||
|
= StandardButton::Ok) \
|
||||||
|
{ \
|
||||||
|
return ShowMessage(Icon::level, tr(title), tr(text), buttons, parent); \
|
||||||
|
} \
|
||||||
|
inline StandardButton level(const char *title, \
|
||||||
|
const char *text, \
|
||||||
|
StandardButtons buttons \
|
||||||
|
= StandardButton::Ok) \
|
||||||
|
{ \
|
||||||
|
return ShowMessage(Icon::level, tr(title), tr(text), buttons, rootObject); \
|
||||||
|
} \
|
||||||
inline StandardButton level(const QString title, \
|
inline StandardButton level(const QString title, \
|
||||||
const QString &text, \
|
const QString &text, \
|
||||||
StandardButtons buttons \
|
StandardButtons buttons \
|
||||||
|
@ -129,11 +144,5 @@ const QString GetOpenFileName(const QString &title,
|
||||||
QString *selectedFilter = nullptr,
|
QString *selectedFilter = nullptr,
|
||||||
Options options = Options());
|
Options options = Options());
|
||||||
|
|
||||||
const QString GetSaveFileName(const QString &title,
|
|
||||||
const QString &dir,
|
|
||||||
const QString &filter,
|
|
||||||
QString *selectedFilter = nullptr,
|
|
||||||
Options options = Options());
|
|
||||||
|
|
||||||
} // namespace QtCommon::Frontend
|
} // namespace QtCommon::Frontend
|
||||||
#endif // QT_FRONTEND_UTIL_H
|
#endif // QT_FRONTEND_UTIL_H
|
||||||
|
|
|
@ -220,8 +220,8 @@ void RemoveBaseContent(u64 program_id, InstalledEntryType type)
|
||||||
program_id);
|
program_id);
|
||||||
if (res) {
|
if (res) {
|
||||||
QtCommon::Frontend::Information(rootObject,
|
QtCommon::Frontend::Information(rootObject,
|
||||||
tr("Successfully Removed"),
|
"Successfully Removed",
|
||||||
tr("Successfully removed the installed base game."));
|
"Successfully removed the installed base game.");
|
||||||
} else {
|
} else {
|
||||||
QtCommon::Frontend::Warning(
|
QtCommon::Frontend::Warning(
|
||||||
rootObject,
|
rootObject,
|
||||||
|
@ -235,8 +235,8 @@ void RemoveUpdateContent(u64 program_id, InstalledEntryType type)
|
||||||
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
|
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
|
||||||
if (res) {
|
if (res) {
|
||||||
QtCommon::Frontend::Information(rootObject,
|
QtCommon::Frontend::Information(rootObject,
|
||||||
tr("Successfully Removed"),
|
"Successfully Removed",
|
||||||
tr("Successfully removed the installed update."));
|
"Successfully removed the installed update.");
|
||||||
} else {
|
} else {
|
||||||
QtCommon::Frontend::Warning(rootObject,
|
QtCommon::Frontend::Warning(rootObject,
|
||||||
GetGameListErrorRemoving(type),
|
GetGameListErrorRemoving(type),
|
||||||
|
|
|
@ -17,12 +17,7 @@ bool OpenShaderCache(u64 program_id, QObject *parent)
|
||||||
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
||||||
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
|
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
|
||||||
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
|
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
|
||||||
QtCommon::Frontend::ShowMessage(QMessageBox::Warning,
|
QtCommon::Frontend::ShowMessage(QMessageBox::Warning, "Error Opening Shader Cache", "Failed to create or open shader cache for this title, ensure your app data directory has write permissions.", QMessageBox::Ok, parent);
|
||||||
tr("Error Opening Shader Cache"),
|
|
||||||
tr("Failed to create or open shader cache for this title, "
|
|
||||||
"ensure your app data directory has write permissions."),
|
|
||||||
QMessageBox::Ok,
|
|
||||||
parent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
|
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
#include <frozen/unordered_map.h>
|
|
||||||
#include <qobjectdefs.h>
|
|
||||||
#include <qtmetamacros.h>
|
|
||||||
|
|
||||||
namespace QtCommon::StringLookup {
|
|
||||||
|
|
||||||
Q_NAMESPACE
|
|
||||||
|
|
||||||
// TODO(crueter): QML interface
|
|
||||||
enum StringKey {
|
|
||||||
SavesTooltip,
|
|
||||||
ShadersTooltip,
|
|
||||||
UserNandTooltip,
|
|
||||||
SysNandTooltip,
|
|
||||||
ModsTooltip,
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr const frozen::unordered_map<StringKey, frozen::string, 5> strings = {
|
|
||||||
{SavesTooltip, "Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING!"},
|
|
||||||
{ShadersTooltip, "Contains Vulkan and OpenGL pipeline caches. Generally safe to remove."},
|
|
||||||
{UserNandTooltip, "Contains updates and DLC for games."},
|
|
||||||
{SysNandTooltip, "Contains firmware and applet data."},
|
|
||||||
{ModsTooltip, "Contains game mods, patches, and cheats."},
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline const QString Lookup(StringKey key)
|
|
||||||
{
|
|
||||||
return QString::fromStdString(strings.at(key).data());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -386,11 +386,9 @@ void BufferCache<P>::BindHostComputeBuffers() {
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
|
void BufferCache<P>::SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
|
||||||
const UniformBufferSizes* sizes) {
|
const UniformBufferSizes* sizes) {
|
||||||
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
|
||||||
if (channel_state->enabled_uniform_buffer_masks != mask) {
|
if (channel_state->enabled_uniform_buffer_masks != mask) {
|
||||||
if constexpr (IS_OPENGL) {
|
|
||||||
channel_state->fast_bound_uniform_buffers.fill(0);
|
channel_state->fast_bound_uniform_buffers.fill(0);
|
||||||
}
|
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
||||||
channel_state->dirty_uniform_buffers.fill(~u32{0});
|
channel_state->dirty_uniform_buffers.fill(~u32{0});
|
||||||
channel_state->uniform_buffer_binding_sizes.fill({});
|
channel_state->uniform_buffer_binding_sizes.fill({});
|
||||||
}
|
}
|
||||||
|
@ -806,7 +804,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size;
|
channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size;
|
||||||
if (should_fast_bind) {
|
if (should_fast_bind) {
|
||||||
// We only have to bind when the currently bound buffer is not the fast version
|
// We only have to bind when the currently bound buffer is not the fast version
|
||||||
channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index;
|
channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index;
|
||||||
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
||||||
runtime.BindFastUniformBuffer(stage, binding_index, size);
|
runtime.BindFastUniformBuffer(stage, binding_index, size);
|
||||||
}
|
}
|
||||||
|
@ -815,10 +813,8 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (IS_OPENGL) {
|
channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index;
|
||||||
channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index;
|
|
||||||
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
||||||
}
|
|
||||||
// Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan
|
// Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan
|
||||||
const std::span<u8> span = runtime.BindMappedUniformBuffer(stage, binding_index, size);
|
const std::span<u8> span = runtime.BindMappedUniformBuffer(stage, binding_index, size);
|
||||||
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
||||||
|
@ -839,9 +835,6 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
}
|
}
|
||||||
const u32 offset = buffer.Offset(device_addr);
|
const u32 offset = buffer.Offset(device_addr);
|
||||||
if constexpr (IS_OPENGL) {
|
if constexpr (IS_OPENGL) {
|
||||||
// Fast buffer will be unbound
|
|
||||||
channel_state->fast_bound_uniform_buffers[stage] &= ~(1U << binding_index);
|
|
||||||
|
|
||||||
// Mark the index as dirty if offset doesn't match
|
// Mark the index as dirty if offset doesn't match
|
||||||
const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset();
|
const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset();
|
||||||
channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index;
|
channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index;
|
||||||
|
@ -855,6 +848,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
} else {
|
} else {
|
||||||
runtime.BindUniformBuffer(buffer, offset, size);
|
runtime.BindUniformBuffer(buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
|
@ -1789,12 +1783,7 @@ std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) {
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept {
|
bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept {
|
||||||
if constexpr (IS_OPENGL) {
|
return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1u) != 0;
|
||||||
return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1) != 0;
|
|
||||||
} else {
|
|
||||||
// Only OpenGL has fast uniform buffers
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
|
|
|
@ -54,6 +54,8 @@ constexpr u32 NUM_STORAGE_BUFFERS = 16;
|
||||||
constexpr u32 NUM_TEXTURE_BUFFERS = 32;
|
constexpr u32 NUM_TEXTURE_BUFFERS = 32;
|
||||||
constexpr u32 NUM_STAGES = 5;
|
constexpr u32 NUM_STAGES = 5;
|
||||||
|
|
||||||
|
static_assert(NUM_GRAPHICS_UNIFORM_BUFFERS <= 32, "fast bitmask must fit u32");
|
||||||
|
|
||||||
using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
|
using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
|
||||||
using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
|
using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
|
||||||
|
|
||||||
|
@ -137,8 +139,8 @@ public:
|
||||||
u32 written_compute_texture_buffers = 0;
|
u32 written_compute_texture_buffers = 0;
|
||||||
u32 image_compute_texture_buffers = 0;
|
u32 image_compute_texture_buffers = 0;
|
||||||
|
|
||||||
std::array<u32, 16> uniform_cache_hits{};
|
std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS> uniform_cache_hits{};
|
||||||
std::array<u32, 16> uniform_cache_shots{};
|
std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS> uniform_cache_shots{};
|
||||||
|
|
||||||
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
|
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
|
||||||
|
|
||||||
|
|
|
@ -234,9 +234,6 @@ add_executable(yuzu
|
||||||
deps_dialog.cpp
|
deps_dialog.cpp
|
||||||
deps_dialog.h
|
deps_dialog.h
|
||||||
deps_dialog.ui
|
deps_dialog.ui
|
||||||
|
|
||||||
data_dialog.h data_dialog.cpp data_dialog.ui
|
|
||||||
data_widget.ui
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
||||||
|
@ -393,13 +390,32 @@ endif()
|
||||||
|
|
||||||
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
|
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
|
||||||
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
|
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Concurrent)
|
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
target_link_libraries(yuzu PRIVATE Qt6::DBus)
|
target_link_libraries(yuzu PRIVATE Qt6::DBus)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_compile_definitions(yuzu PRIVATE
|
||||||
|
# Use QStringBuilder for string concatenation to reduce
|
||||||
|
# the overall number of temporary strings created.
|
||||||
|
QT_USE_QSTRINGBUILDER
|
||||||
|
|
||||||
|
# Disable implicit conversions from/to C strings
|
||||||
|
QT_NO_CAST_FROM_ASCII
|
||||||
|
QT_NO_CAST_TO_ASCII
|
||||||
|
|
||||||
|
# Disable implicit type narrowing in signal/slot connect() calls.
|
||||||
|
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||||
|
|
||||||
|
# Disable unsafe overloads of QProcess' start() function.
|
||||||
|
QT_NO_PROCESS_COMBINED_ARGUMENT_START
|
||||||
|
|
||||||
|
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
||||||
|
QT_NO_URL_CAST_FROM_STRING
|
||||||
|
)
|
||||||
|
|
||||||
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||||
target_compile_definitions(yuzu PRIVATE YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
target_compile_definitions(yuzu PRIVATE YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <qtconcurrentrun.h>
|
#include <QtConcurrent/qtconcurrentrun.h>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "ui_compatdb.h"
|
#include "ui_compatdb.h"
|
||||||
#include "yuzu/compatdb.h"
|
#include "yuzu/compatdb.h"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <QRegExpValidator>
|
#include <QRegExpValidator>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "ui_configure_web.h"
|
#include "ui_configure_web.h"
|
||||||
#include "qt_common/uisettings.h"
|
#include "qt_common/uisettings.h"
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "data_dialog.h"
|
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
|
||||||
#include "frontend_common/data_manager.h"
|
|
||||||
#include "qt_common/qt_content_util.h"
|
|
||||||
#include "qt_common/qt_frontend_util.h"
|
|
||||||
#include "qt_common/qt_progress_dialog.h"
|
|
||||||
#include "qt_common/qt_string_lookup.h"
|
|
||||||
#include "ui_data_dialog.h"
|
|
||||||
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QProgressDialog>
|
|
||||||
#include <QtConcurrentRun>
|
|
||||||
|
|
||||||
#include <core/frontend/applets/profile_select.h>
|
|
||||||
|
|
||||||
#include <applets/qt_profile_select.h>
|
|
||||||
|
|
||||||
DataDialog::DataDialog(QWidget *parent)
|
|
||||||
: QDialog(parent)
|
|
||||||
, ui(std::make_unique<Ui::DataDialog>())
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
// TODO: Should we make this a single widget that pulls data from a model?
|
|
||||||
#define WIDGET(name) \
|
|
||||||
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
|
|
||||||
QtCommon::StringLookup::name##Tooltip, \
|
|
||||||
this));
|
|
||||||
|
|
||||||
WIDGET(Saves)
|
|
||||||
WIDGET(Shaders)
|
|
||||||
WIDGET(UserNand)
|
|
||||||
WIDGET(SysNand)
|
|
||||||
WIDGET(Mods)
|
|
||||||
|
|
||||||
#undef WIDGET
|
|
||||||
|
|
||||||
connect(ui->labels, &QListWidget::itemSelectionChanged, this, [this]() {
|
|
||||||
const auto items = ui->labels->selectedItems();
|
|
||||||
if (items.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->page->setCurrentIndex(ui->labels->row(items[0]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
DataDialog::~DataDialog() = default;
|
|
||||||
|
|
||||||
DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
|
|
||||||
QtCommon::StringLookup::StringKey tooltip,
|
|
||||||
QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, ui(std::make_unique<Ui::DataWidget>())
|
|
||||||
, m_dir(data_dir)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
ui->tooltip->setText(QtCommon::StringLookup::Lookup(tooltip));
|
|
||||||
|
|
||||||
ui->clear->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
|
|
||||||
ui->open->setIcon(QIcon::fromTheme(QStringLiteral("folder")));
|
|
||||||
ui->upload->setIcon(QIcon::fromTheme(QStringLiteral("upload")));
|
|
||||||
ui->download->setIcon(QIcon::fromTheme(QStringLiteral("download")));
|
|
||||||
|
|
||||||
connect(ui->clear, &QPushButton::clicked, this, &DataWidget::clear);
|
|
||||||
connect(ui->open, &QPushButton::clicked, this, &DataWidget::open);
|
|
||||||
connect(ui->upload, &QPushButton::clicked, this, &DataWidget::upload);
|
|
||||||
connect(ui->download, &QPushButton::clicked, this, &DataWidget::download);
|
|
||||||
|
|
||||||
scan();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWidget::clear()
|
|
||||||
{
|
|
||||||
std::string user_id{};
|
|
||||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
|
||||||
user_id = selectProfile();
|
|
||||||
}
|
|
||||||
QtCommon::Content::ClearDataDir(m_dir, user_id);
|
|
||||||
scan();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWidget::open()
|
|
||||||
{
|
|
||||||
std::string user_id{};
|
|
||||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
|
||||||
user_id = selectProfile();
|
|
||||||
}
|
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
|
||||||
QString::fromStdString(FrontendCommon::DataManager::GetDataDir(m_dir, user_id))));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWidget::upload()
|
|
||||||
{
|
|
||||||
std::string user_id{};
|
|
||||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
|
||||||
user_id = selectProfile();
|
|
||||||
}
|
|
||||||
QtCommon::Content::ExportDataDir(m_dir, user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWidget::download()
|
|
||||||
{
|
|
||||||
std::string user_id{};
|
|
||||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
|
||||||
user_id = selectProfile();
|
|
||||||
}
|
|
||||||
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWidget::scan() {
|
|
||||||
ui->size->setText(tr("Calculating..."));
|
|
||||||
|
|
||||||
QFutureWatcher<u64> *watcher = new QFutureWatcher<u64>(this);
|
|
||||||
|
|
||||||
connect(watcher, &QFutureWatcher<u64>::finished, this, [=, this]() {
|
|
||||||
u64 size = watcher->result();
|
|
||||||
ui->size->setText(
|
|
||||||
QString::fromStdString(FrontendCommon::DataManager::ReadableBytesSize(size)));
|
|
||||||
watcher->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher->setFuture(
|
|
||||||
QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); }));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DataWidget::selectProfile()
|
|
||||||
{
|
|
||||||
const auto select_profile = [this] {
|
|
||||||
const Core::Frontend::ProfileSelectParameters parameters{
|
|
||||||
.mode = Service::AM::Frontend::UiMode::UserSelector,
|
|
||||||
.invalid_uid_list = {},
|
|
||||||
.display_options = {},
|
|
||||||
.purpose = Service::AM::Frontend::UserSelectionPurpose::General,
|
|
||||||
};
|
|
||||||
QtProfileSelectionDialog dialog(*QtCommon::system, this, parameters);
|
|
||||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint
|
|
||||||
| Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
|
||||||
dialog.setWindowModality(Qt::WindowModal);
|
|
||||||
|
|
||||||
if (dialog.exec() == QDialog::Rejected) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dialog.GetIndex();
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto index = select_profile();
|
|
||||||
if (index == -1) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto uuid = QtCommon::system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
|
|
||||||
ASSERT(uuid);
|
|
||||||
|
|
||||||
const auto user_id = uuid->AsU128();
|
|
||||||
|
|
||||||
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#ifndef DATA_DIALOG_H
|
|
||||||
#define DATA_DIALOG_H
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include "frontend_common/data_manager.h"
|
|
||||||
#include "qt_common/qt_string_lookup.h"
|
|
||||||
|
|
||||||
#include "ui_data_widget.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class DataDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DataDialog : public QDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit DataDialog(QWidget *parent = nullptr);
|
|
||||||
~DataDialog();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Ui::DataDialog> ui;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DataWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DataWidget(FrontendCommon::DataManager::DataDir data_dir,
|
|
||||||
QtCommon::StringLookup::StringKey tooltip,
|
|
||||||
QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void clear();
|
|
||||||
void open();
|
|
||||||
void upload();
|
|
||||||
void download();
|
|
||||||
|
|
||||||
void scan();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Ui::DataWidget> ui;
|
|
||||||
FrontendCommon::DataManager::DataDir m_dir;
|
|
||||||
|
|
||||||
std::string selectProfile();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DATA_DIALOG_H
|
|
|
@ -1,148 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>DataDialog</class>
|
|
||||||
<widget class="QDialog" name="DataDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>480</width>
|
|
||||||
<height>320</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>300</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Data Manager</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
|
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="labels">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Saves</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Shaders</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>UserNAND</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>SysNAND</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Mods</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QStackedWidget" name="page">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>275</width>
|
|
||||||
<height>200</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>-1</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Deleting ANY data is IRREVERSABLE!</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::StandardButton::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>DataDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>DataDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
|
@ -1,205 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>DataWidget</class>
|
|
||||||
<widget class="QWidget" name="DataWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>275</width>
|
|
||||||
<height>200</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="tooltip">
|
|
||||||
<property name="text">
|
|
||||||
<string>Tooltip</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="size">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>10</pointsize>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">Size</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="open">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>1</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>52</width>
|
|
||||||
<height>47</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Open with your system file manager</string>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true">QPushButton {
|
|
||||||
max-width: 50px;
|
|
||||||
max-height: 45px;
|
|
||||||
min-width: 50px;
|
|
||||||
min-height: 45px;
|
|
||||||
}</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>28</width>
|
|
||||||
<height>28</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="clear">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>1</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>52</width>
|
|
||||||
<height>47</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Delete all data in this directory. THIS IS 100% IRREVERSABLE!</string>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true">QPushButton {
|
|
||||||
max-width: 50px;
|
|
||||||
max-height: 45px;
|
|
||||||
min-width: 50px;
|
|
||||||
min-height: 45px;
|
|
||||||
}</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>28</width>
|
|
||||||
<height>28</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="upload">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>1</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>52</width>
|
|
||||||
<height>47</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Export all data in this directory. This may take a while!</string>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true">QPushButton {
|
|
||||||
max-width: 50px;
|
|
||||||
max-height: 45px;
|
|
||||||
min-width: 50px;
|
|
||||||
min-height: 45px;
|
|
||||||
}</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>28</width>
|
|
||||||
<height>28</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="download">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>1</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>52</width>
|
|
||||||
<height>47</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Import data for this directory. This may take a while, and will delete ALL EXISTING DATA!</string>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true">QPushButton {
|
|
||||||
max-width: 50px;
|
|
||||||
max-height: 45px;
|
|
||||||
min-width: 50px;
|
|
||||||
min-height: 45px;
|
|
||||||
}</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>28</width>
|
|
||||||
<height>28</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
|
@ -156,7 +156,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
#include "yuzu/debugger/console.h"
|
#include "yuzu/debugger/console.h"
|
||||||
#include "yuzu/debugger/controller.h"
|
#include "yuzu/debugger/controller.h"
|
||||||
#include "yuzu/debugger/wait_tree.h"
|
#include "yuzu/debugger/wait_tree.h"
|
||||||
#include "yuzu/data_dialog.h"
|
|
||||||
#include "yuzu/deps_dialog.h"
|
#include "yuzu/deps_dialog.h"
|
||||||
#include "yuzu/discord.h"
|
#include "yuzu/discord.h"
|
||||||
#include "yuzu/game_list.h"
|
#include "yuzu/game_list.h"
|
||||||
|
@ -1706,7 +1705,6 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
|
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
|
||||||
connect_menu(ui->action_About, &GMainWindow::OnAbout);
|
connect_menu(ui->action_About, &GMainWindow::OnAbout);
|
||||||
connect_menu(ui->action_Eden_Dependencies, &GMainWindow::OnEdenDependencies);
|
connect_menu(ui->action_Eden_Dependencies, &GMainWindow::OnEdenDependencies);
|
||||||
connect_menu(ui->action_Data_Manager, &GMainWindow::OnDataDialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateMenuState() {
|
void GMainWindow::UpdateMenuState() {
|
||||||
|
@ -2408,6 +2406,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
|
|
||||||
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
||||||
|
|
||||||
|
// TODO(alekpop): It returns the wrong user
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case GameListOpenTarget::SaveData: {
|
case GameListOpenTarget::SaveData: {
|
||||||
open_target = tr("Save Data");
|
open_target = tr("Save Data");
|
||||||
|
@ -2793,7 +2792,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
||||||
// TODO(crueter): QtCommon
|
|
||||||
std::filesystem::path fs_path;
|
std::filesystem::path fs_path;
|
||||||
if (directory == QStringLiteral("SDMC")) {
|
if (directory == QStringLiteral("SDMC")) {
|
||||||
fs_path =
|
fs_path =
|
||||||
|
@ -3936,15 +3934,6 @@ void GMainWindow::OnEdenDependencies() {
|
||||||
depsDialog.exec();
|
depsDialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnDataDialog() {
|
|
||||||
DataDialog dataDialog(this);
|
|
||||||
dataDialog.exec();
|
|
||||||
|
|
||||||
// refresh stuff in case it was cleared
|
|
||||||
OnGameListRefresh();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void GMainWindow::OnToggleFilterBar() {
|
void GMainWindow::OnToggleFilterBar() {
|
||||||
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
||||||
if (ui->action_Show_Filter_Bar->isChecked()) {
|
if (ui->action_Show_Filter_Bar->isChecked()) {
|
||||||
|
@ -4485,15 +4474,11 @@ void GMainWindow::SetFirmwareVersion() {
|
||||||
|
|
||||||
if (result.IsError() || !CheckFirmwarePresence()) {
|
if (result.IsError() || !CheckFirmwarePresence()) {
|
||||||
LOG_INFO(Frontend, "Installed firmware: No firmware available");
|
LOG_INFO(Frontend, "Installed firmware: No firmware available");
|
||||||
ui->menu_Applets->setEnabled(false);
|
|
||||||
ui->menu_Create_Shortcuts->setEnabled(false);
|
|
||||||
firmware_label->setVisible(false);
|
firmware_label->setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
firmware_label->setVisible(true);
|
firmware_label->setVisible(true);
|
||||||
ui->menu_Applets->setEnabled(true);
|
|
||||||
ui->menu_Create_Shortcuts->setEnabled(true);
|
|
||||||
|
|
||||||
const std::string display_version(firmware_data.display_version.data());
|
const std::string display_version(firmware_data.display_version.data());
|
||||||
const std::string display_title(firmware_data.display_title.data());
|
const std::string display_title(firmware_data.display_title.data());
|
||||||
|
|
|
@ -387,7 +387,6 @@ private slots:
|
||||||
void OnInstallDecryptionKeys();
|
void OnInstallDecryptionKeys();
|
||||||
void OnAbout();
|
void OnAbout();
|
||||||
void OnEdenDependencies();
|
void OnEdenDependencies();
|
||||||
void OnDataDialog();
|
|
||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnToggleStatusBar();
|
void OnToggleStatusBar();
|
||||||
void OnGameListRefresh();
|
void OnGameListRefresh();
|
||||||
|
|
|
@ -158,23 +158,13 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_cabinet_applet">
|
<widget class="QMenu" name="menu_cabinet_applet">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Am&iibo</string>
|
<string>&Amiibo</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Load_Cabinet_Nickname_Owner"/>
|
<addaction name="action_Load_Cabinet_Nickname_Owner"/>
|
||||||
<addaction name="action_Load_Cabinet_Eraser"/>
|
<addaction name="action_Load_Cabinet_Eraser"/>
|
||||||
<addaction name="action_Load_Cabinet_Restorer"/>
|
<addaction name="action_Load_Cabinet_Restorer"/>
|
||||||
<addaction name="action_Load_Cabinet_Formatter"/>
|
<addaction name="action_Load_Cabinet_Formatter"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_Applets">
|
|
||||||
<property name="title">
|
|
||||||
<string>&Applets</string>
|
|
||||||
</property>
|
|
||||||
<addaction name="action_Load_Home_Menu"/>
|
|
||||||
<addaction name="action_Load_Album"/>
|
|
||||||
<addaction name="action_Load_Mii_Edit"/>
|
|
||||||
<addaction name="action_Open_Controller_Menu"/>
|
|
||||||
<addaction name="action_Open_Setup"/>
|
|
||||||
</widget>
|
|
||||||
<widget class="QMenu" name="menuTAS">
|
<widget class="QMenu" name="menuTAS">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&TAS</string>
|
<string>&TAS</string>
|
||||||
|
@ -194,7 +184,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuInstall_Firmware">
|
<widget class="QMenu" name="menuInstall_Firmware">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Install &Firmware</string>
|
<string>Install Firmware</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Firmware_From_Folder"/>
|
<addaction name="action_Firmware_From_Folder"/>
|
||||||
<addaction name="action_Firmware_From_ZIP"/>
|
<addaction name="action_Firmware_From_ZIP"/>
|
||||||
|
@ -202,10 +192,13 @@
|
||||||
<addaction name="action_Install_Keys"/>
|
<addaction name="action_Install_Keys"/>
|
||||||
<addaction name="menuInstall_Firmware"/>
|
<addaction name="menuInstall_Firmware"/>
|
||||||
<addaction name="action_Verify_installed_contents"/>
|
<addaction name="action_Verify_installed_contents"/>
|
||||||
<addaction name="action_Data_Manager"/>
|
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_cabinet_applet"/>
|
<addaction name="menu_cabinet_applet"/>
|
||||||
<addaction name="menu_Applets"/>
|
<addaction name="action_Load_Album"/>
|
||||||
|
<addaction name="action_Load_Mii_Edit"/>
|
||||||
|
<addaction name="action_Open_Controller_Menu"/>
|
||||||
|
<addaction name="action_Load_Home_Menu"/>
|
||||||
|
<addaction name="action_Open_Setup"/>
|
||||||
<addaction name="menu_Create_Shortcuts"/>
|
<addaction name="menu_Create_Shortcuts"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="action_Capture_Screenshot"/>
|
<addaction name="action_Capture_Screenshot"/>
|
||||||
|
@ -504,12 +497,12 @@
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Install_Keys">
|
<action name="action_Install_Keys">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Install Decryption &Keys</string>
|
<string>Install Decryption Keys</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Load_Home_Menu">
|
<action name="action_Load_Home_Menu">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open &Home Menu</string>
|
<string>Open Home Menu</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Discord">
|
<action name="action_Discord">
|
||||||
|
@ -600,11 +593,6 @@
|
||||||
<string>&Eden Dependencies</string>
|
<string>&Eden Dependencies</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Data_Manager">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Data Manager</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="yuzu.qrc"/>
|
<include location="yuzu.qrc"/>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "network/announce_multiplayer_session.h"
|
#include "network/announce_multiplayer_session.h"
|
||||||
#include "ui_chat_room.h"
|
#include "ui_chat_room.h"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "network/announce_multiplayer_session.h"
|
#include "network/announce_multiplayer_session.h"
|
||||||
#include "ui_client_room.h"
|
#include "ui_client_room.h"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <QIntValidator>
|
#include <QIntValidator>
|
||||||
#include <QRegularExpressionValidator>
|
#include <QRegularExpressionValidator>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/internal_network/network_interface.h"
|
#include "core/internal_network/network_interface.h"
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|