Compare commits

...

80 Commits

Author SHA1 Message Date
Julian Schill
3bc12bbe1f Added appimage workflow 2023-05-08 10:16:14 +02:00
bunnei
b70a205a96 Merge pull request #10075 from Kelebek1/silence_nifm_spam
Silence network spam
2023-05-07 17:45:32 -07:00
bunnei
3d8eca92f9 Merge pull request #10197 from liamwhite/resume-token
bootmanager: remove stop_token header
2023-05-07 17:43:15 -07:00
bunnei
d6d60f7104 Merge pull request #10194 from bunnei/update-dynarmic-3
externals: Update dynarmic to include latest patch.
2023-05-07 17:13:32 -07:00
Liam
bdb7c11d8e bootmanager: remove stop_token header 2023-05-07 19:20:09 -04:00
liamwhite
8f605b542c Merge pull request #10195 from german77/mutex
core: hid: Update motion on a better place
2023-05-07 19:06:10 -04:00
liamwhite
2688fb1aa2 Merge pull request #10155 from FernandoS27/reactive-flushing-new
Y.F.C. bring back Reactive Flushing
2023-05-07 19:05:56 -04:00
german77
cf023aa8ec core: hid: Update motion on a better place 2023-05-07 17:01:57 -06:00
bunnei
7eb17f3aa5 externals: Update dynarmic to include latest patch. 2023-05-07 15:06:58 -07:00
Fernando Sahmkow
8014dd8259 Texture cache: Only force flush the dma downloads 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
2df19ef0fd Buffer Cache: disable reactive flushing in it. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
016c6feb49 Texture cache: reverse inmediate flush changes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
36c302fa32 Buffer cache: always use async buffer downloads and fix regression. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
6f90dff293 Address feedback, add CR notice, etc 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
ab0c0a469c Query cache: stop updating pages as it's not affected by cpu writes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
92da86290c Settings: add option to enable / disable reactive flushing 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
0f4f18265f Texture cache: sync the first flush. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
c6cac2ffaa GPU: Add Reactive flushing 2023-05-07 23:46:12 +02:00
bunnei
e58090c9c7 Merge pull request #10097 from german77/nfp_full
service: nfc: Merge device interfaces and create the device manager
2023-05-07 12:18:09 -07:00
bunnei
ed98afdebc Merge pull request #10192 from bunnei/update-dynarmic-2
externals: Update dynarmic to include latest patch.
2023-05-07 12:17:40 -07:00
bunnei
e46074a2e3 externals: Update dynarmic to include latest patch. 2023-05-07 12:04:42 -07:00
liamwhite
0c5bdc7241 Merge pull request #10081 from Kelebek1/copy_overlap_tick
Sort overlap_ids by modification tick before copy
2023-05-07 14:09:10 -04:00
liamwhite
cb49c3b64c Merge pull request #10172 from Kelebek1/debug_validation_names
Log object names with debug renderer, add a GPU address to ImageViews
2023-05-07 14:09:03 -04:00
liamwhite
35862b21d6 Merge pull request #10189 from Qigo42/master
yuzu/applets/qt_profile_select: connect double-click to accept()
2023-05-07 14:08:57 -04:00
QGJ
4366a21eae yuzu/applets/qt_profile_select: connect double-click to accept()
In the profile selection window:

Allow the user to start the game by double-clicking a profile to avoid having to additionally click the OK button. This avoids an unnecessary "step" to the start of the game...
2023-05-07 18:05:42 +02:00
Fernando S
1166ca3a57 Merge pull request #10187 from Kelebek1/address_space
Fix address space allocator slow path to avoid OOB
2023-05-07 17:06:36 +02:00
Kelebek1
6fed48b3a4 Fix address space allocator slow path to avoid OOB 2023-05-07 15:11:16 +01:00
bunnei
f36b5dfd18 Merge pull request #10180 from german77/debug
input_common: Revert debugging changes
2023-05-06 22:26:53 -07:00
german77
91695a453b input_common: Revert debugging changes 2023-05-06 23:06:44 -06:00
bunnei
3547248ec2 Merge pull request #10125 from lat9nq/vsync-select
configuration: Expose separate swap present modes
2023-05-06 21:55:39 -07:00
bunnei
82188b2f79 Merge pull request #10174 from german77/motriod
input_common: Add experimental motion to button
2023-05-06 21:40:20 -07:00
bunnei
5467d39bc3 Merge pull request #10171 from german77/powera
input_common: Add property to invert an axis button
2023-05-06 21:39:44 -07:00
liamwhite
27ff00acb1 Merge pull request #10162 from lat9nq/sdl-remove-old
yuzu-sdl,audio_core: Remove antiquated warning ignore
2023-05-06 23:10:09 -04:00
liamwhite
f1b770a8f0 Merge pull request #10165 from merryhime/update-dynarmic
externals: Update dynarmic to 6.4.7
2023-05-06 23:10:02 -04:00
liamwhite
9c9b4616c3 Merge pull request #10167 from german77/motion_preview
yuzu: Add motion preview to controller input
2023-05-06 23:09:55 -04:00
liamwhite
e7b6389e44 Merge pull request #10178 from ronikirla/2-hour-crash
Fix read access violation after 2 hours of gameplay in PMD: RTDX
2023-05-06 23:09:36 -04:00
Roni Kirla
a13fd5f7cc Fix read access violation 2023-05-07 00:13:33 +03:00
german77
9b771bcb8f input_common: Add experimental motion to button 2023-05-05 22:49:56 -06:00
Narr the Reg
94151097b9 service: nfc: Merge device interfaces and create the device manager 2023-05-05 22:02:59 -06:00
Kelebek1
ca6bf06ef7 Log object names with debug renderer, add a GPU address to ImageViews 2023-05-06 04:48:32 +01:00
Narr the Reg
f017335fef input_common: Add property to invert an axis button 2023-05-05 17:18:35 -06:00
Narr the Reg
f764223f93 input_common: Add property to invert an axis button 2023-05-05 17:14:20 -06:00
Narr the Reg
46e835f2d6 yuzu: Add motion preview to controller input 2023-05-05 13:53:38 -06:00
bunnei
bb2e407772 Merge pull request #10159 from german77/home_screenshot
core: hid: Fix state of capture and home buttons
2023-05-05 12:02:15 -07:00
Merry
432d754d7d externals: Update dynarmic to 6.4.7 2023-05-05 16:30:18 +01:00
lat9nq
740c349820 yuzu-sdl,audio_core: Remove antiquated warning ignore
Issue was fixed a long time ago, both by SDL2 and in yuzu by including
SDL2 as a system library.
2023-05-05 03:48:28 -04:00
german77
8df3aed2f1 core: hid: Fix state of capture and home buttons 2023-05-04 22:36:59 -06:00
liamwhite
16939b1a6e Merge pull request #10128 from Kelebek1/audren_terminate
Wait for the terminate event before destroying a system instance
2023-05-04 14:44:09 -04:00
liamwhite
60d54d911e Merge pull request #10145 from Kelebek1/code_size
Fix shader code resize to use word size rather than byte size
2023-05-04 14:44:02 -04:00
liamwhite
e2b81ae5fe Merge pull request #10156 from v1993/looks-decent-to-me
Remove LGTM config
2023-05-04 14:43:55 -04:00
Valeri
b095a0242d Remove LGTM config
LGTM.com is no longer available since it was superseded by CodeQL.
2023-05-04 15:36:47 +03:00
Fernando S
2506594c50 Merge pull request #10153 from FernandoS27/a-quickie-fixie
Memory manager: Fix possible softlock
2023-05-04 03:56:53 +02:00
Fernando S
7d5683c63c Merge pull request #10154 from liamwhite/optimistic
settings: remove pessimistic flushing
2023-05-04 01:55:51 +02:00
bunnei
055ee84024 Merge pull request #10142 from FernandoS27/missing-astc
GPU: implement missing ASTC
2023-05-03 16:49:27 -07:00
Liam
ae59ffc56c settings: remove pessimistic flushing 2023-05-03 18:52:42 -04:00
lat9nq
a71498d163 qt_common: Remove yuzu prefix 2023-05-03 18:11:53 -04:00
Fernando Sahmkow
87a9be8dec GPU: implement missing ASTC 2023-05-03 11:33:28 -04:00
lat9nq
35e7f36a39 configure_graphics: No there isn't a hyphen in VSync
Most sources seem to suggest VSync and not V-Sync
2023-05-02 21:52:43 -04:00
lat9nq
d82cad3fb3 configure_input_player: Add missing include
Cleaning up includes in bootmanager and configure_graphics has exposed a
missing include here.
2023-05-02 21:52:43 -04:00
lat9nq
cd2981ee12 configure_graphics: Clean up includes [IWYU] 2023-05-02 21:52:43 -04:00
lat9nq
0c0f5b7ccc bootmanager: Clean up includes [IWYU]
bootmanager: Remove system-specific headers

IWYU can be too complete I suppose.
2023-05-02 21:52:43 -04:00
lat9nq
a546ecbb12 configure_graphics: Actively find present modes
When Vulkan devices are enumerated, this also determines the available
present modes for each device, maps them to a vector, and gives
those options to the user.
OpenGL options are limited to On/Off.

Required creating a VkSurfaceKHR during device enumeration, which
may or may not be desireable. For the sake of a less confusing UI.

Also fixes a bug where if a graphics device disappears on the host, we
don't try and select the non-existant devices.

configure_graphics: Remove vsync runtime lock for Vulkan

configure_graphics: Recommend Mailbox present mode

configure_graphics: Fix type-limits warning

configure_graphics: Clean up includes

configure_graphics: Add tooltip
2023-05-02 21:52:43 -04:00
lat9nq
952b271092 vk_swapchain: Use certain modes for unlocked
Uses mailbox, then immediate for unlocked framerate depending on
support for either. Also adds support for FIFO_RELAXED.

This function now assumes vsync_mode was originially configured to a value
that the driver supports.

vk_swapchain: ChooseSwapPresentMode determines updates

Simplifies swapchain a bit and allows us to change the present mode
during guest runtime.

vk_swapchain: Fix MSVC error

vk_swapchain: Enforce available present modes

Some frontends don't check the value of vsync_mode before comitting it.
Just as well, since a driver update or misconfiguration could problems
in the swap chain.

vk_swapchain: Silence warnings

Silences GCC warnings implicit-fallthrough and shadow, which apparently
are not enabled on clang.
2023-05-02 21:52:43 -04:00
lat9nq
a090a380be bootmanager: Remove inaccurate switch
Those vulkan settings do not correspond 1:1 to the swap intervals that
they set for OpenGL, so remove it.

bootmanager: Add missing include

I didn't add this log why did it break
2023-05-02 21:51:30 -04:00
lat9nq
cbd79df233 qt_common: Move window info function out of bootmanager
Function is useful outside of bootmanager, so put it in a common place.

qt_common: Add missing include

qt_common: Add some newlines

qt_common: Add trailing newline

qt_common: Add trainline newline
2023-05-02 21:51:30 -04:00
lat9nq
c6c11c1553 vulkan_surface: Pass only window info for surface creation
We don't need the whole EmuWindow when creating a surface,
and it creates onerous requirements outside of typical usage for
creating a surface elsewhere.
2023-05-02 21:51:30 -04:00
lat9nq
2528cf7c54 settings: Enable FIFO relaxed
Not entirely sure if we need this, but there's also no reason not to
support it.

settings: Give VSyncMode values
2023-05-02 21:51:30 -04:00
lat9nq
6b973c5986 configure_graphics: Fix another typo 2023-05-02 21:51:29 -04:00
lat9nq
40f062f749 telemetry_session: Make translate function static
Addresses review feedback

Co-authored-by: Lioncash <mathew1800@gmail.com>
2023-05-02 21:51:29 -04:00
lat9nq
29a56496bf bootmanager: Return value in impossible case
The setting is ranged, so this return statement is unreachable.
But GCC can't tell I guess.
2023-05-02 21:51:29 -04:00
lat9nq
41a103c0fc configure_graphics: Fix typo 2023-05-02 21:51:29 -04:00
lat9nq
66ed1c1872 default_ini: Update V-Sync description
default_ini: Update vsync text

default_ini: Add tooltip from configure_graphics
2023-05-02 21:51:29 -04:00
lat9nq
6f0929df82 configuration: Expose separate swap present modes
Previously, yuzu would try and guess which vsync mode to use given
different scenarios, but apparently we didn't always get it right. This
exposes the separate modes in a drop-down the user can select.

If a mode isn't available in Vulkan, it defaults to FIFO.
2023-05-02 21:51:29 -04:00
Kelebek1
f902cc2a2b Fix code resize to use word size rather than byte size 2023-05-02 23:52:21 +01:00
Kelebek1
2feb40f14d Wait for the terminate event before destroying a system instance 2023-05-01 00:27:12 +01:00
german77
5e16fe4579 core: service: Add FunctionInfoTyped to allow expanding existing interfaces 2023-04-25 23:36:24 -06:00
Narr the Reg
00d76fc5f5 service: nfc: Create mifare interface 2023-04-23 23:28:09 -06:00
Narr the Reg
a3fa64fcc4 service: nfc: Create interface 2023-04-23 23:28:09 -06:00
Kelebek1
4da4ecb1ff Silence nifm spam 2023-04-22 14:29:58 +01:00
Kelebek1
3346de365a Sort overlap_ids by modification tick before copy 2023-04-22 14:02:10 +01:00
124 changed files with 3466 additions and 3046 deletions

49
.github/workflows/build_appimage.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
name: 'yuzu AppImage'
on:
workflow_dispatch:
jobs:
build:
name: 'build'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- type: linux
image: linux-fresh
container:
image: yuzuemu/build-environments:${{ matrix.image }}
options: -u 1001
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0
- name: Set up cache
uses: actions/cache@v3
id: ccache-restore
with:
path: ~/.ccache
key: ${{ runner.os }}-${{ matrix.type }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ matrix.type }}-
- name: Create ccache directory
if: steps.ccache-restore.outputs.cache-hit != 'true'
run: mkdir -p ~/.ccache
- name: Build
run: ./.ci/scripts/${{ matrix.type }}/docker.sh
env:
ENABLE_COMPATIBILITY_REPORTING: "ON"
- name: Pack
run: ./.ci/scripts/${{ matrix.type }}/upload.sh
env:
NO_SOURCE_PACK: "YES"
- name: Upload
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.type }}
path: artifacts/

View File

@@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
path_classifiers:
library: "externals"
extraction:
cpp:
prepare:
packages:
- "libsdl2-dev"
- "qtmultimedia5-dev"
- "libtbb-dev"
- "libjack-jackd2-dev"

View File

@@ -436,10 +436,7 @@ void System::Stop() {
}
if (execution_mode == ExecutionMode::Auto) {
// Should wait for the system to terminate here, but core timing (should have) already
// stopped, so this isn't needed. Find a way to make this definite.
// terminate_event.Wait();
terminate_event.Wait();
}
}

View File

@@ -3,6 +3,7 @@
#include <span>
#include <vector>
#include <SDL.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/sdl2_sink.h"
@@ -10,16 +11,6 @@
#include "common/logging/log.h"
#include "core/core.h"
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#include <SDL.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
namespace AudioCore::Sink {
/**
* SDL sink stream, responsible for sinking samples to hardware.

View File

@@ -72,7 +72,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf
}
}()};
if (block_end_predecessor->virt >= virt) {
if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
// If this block's start would be overlapped by the map then reuse it as a tail
// block
block_end_predecessor->virt = virt_end;
@@ -336,7 +336,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) {
ASSERT_MSG(false, "Unexpected allocator state!");
}
auto search_predecessor{this->blocks.begin()};
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&

View File

@@ -111,6 +111,8 @@ struct AnalogProperties {
float offset{};
// Invert direction of the sensor data
bool inverted{};
// Invert the state if it's converted to a button
bool inverted_button{};
// Press once to activate, press again to release
bool toggle{};
};

View File

@@ -61,7 +61,8 @@ void LogSettings() {
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
@@ -223,11 +224,10 @@ void RestoreGlobalState(bool is_powered_on) {
values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.async_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_reactive_flushing.SetGlobal(true);
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.use_pessimistic_flushes.SetGlobal(true);
values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);

View File

@@ -16,6 +16,13 @@
namespace Settings {
enum class VSyncMode : u32 {
Immediate = 0,
Mailbox = 1,
FIFO = 2,
FIFORelaxed = 3,
};
enum class RendererBackend : u32 {
OpenGL = 0,
Vulkan = 1,
@@ -456,12 +463,13 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> async_astc{false, "async_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
VSyncMode::FIFORelaxed, "use_vsync"};
SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"};
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
"use_vulkan_driver_pipeline_cache"};

View File

@@ -259,6 +259,20 @@ public:
return *this;
}
void RotateFromOrigin(float roll, float pitch, float yaw) {
float temp = y;
y = std::cos(roll) * y - std::sin(roll) * z;
z = std::sin(roll) * temp + std::cos(roll) * z;
temp = x;
x = std::cos(pitch) * x + std::sin(pitch) * z;
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
temp = x;
x = std::cos(yaw) * x - std::sin(yaw) * y;
y = std::sin(yaw) * temp + std::cos(yaw) * y;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}

View File

@@ -555,21 +555,22 @@ add_library(core STATIC
hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/mifare_user.cpp
hle/service/nfc/mifare_user.h
hle/service/nfc/common/amiibo_crypto.cpp
hle/service/nfc/common/amiibo_crypto.h
hle/service/nfc/common/device.cpp
hle/service/nfc/common/device.h
hle/service/nfc/common/device_manager.cpp
hle/service/nfc/common/device_manager.h
hle/service/nfc/mifare_result.h
hle/service/nfc/mifare_types.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp
hle/service/nfc/nfc_device.h
hle/service/nfc/nfc_interface.cpp
hle/service/nfc/nfc_interface.h
hle/service/nfc/nfc_result.h
hle/service/nfc/nfc_user.cpp
hle/service/nfc/nfc_user.h
hle/service/nfp/amiibo_crypto.cpp
hle/service/nfp/amiibo_crypto.h
hle/service/nfc/nfc_types.h
hle/service/nfp/nfp.cpp
hle/service/nfp/nfp.h
hle/service/nfp/nfp_device.cpp
hle/service/nfp/nfp_device.h
hle/service/nfp/nfp_interface.cpp
hle/service/nfp/nfp_interface.h
hle/service/nfp/nfp_result.h

View File

@@ -612,6 +612,10 @@ void System::PrepareReschedule(const u32 core_index) {
impl->kernel.PrepareReschedule(core_index);
}
size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID();
}
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}

View File

@@ -222,6 +222,8 @@ public:
/// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
[[nodiscard]] size_t GetCurrentHostThreadID() const;
/// Gets and resets core performance statistics
[[nodiscard]] PerfStatsResults GetAndResetPerfStats();

View File

@@ -14,7 +14,7 @@ void DefaultCabinetApplet::Close() const {}
void DefaultCabinetApplet::ShowCabinetApplet(
const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback(false, {});
}

View File

@@ -7,9 +7,9 @@
#include "core/frontend/applets/applet.h"
#include "core/hle/service/nfp/nfp_types.h"
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
namespace Service::NFC {
class NfcDevice;
} // namespace Service::NFC
namespace Core::Frontend {
@@ -26,14 +26,14 @@ public:
virtual ~CabinetApplet();
virtual void ShowCabinetApplet(const CabinetCallback& callback,
const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const = 0;
};
class DefaultCabinetApplet final : public CabinetApplet {
public:
void Close() const override;
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const override;
};
} // namespace Core::Frontend

View File

@@ -376,6 +376,7 @@ void EmulatedController::ReloadInput() {
motion.accel = emulated_motion.GetAcceleration();
motion.gyro = emulated_motion.GetGyroscope();
motion.rotation = emulated_motion.GetRotations();
motion.euler = emulated_motion.GetEulerAngles();
motion.orientation = emulated_motion.GetOrientation();
motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
}
@@ -551,6 +552,8 @@ void EmulatedController::EnableSystemButtons() {
void EmulatedController::DisableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = false;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
}
void EmulatedController::ResetSystemButtons() {
@@ -734,6 +737,8 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (is_configuring) {
controller.npad_button_state.raw = NpadButton::None;
controller.debug_pad_button_state.raw = 0;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Button, false);
return;
@@ -974,16 +979,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold);
emulated.UpdateRotation(raw_status.delta_timestamp);
emulated.UpdateOrientation(raw_status.delta_timestamp);
force_update_motion = raw_status.force_update;
if (is_configuring) {
return;
}
auto& motion = controller.motion_state[index];
motion.accel = emulated.GetAcceleration();
motion.gyro = emulated.GetGyroscope();
motion.rotation = emulated.GetRotations();
motion.euler = emulated.GetEulerAngles();
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
}
@@ -1616,19 +1617,6 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
MotionState EmulatedController::GetMotions() const {
std::unique_lock lock{mutex};
// Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
lock.unlock();
device->ForceUpdate();
lock.lock();
}
}
return controller.motion_state;
}
@@ -1694,8 +1682,21 @@ void EmulatedController::DeleteCallback(int key) {
callback_list.erase(iterator);
}
void EmulatedController::TurboButtonUpdate() {
void EmulatedController::StatusUpdate() {
turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
// Some drivers like key motion need constant refreshing
for (std::size_t index = 0; index < motion_devices.size(); ++index) {
const auto& raw_status = controller.motion_values[index].raw_status;
auto& device = motion_devices[index];
if (!raw_status.force_update) {
continue;
}
if (!device) {
continue;
}
device->ForceUpdate();
}
}
NpadButton EmulatedController::GetTurboButtonMask() const {

View File

@@ -106,6 +106,7 @@ struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
Common::Vec3f euler{};
std::array<Common::Vec3f, 3> orientation{};
bool is_at_rest{};
};
@@ -414,8 +415,8 @@ public:
*/
void DeleteCallback(int key);
/// Swaps the state of the turbo buttons
void TurboButtonUpdate();
/// Swaps the state of the turbo buttons and updates motion input
void StatusUpdate();
private:
/// creates input devices from params
@@ -527,7 +528,6 @@ private:
bool is_configuring{false};
bool system_buttons_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
bool force_update_motion{false};
u32 turbo_button_state{0};
// Temporary values to avoid doing changes while the controller is in configuring mode

View File

@@ -54,6 +54,7 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
case Common::Input::InputType::Analog:
status.value = TransformToTrigger(callback).pressed.value;
status.toggle = callback.analog_status.properties.toggle;
status.inverted = callback.analog_status.properties.inverted_button;
break;
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
@@ -61,6 +62,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
case Common::Input::InputType::Button:
status = callback.button_status;
break;
case Common::Input::InputType::Motion:
status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
break;
@@ -82,7 +86,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
.range = 1.0f,
.offset = 0.0f,
};
status.delta_timestamp = 5000;
status.delta_timestamp = 1000;
status.force_update = true;
status.accel.x = {
.value = 0.0f,
@@ -226,6 +230,10 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
status = callback.trigger_status;
calculate_button_value = false;
break;
case Common::Input::InputType::Motion:
status.analog.properties.range = 1.0f;
raw_value = callback.motion_status.accel.x.raw_value;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
break;

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmath>
#include "common/math_util.h"
#include "core/hid/motion_input.h"
@@ -51,6 +53,20 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
quat = quaternion;
}
void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
const float cr = std::cos(euler_angles.x * 0.5f);
const float sr = std::sin(euler_angles.x * 0.5f);
const float cp = std::cos(euler_angles.y * 0.5f);
const float sp = std::sin(euler_angles.y * 0.5f);
const float cy = std::cos(euler_angles.z * 0.5f);
const float sy = std::sin(euler_angles.z * 0.5f);
quat.w = cr * cp * cy + sr * sp * sy;
quat.xyz.x = sr * cp * cy - cr * sp * sy;
quat.xyz.y = cr * sp * cy + sr * cp * sy;
quat.xyz.z = cr * cp * sy - sr * sp * cy;
}
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
gyro_bias = bias;
}
@@ -222,6 +238,26 @@ Common::Vec3f MotionInput::GetRotations() const {
return rotations;
}
Common::Vec3f MotionInput::GetEulerAngles() const {
// roll (x-axis rotation)
const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
// pitch (y-axis rotation)
const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
// yaw (z-axis rotation)
const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
return {
std::atan2(sinr_cosp, cosr_cosp),
2 * std::atan2(sinp, cosp) - Common::PI / 2,
std::atan2(siny_cosp, cosy_cosp),
};
}
void MotionInput::ResetOrientation() {
if (!reset_enabled || only_accelerometer) {
return;

View File

@@ -35,6 +35,7 @@ public:
void SetAcceleration(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec3f& gyroscope);
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
void SetEulerAngles(const Common::Vec3f& euler_angles);
void SetGyroBias(const Common::Vec3f& bias);
void SetGyroThreshold(f32 threshold);
@@ -54,6 +55,7 @@ public:
[[nodiscard]] Common::Vec3f GetGyroBias() const;
[[nodiscard]] Common::Vec3f GetRotations() const;
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
[[nodiscard]] Common::Vec3f GetEulerAngles() const;
[[nodiscard]] bool IsMoving(f32 sensitivity) const;
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const;

View File

@@ -11,7 +11,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfc/common/device.h"
namespace Service::AM::Applets {
@@ -72,10 +72,10 @@ void Cabinet::Execute() {
// TODO: listen on all controllers
if (nfp_device == nullptr) {
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
nfp_device = std::make_shared<Service::NFC::NfcDevice>(
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
nfp_device->Initialize();
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
nfp_device->StartDetection(Service::NFC::NfcProtocol::All);
}
const Core::Frontend::CabinetParameters parameters{
@@ -106,20 +106,22 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
Cancel();
}
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
Cancel();
}
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::MountTarget::All);
if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);
}
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
Service::NFP::AmiiboName name{};
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
nfp_device->SetRegisterInfoPrivate(name);
Service::NFP::RegisterInfoPrivate register_info{};
std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
nfp_device->SetRegisterInfoPrivate(register_info);
break;
}
case Service::NFP::CabinetMode::StartGameDataEraser:
@@ -139,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false);
nfp_device->Finalize();
if (reg_result.IsSuccess()) {

View File

@@ -19,8 +19,8 @@ namespace Core {
class System;
} // namespace Core
namespace Service::NFP {
class NfpDevice;
namespace Service::NFC {
class NfcDevice;
}
namespace Service::AM::Applets {
@@ -96,7 +96,7 @@ private:
Core::System& system;
bool is_complete{false};
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
Kernel::KEvent* availability_change_event;
KernelHelpers::ServiceContext service_context;
StartParamForAmiiboSettings applet_input_common{};

View File

@@ -423,8 +423,8 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
return;
}
// This function is unique to yuzu for the turbo buttons to work properly
controller.device->TurboButtonUpdate();
// This function is unique to yuzu for the turbo buttons and motion to work properly
controller.device->StatusUpdate();
auto& pad_entry = controller.npad_pad_state;
auto& trigger_entry = controller.npad_trigger_state;

View File

@@ -12,7 +12,7 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
#include "core/hle/service/nfc/common/amiibo_crypto.h"
namespace Service::NFP::AmiiboCrypto {
@@ -55,7 +55,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
if (amiibo_data.constant_value != 0xA5) {
return false;
}
if (amiibo_data.model_info.tag_type != PackedTagType::Type2) {
if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) {
return false;
}
if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {

View File

@@ -24,9 +24,9 @@ using DrgbOutput = std::array<u8, 0x20>;
struct HashSeed {
u16_be magic;
std::array<u8, 0xE> padding;
UniqueSerialNumber uid_1;
NFC::UniqueSerialNumber uid_1;
u8 nintendo_id_1;
UniqueSerialNumber uid_2;
NFC::UniqueSerialNumber uid_2;
u8 nintendo_id_2;
std::array<u8, 0x20> keygen_salt;
};

View File

@@ -0,0 +1,138 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(NfcProtocol allowed_protocol);
Result StopDetection();
Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const;
Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
std::span<MifareReadBlockData> read_block_data) const;
Result ReadMifare(const MifareReadBlockParameter& parameter,
MifareReadBlockData& read_block_data) const;
Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
Result WriteMifare(const MifareWriteBlockParameter& parameter);
Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount();
Result Flush();
Result FlushDebug();
Result FlushWithBreak(NFP::BreakType break_type);
Result Restore();
Result GetCommonInfo(NFP::CommonInfo& common_info) const;
Result GetModelInfo(NFP::ModelInfo& model_info) const;
Result GetRegisterInfo(NFP::RegisterInfo& register_info) const;
Result GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(NFP::AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::span<u8> data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
Result ExistsApplicationArea(bool& has_application_area) const;
Result GetAll(NFP::NfpData& data) const;
Result SetAll(const NFP::NfpData& data);
Result BreakTag(NFP::BreakType break_type);
Result ReadBackupData(std::span<u8> data) const;
Result WriteBackupData(std::span<const u8> data);
Result WriteNtf(std::span<const u8> data);
u64 GetHandle() const;
DeviceState GetCurrentState() const;
Result GetNpadId(Core::HID::NpadIdType& out_npad_id) const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
NfcProtocol allowed_protocols{};
DeviceState device_state{DeviceState::Unavailable};
// NFP data
bool is_data_moddified{};
bool is_app_area_open{};
bool is_plain_amiibo{};
s64 current_posix_time{};
NFP::MountTarget mount_target{NFP::MountTarget::None};
NFP::NTAG215File tag_data{};
std::vector<u8> mifare_data{};
NFP::EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,695 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: system{system_}, service_context{service_context_} {
availability_change_event =
service_context.CreateEvent("Nfc:DeviceManager:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < devices.size(); device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
is_initialized = false;
}
DeviceManager ::~DeviceManager() {
service_context.CloseEvent(availability_change_event);
}
Result DeviceManager::Initialize() {
for (auto& device : devices) {
device->Initialize();
}
is_initialized = true;
return ResultSuccess;
}
Result DeviceManager::Finalize() {
for (auto& device : devices) {
device->Finalize();
}
is_initialized = false;
return ResultSuccess;
}
Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices,
std::size_t max_allowed_devices) const {
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
return ResultDeviceNotFound;
}
return ResultSuccess;
}
DeviceState DeviceManager::GetDeviceState(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
const auto result = GetDeviceFromHandle(device_handle, device, false);
if (result.IsSuccess()) {
return device->GetCurrentState();
}
return DeviceState::Unavailable;
}
Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetNpadId(npad_id);
result = VerifyDeviceResult(device, result);
}
return result;
}
Kernel::KReadableEvent& DeviceManager::AttachAvailabilityChangeEvent() const {
return availability_change_event->GetReadableEvent();
}
Result DeviceManager::StartDetection(u64 device_handle, NfcProtocol tag_protocol) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->StartDetection(tag_protocol);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::StopDetection(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->StopDetection();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetTagInfo(tag_info, is_mifare);
result = VerifyDeviceResult(device, result);
}
return result;
}
Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
GetDeviceFromHandle(device_handle, device, false);
// TODO: Return proper error code on failure
return device->GetActivateEvent();
}
Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
GetDeviceFromHandle(device_handle, device, false);
// TODO: Return proper error code on failure
return device->GetDeactivateEvent();
}
Result DeviceManager::ReadMifare(u64 device_handle,
std::span<const MifareReadBlockParameter> read_parameters,
std::span<MifareReadBlockData> read_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ReadMifare(read_parameters, read_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteMifare(write_parameters);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SendCommandByPassThrough(u64 device_handle,
const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data,
std::span<u8> out_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SendCommandByPassThrough(timeout, command_data, out_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Mount(u64 device_handle, NFP::ModelType model_type,
NFP::MountTarget mount_target) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Mount(model_type, mount_target);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Unmount(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Unmount();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->OpenApplicationArea(access_id);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetApplicationArea(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetApplicationArea(u64 device_handle, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetApplicationArea(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Flush(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Flush();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Restore(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Restore();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id,
std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->CreateApplicationArea(access_id, data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetRegisterInfo(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetCommonInfo(common_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetModelInfo(model_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
u32 DeviceManager::GetApplicationAreaSize() const {
return sizeof(NFP::ApplicationArea);
}
Result DeviceManager::RecreateApplicationArea(u64 device_handle, u32 access_id,
std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->RecreateApplicationArea(access_id, data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Format(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Format();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetAdminInfo(admin_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle,
NFP::RegisterInfoPrivate& register_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetRegisterInfoPrivate(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetRegisterInfoPrivate(u64 device_handle,
const NFP::RegisterInfoPrivate& register_info) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetRegisterInfoPrivate(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::DeleteRegisterInfo(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->DeleteRegisterInfo();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::DeleteApplicationArea(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->DeleteApplicationArea();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ExistsApplicationArea(has_application_area);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetAll(nfp_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetAll(u64 device_handle, const NFP::NfpData& nfp_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetAll(nfp_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::FlushDebug(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->FlushDebug();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->BreakTag(break_type);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ReadBackupData(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteBackupData(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteNtf(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device,
bool check_state) const {
if (check_state) {
const Result is_parameter_set = IsNfcParameterSet();
if (is_parameter_set.IsError()) {
return is_parameter_set;
}
const Result is_enabled = IsNfcEnabled();
if (is_enabled.IsError()) {
return is_enabled;
}
const Result is_nfc_initialized = IsNfcInitialized();
if (is_nfc_initialized.IsError()) {
return is_nfc_initialized;
}
}
for (auto& device : devices) {
if (device->GetHandle() == handle) {
nfc_device = device;
return ResultSuccess;
}
}
return ResultDeviceNotFound;
}
std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
const std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) const {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const {
const auto result = GetDeviceFromHandle(handle, device, true);
if (result.IsError()) {
return result;
}
return CheckDeviceState(device);
}
Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
Result operation_result) const {
if (operation_result.IsSuccess()) {
return operation_result;
}
const Result is_parameter_set = IsNfcParameterSet();
if (is_parameter_set.IsError()) {
return is_parameter_set;
}
const Result is_enabled = IsNfcEnabled();
if (is_enabled.IsError()) {
return is_enabled;
}
const Result is_nfc_initialized = IsNfcInitialized();
if (is_nfc_initialized.IsError()) {
return is_nfc_initialized;
}
const Result device_state = CheckDeviceState(device);
if (device_state.IsError()) {
return device_state;
}
return operation_result;
}
Result DeviceManager::CheckDeviceState(std::shared_ptr<NfcDevice> device) const {
if (device == nullptr) {
return ResultInvalidArgument;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcEnabled() const {
// TODO: This calls nn::settings::detail::GetNfcEnableFlag
const bool is_enabled = true;
if (!is_enabled) {
return ResultNfcDisabled;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcParameterSet() const {
// TODO: This calls checks against a bool on offset 0x450
const bool is_set = true;
if (!is_set) {
return ResultUnknown76;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcInitialized() const {
if (!is_initialized) {
return ResultNfcNotInitialized;
}
return ResultSuccess;
}
} // namespace Service::NFC

View File

@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include <span>
#include "core/hid/hid_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
class NfcDevice;
class DeviceManager {
public:
explicit DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
~DeviceManager();
// Nfc device manager
Result Initialize();
Result Finalize();
Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices) const;
DeviceState GetDeviceState(u64 device_handle) const;
Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const;
Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
Result StopDetection(u64 device_handle);
Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const;
Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
Result ReadMifare(u64 device_handle,
const std::span<const MifareReadBlockParameter> read_parameters,
std::span<MifareReadBlockData> read_data);
Result WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters);
Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
// Nfp device manager
Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount(u64 device_handle);
Result OpenApplicationArea(u64 device_handle, u32 access_id);
Result GetApplicationArea(u64 device_handle, std::span<u8> data) const;
Result SetApplicationArea(u64 device_handle, std::span<const u8> data);
Result Flush(u64 device_handle);
Result Restore(u64 device_handle);
Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const;
Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const;
Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const;
u32 GetApplicationAreaSize() const;
Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
Result Format(u64 device_handle);
Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const;
Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const;
Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info);
Result DeleteRegisterInfo(u64 device_handle);
Result DeleteApplicationArea(u64 device_handle);
Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const;
Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const;
Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data);
Result FlushDebug(u64 device_handle);
Result BreakTag(u64 device_handle, NFP::BreakType break_type);
Result ReadBackupData(u64 device_handle, std::span<u8> data) const;
Result WriteBackupData(u64 device_handle, std::span<const u8> data);
Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data);
private:
Result IsNfcEnabled() const;
Result IsNfcParameterSet() const;
Result IsNfcInitialized() const;
Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device,
bool check_state) const;
Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const;
Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result) const;
Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const;
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
bool is_initialized = false;
mutable std::mutex mutex;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
Core::System& system;
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::NFC::Mifare {
constexpr Result ResultDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result ResultReadError(ErrorModule::NFCMifare, 288);
} // namespace Service::NFC::Mifare

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::NFC {
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
// This is nn::nfc::MifareReadBlockParameter
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
// This is nn::nfc::MifareReadBlockData
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
// This is nn::nfc::MifareWriteBlockParameter
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFC

View File

@@ -1,400 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
namespace Service::NFC {
MFIUser::MFIUser(Core::System& system_)
: ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::Read, "Read"},
{6, &MFIUser::Write, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"},
{9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
MFIUser ::~MFIUser() {
availability_change_event->Close();
}
void MFIUser::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void MFIUser::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void MFIUser::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (const auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void MFIUser::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(NFP::TagProtocol::All);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareRead(read_commands[i], out_data[i]);
if (result.IsError()) {
break;
}
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareWrite(write_commands[i]);
if (result.IsError()) {
break;
}
}
if (result.IsSuccess()) {
result = device.value()->Flush();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, true);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetActivateEventHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void MFIUser::GetDeactivateEventHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void MFIUser::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void MFIUser::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void MFIUser::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void MFIUser::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -1,52 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class MFIUser final : public ServiceFramework<MFIUser> {
public:
explicit MFIUser(Core::System& system_);
~MFIUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
void Read(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void GetTagInfo(HLERequestContext& ctx);
void GetActivateEventHandle(HLERequestContext& ctx);
void GetDeactivateEventHandle(HLERequestContext& ctx);
void GetState(HLERequestContext& ctx);
void GetDeviceState(HLERequestContext& ctx);
void GetNpadId(HLERequestContext& ctx);
void GetAvailabilityChangeEventHandle(HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -6,14 +6,115 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class IUser final : public NfcInterface {
public:
explicit IUser(Core::System& system_) : NfcInterface(system_, "NFC::IUser", BackendType::Nfc) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NfcInterface::Initialize, "InitializeOld"},
{1, &NfcInterface::Finalize, "FinalizeOld"},
{2, &NfcInterface::GetState, "GetStateOld"},
{3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &NfcInterface::Initialize, "Initialize"},
{401, &NfcInterface::Finalize, "Finalize"},
{402, &NfcInterface::GetState, "GetState"},
{403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
{404, &NfcInterface::ListDevices, "ListDevices"},
{405, &NfcInterface::GetDeviceState, "GetDeviceState"},
{406, &NfcInterface::GetNpadId, "GetNpadId"},
{407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &NfcInterface::StartDetection, "StartDetection"},
{409, &NfcInterface::StopDetection, "StopDetection"},
{410, &NfcInterface::GetTagInfo, "GetTagInfo"},
{411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
{412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, &NfcInterface::ReadMifare, "ReadMifare"},
{1001, &NfcInterface::WriteMifare ,"WriteMifare"},
{1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ISystem final : public NfcInterface {
public:
explicit ISystem(Core::System& system_)
: NfcInterface{system_, "NFC::ISystem", BackendType::Nfc} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NfcInterface::Initialize, "InitializeOld"},
{1, &NfcInterface::Finalize, "FinalizeOld"},
{2, &NfcInterface::GetState, "GetStateOld"},
{3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
{100, nullptr, "SetNfcEnabledOld"},
{400, &NfcInterface::Initialize, "Initialize"},
{401, &NfcInterface::Finalize, "Finalize"},
{402, &NfcInterface::GetState, "GetState"},
{403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
{404, &NfcInterface::ListDevices, "ListDevices"},
{405, &NfcInterface::GetDeviceState, "GetDeviceState"},
{406, &NfcInterface::GetNpadId, "GetNpadId"},
{407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &NfcInterface::StartDetection, "StartDetection"},
{409, &NfcInterface::StopDetection, "StopDetection"},
{410, &NfcInterface::GetTagInfo, "GetTagInfo"},
{411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
{412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
{500, nullptr, "SetNfcEnabled"},
{510, nullptr, "OutputTestWave"},
{1000, &NfcInterface::ReadMifare, "ReadMifare"},
{1001, &NfcInterface::WriteMifare, "WriteMifare"},
{1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
// MFInterface has an unique interface but it's identical to NfcInterface so we can keep the code
// simpler
using MFInterface = NfcInterface;
class MFIUser final : public MFInterface {
public:
explicit MFIUser(Core::System& system_)
: MFInterface{system_, "NFC::MFInterface", BackendType::Mifare} {
// clang-format off
static const FunctionInfoTyped<MFIUser> functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::ReadMifare, "Read"},
{6, &MFIUser::WriteMifare, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::AttachActivateEvent, "GetActivateEventHandle"},
{9, &MFIUser::AttachDeactivateEvent, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::AttachAvailabilityChangeEvent, "GetAvailabilityChangeEventHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IAm final : public ServiceFramework<IAm> {
public:
explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} {
@@ -34,7 +135,7 @@ public:
explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_AM::CreateAmInterface, "CreateAmInterface"},
{0, &NFC_AM::CreateAmNfcInterface, "CreateAmNfcInterface"},
};
// clang-format on
@@ -42,7 +143,7 @@ public:
}
private:
void CreateAmInterface(HLERequestContext& ctx) {
void CreateAmNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -56,7 +157,7 @@ public:
explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"},
{0, &NFC_MF_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
};
// clang-format on
@@ -64,7 +165,7 @@ public:
}
private:
void CreateUserInterface(HLERequestContext& ctx) {
void CreateUserNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -78,7 +179,7 @@ public:
explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
{0, &NFC_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
};
// clang-format on
@@ -86,7 +187,7 @@ public:
}
private:
void CreateUserInterface(HLERequestContext& ctx) {
void CreateUserNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -95,49 +196,12 @@ private:
}
};
class ISystem final : public ServiceFramework<ISystem> {
public:
explicit ISystem(Core::System& system_) : ServiceFramework{system_, "ISystem"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, nullptr, "Finalize"},
{2, nullptr, "GetStateOld"},
{3, nullptr, "IsNfcEnabledOld"},
{100, nullptr, "SetNfcEnabledOld"},
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
{402, nullptr, "GetState"},
{403, nullptr, "IsNfcEnabled"},
{404, nullptr, "ListDevices"},
{405, nullptr, "GetDeviceState"},
{406, nullptr, "GetNpadId"},
{407, nullptr, "AttachAvailabilityChangeEvent"},
{408, nullptr, "StartDetection"},
{409, nullptr, "StopDetection"},
{410, nullptr, "GetTagInfo"},
{411, nullptr, "AttachActivateEvent"},
{412, nullptr, "AttachDeactivateEvent"},
{500, nullptr, "SetNfcEnabled"},
{510, nullptr, "OutputTestWave"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, nullptr, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class NFC_SYS final : public ServiceFramework<NFC_SYS> {
public:
explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"},
{0, &NFC_SYS::CreateSystemNfcInterface, "CreateSystemNfcInterface"},
};
// clang-format on
@@ -145,7 +209,7 @@ public:
}
private:
void CreateSystemInterface(HLERequestContext& ctx) {
void CreateSystemNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -161,6 +225,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("nfc:mf:u", std::make_shared<NFC_MF_U>(system));
server_manager->RegisterNamedService("nfc:user", std::make_shared<NFC_U>(system));
server_manager->RegisterNamedService("nfc:sys", std::make_shared<NFC_SYS>(system));
ServerManager::RunServer(std::move(server_manager));
}

View File

@@ -1,288 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/input.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
namespace Service::NFC {
NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_)
: npad_id{npad_id_}, system{system_}, service_context{service_context_},
availability_change_event{availability_change_event_} {
activate_event = service_context.CreateEvent("IUser:NFCActivateEvent");
deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent");
npad_device = system.HIDCore().GetEmulatedController(npad_id);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
.is_npad_service = false,
};
is_controller_set = true;
callback_key = npad_device->SetCallback(engine_callback);
}
NfcDevice::~NfcDevice() {
activate_event->Close();
deactivate_event->Close();
if (!is_controller_set) {
return;
}
npad_device->DeleteCallback(callback_key);
is_controller_set = false;
};
void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
if (!is_initalized) {
return;
}
if (type == Core::HID::ControllerTriggerType::Connected) {
Initialize();
availability_change_event->Signal();
return;
}
if (type == Core::HID::ControllerTriggerType::Disconnected) {
device_state = NFP::DeviceState::Unavailable;
availability_change_event->Signal();
return;
}
if (type != Core::HID::ControllerTriggerType::Nfc) {
return;
}
if (!npad_device->IsConnected()) {
return;
}
const auto nfc_status = npad_device->GetNfc();
switch (nfc_status.state) {
case Common::Input::NfcState::NewAmiibo:
LoadNfcTag(nfc_status.data);
break;
case Common::Input::NfcState::AmiiboRemoved:
if (device_state != NFP::DeviceState::SearchingForTag) {
CloseNfcTag();
}
break;
default:
break;
}
}
bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
if (device_state != NFP::DeviceState::SearchingForTag) {
LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
return false;
}
if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
return false;
}
tag_data.resize(data.size());
memcpy(tag_data.data(), data.data(), data.size());
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
device_state = NFP::DeviceState::TagFound;
deactivate_event->GetReadableEvent().Clear();
activate_event->Signal();
return true;
}
void NfcDevice::CloseNfcTag() {
LOG_INFO(Service_NFC, "Remove nfc tag");
device_state = NFP::DeviceState::TagRemoved;
encrypted_tag_data = {};
activate_event->GetReadableEvent().Clear();
deactivate_event->Signal();
}
Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
return activate_event->GetReadableEvent();
}
Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
return deactivate_event->GetReadableEvent();
}
void NfcDevice::Initialize() {
device_state =
npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
encrypted_tag_data = {};
is_initalized = true;
}
void NfcDevice::Finalize() {
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
StopDetection();
}
device_state = NFP::DeviceState::Unavailable;
is_initalized = false;
}
Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
if (device_state != NFP::DeviceState::Initialized &&
device_state != NFP::DeviceState::TagRemoved) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::NFC) !=
Common::Input::DriverResult::Success) {
LOG_ERROR(Service_NFC, "Nfc not supported");
return NfcDisabled;
}
device_state = NFP::DeviceState::SearchingForTag;
allowed_protocols = allowed_protocol;
return ResultSuccess;
}
Result NfcDevice::StopDetection() {
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
if (device_state == NFP::DeviceState::Initialized) {
return ResultSuccess;
}
if (device_state == NFP::DeviceState::TagFound ||
device_state == NFP::DeviceState::TagMounted) {
CloseNfcTag();
return ResultSuccess;
}
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
device_state = NFP::DeviceState::Initialized;
return ResultSuccess;
}
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
Result NfcDevice::Flush() {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (!npad_device->WriteNfc(tag_data)) {
LOG_ERROR(Service_NFP, "Error writing to file");
return MifareReadError;
}
return ResultSuccess;
}
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (is_mifare) {
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type4,
};
return ResultSuccess;
}
// Protocol and tag type may change here
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type2,
};
return ResultSuccess;
}
Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
read_block_data.sector_number = parameter.sector_number;
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to read encrypted data
memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock));
return ResultSuccess;
}
Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to encrypt the data
memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock));
return ResultSuccess;
}
u64 NfcDevice::GetHandle() const {
// Generate a handle based of the npad id
return static_cast<u64>(npad_id);
}
NFP::DeviceState NfcDevice::GetCurrentState() const {
return device_state;
}
Core::HID::NpadIdType NfcDevice::GetNpadId() const {
return npad_id;
}
} // namespace Service::NFC

View File

@@ -1,78 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(NFP::TagProtocol allowed_protocol);
Result StopDetection();
Result Flush();
Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const;
Result MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data);
Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter);
u64 GetHandle() const;
NFP::DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
NFP::TagProtocol allowed_protocols{};
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
NFP::EncryptedNTAG215File encrypted_tag_data{};
std::vector<u8> tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,382 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/mifare_result.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
NfcInterface::NfcInterface(Core::System& system_, const char* name, BackendType service_backend)
: ServiceFramework{system_, name}, service_context{system_, service_name},
backend_type{service_backend} {}
NfcInterface ::~NfcInterface() = default;
void NfcInterface::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
auto manager = GetManager();
auto result = manager->Initialize();
if (result.IsSuccess()) {
state = State::Initialized;
} else {
manager->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(result);
}
void NfcInterface::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state != State::NonInitialized) {
if (GetBackendType() != BackendType::None) {
GetManager()->Finalize();
}
device_manager = nullptr;
state = State::NonInitialized;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void NfcInterface::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void NfcInterface::IsNfcEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
// TODO: This calls nn::settings::detail::GetNfcEnableFlag
const bool is_enabled = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
}
void NfcInterface::ListDevices(HLERequestContext& ctx) {
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
LOG_DEBUG(Service_NFC, "called");
auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void NfcInterface::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
const auto device_state = GetManager()->GetDeviceState(device_handle);
if (device_state > DeviceState::Finalized) {
ASSERT_MSG(false, "Invalid device state");
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device_state);
}
void NfcInterface::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
Core::HID::NpadIdType npad_id{};
auto result = GetManager()->GetNpadId(device_handle, npad_id);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(npad_id);
}
void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
}
void NfcInterface::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto tag_protocol{rp.PopEnum<NfcProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
auto result = GetManager()->StartDetection(device_handle, tag_protocol);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
auto result = GetManager()->StopDetection(device_handle);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
TagInfo tag_info{};
auto result =
GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
result = TranslateResultToServiceError(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(tag_info);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle));
}
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle));
}
void NfcInterface::ReadMifare(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<MifareReadBlockParameter>()};
std::vector<MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
std::vector<MifareReadBlockData> out_data(number_of_commands);
auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
result = TranslateResultToServiceError(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(out_data);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::WriteMifare(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<MifareWriteBlockParameter>()};
std::vector<MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
auto result = GetManager()->WriteMifare(device_handle, write_commands);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
std::vector<u8> out_data(1);
auto result =
GetManager()->SendCommandByPassThrough(device_handle, timeout, command_data, out_data);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::shared_ptr<DeviceManager> NfcInterface::GetManager() {
if (device_manager == nullptr) {
device_manager = std::make_shared<DeviceManager>(system, service_context);
}
return device_manager;
}
BackendType NfcInterface::GetBackendType() const {
return backend_type;
}
Result NfcInterface::TranslateResultToServiceError(Result result) const {
const auto backend = GetBackendType();
if (result.IsSuccess()) {
return result;
}
if (result.module != ErrorModule::NFC) {
return result;
}
switch (backend) {
case BackendType::Mifare:
return TranslateResultToNfp(result);
case BackendType::Nfp: {
return TranslateResultToNfp(result);
}
default:
if (result != ResultUnknown216) {
return result;
}
return ResultUnknown74;
}
}
Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultDeviceNotFound) {
return NFP::ResultDeviceNotFound;
}
if (result == ResultInvalidArgument) {
return NFP::ResultInvalidArgument;
}
if (result == ResultWrongApplicationAreaSize) {
return NFP::ResultWrongApplicationAreaSize;
}
if (result == ResultWrongDeviceState) {
return NFP::ResultWrongDeviceState;
}
if (result == ResultUnknown74) {
return NFP::ResultUnknown74;
}
if (result == ResultNfcDisabled) {
return NFP::ResultNfcDisabled;
}
if (result == ResultNfcNotInitialized) {
return NFP::ResultNfcDisabled;
}
if (result == ResultWriteAmiiboFailed) {
return NFP::ResultWriteAmiiboFailed;
}
if (result == ResultTagRemoved) {
return NFP::ResultTagRemoved;
}
if (result == ResultRegistrationIsNotInitialized) {
return NFP::ResultRegistrationIsNotInitialized;
}
if (result == ResultApplicationAreaIsNotInitialized) {
return NFP::ResultApplicationAreaIsNotInitialized;
}
if (result == ResultCorruptedData) {
return NFP::ResultCorruptedData;
}
if (result == ResultWrongApplicationAreaId) {
return NFP::ResultWrongApplicationAreaId;
}
if (result == ResultApplicationAreaExist) {
return NFP::ResultApplicationAreaExist;
}
if (result == ResultNotAnAmiibo) {
return NFP::ResultNotAnAmiibo;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
Result NfcInterface::TranslateResultToMifare(Result result) const {
if (result == ResultDeviceNotFound) {
return Mifare::ResultDeviceNotFound;
}
if (result == ResultInvalidArgument) {
return Mifare::ResultInvalidArgument;
}
if (result == ResultWrongDeviceState) {
return Mifare::ResultWrongDeviceState;
}
if (result == ResultNfcDisabled) {
return Mifare::ResultNfcDisabled;
}
if (result == ResultTagRemoved) {
return Mifare::ResultTagRemoved;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
} // namespace Service::NFC

View File

@@ -3,26 +3,17 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class DeviceManager;
class IUser final : public ServiceFramework<IUser> {
class NfcInterface : public ServiceFramework<NfcInterface> {
public:
explicit IUser(Core::System& system_);
~IUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
explicit NfcInterface(Core::System& system_, const char* name, BackendType service_backend);
~NfcInterface();
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
@@ -37,16 +28,22 @@ private:
void GetTagInfo(HLERequestContext& ctx);
void AttachActivateEvent(HLERequestContext& ctx);
void AttachDeactivateEvent(HLERequestContext& ctx);
void ReadMifare(HLERequestContext& ctx);
void WriteMifare(HLERequestContext& ctx);
void SendCommandByPassThrough(HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
protected:
std::shared_ptr<DeviceManager> GetManager();
BackendType GetBackendType() const;
Result TranslateResultToServiceError(Result result) const;
Result TranslateResultToNfp(Result result) const;
Result TranslateResultToMifare(Result result) const;
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
BackendType backend_type;
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
std::shared_ptr<DeviceManager> device_manager = nullptr;
};
} // namespace Service::NFC

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -7,17 +7,22 @@
namespace Service::NFC {
constexpr Result DeviceNotFound(ErrorModule::NFC, 64);
constexpr Result InvalidArgument(ErrorModule::NFC, 65);
constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
constexpr Result NfcDisabled(ErrorModule::NFC, 80);
constexpr Result TagRemoved(ErrorModule::NFC, 97);
constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result MifareReadError(ErrorModule::NFCMifare, 288);
constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
} // namespace Service::NFC

View File

@@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::NFC {
enum class BackendType : u32 {
None,
Nfc,
Nfp,
Mifare,
};
// This is nn::nfc::DeviceState
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unavailable,
Finalized,
};
// This is nn::nfc::State
enum class State : u32 {
NonInitialized,
Initialized,
};
// This is nn::nfc::TagType
enum class TagType : u32 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
enum class PackedTagType : u8 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// This is nn::nfc::NfcProtocol
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class NfcProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony FeliCa
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
// this is nn::nfc::TestWaveType
enum class TestWaveType : u32 {
Unknown,
};
using UniqueSerialNumber = std::array<u8, 7>;
using UniqueSerialNumberExtension = std::array<u8, 3>;
// This is nn::nfc::DeviceHandle
using DeviceHandle = u64;
// This is nn::nfc::TagInfo
struct TagInfo {
UniqueSerialNumber uuid;
UniqueSerialNumberExtension uuid_extension;
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
NfcProtocol protocol;
TagType tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
} // namespace Service::NFC

View File

@@ -1,365 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
IUser::IUser(Core::System& system_)
: ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "InitializeOld"},
{1, &IUser::Finalize, "FinalizeOld"},
{2, &IUser::GetState, "GetStateOld"},
{3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &IUser::Initialize, "Initialize"},
{401, &IUser::Finalize, "Finalize"},
{402, &IUser::GetState, "GetState"},
{403, &IUser::IsNfcEnabled, "IsNfcEnabled"},
{404, &IUser::ListDevices, "ListDevices"},
{405, &IUser::GetDeviceState, "GetDeviceState"},
{406, &IUser::GetNpadId, "GetNpadId"},
{407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &IUser::StartDetection, "StartDetection"},
{409, &IUser::StopDetection, "StopDetection"},
{410, &IUser::GetTagInfo, "GetTagInfo"},
{411, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
IUser ::~IUser() {
availability_change_event->Close();
}
void IUser::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void IUser::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IUser::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void IUser::IsNfcEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(state != State::NonInitialized);
}
void IUser::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void IUser::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void IUser::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
void IUser::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(nfp_protocol);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, false);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void IUser::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
std::vector<u8> out_data(1);
// TODO: Request data from nfc device
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::optional<std::shared_ptr<NfcDevice>> IUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -13,7 +13,7 @@ class IUser final : public Interface {
public:
explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<IUser> functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
{2, &IUser::ListDevices, "ListDevices"},
@@ -50,7 +50,7 @@ class ISystem final : public Interface {
public:
explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<ISystem> functions[] = {
{0, &ISystem::InitializeSystem, "InitializeSystem"},
{1, &ISystem::FinalizeSystem, "FinalizeSystem"},
{2, &ISystem::ListDevices, "ListDevices"},
@@ -89,7 +89,7 @@ class IDebug final : public Interface {
public:
explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<IDebug> functions[] = {
{0, &IDebug::InitializeDebug, "InitializeDebug"},
{1, &IDebug::FinalizeDebug, "FinalizeDebug"},
{2, &IDebug::ListDevices, "ListDevices"},
@@ -126,9 +126,9 @@ public:
{201, &IDebug::SetAll, "SetAll"},
{202, &IDebug::FlushDebug, "FlushDebug"},
{203, &IDebug::BreakTag, "BreakTag"},
{204, nullptr, "ReadBackupData"},
{205, nullptr, "WriteBackupData"},
{206, nullptr, "WriteNtf"},
{204, &IDebug::ReadBackupData, "ReadBackupData"},
{205, &IDebug::WriteBackupData, "WriteBackupData"},
{206, &IDebug::WriteNtf, "WriteNtf"},
};
// clang-format on
@@ -152,16 +152,10 @@ private:
void CreateUserInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (user_interface == nullptr) {
user_interface = std::make_shared<IUser>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(user_interface);
rb.PushIpcInterface<IUser>(system);
}
std::shared_ptr<IUser> user_interface;
};
class ISystemManager final : public ServiceFramework<ISystemManager> {
@@ -180,16 +174,10 @@ private:
void CreateSystemInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (system_interface == nullptr) {
system_interface = std::make_shared<ISystem>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(system_interface);
rb.PushIpcInterface<ISystem>(system);
}
std::shared_ptr<ISystem> system_interface;
};
class IDebugManager final : public ServiceFramework<IDebugManager> {
@@ -208,16 +196,10 @@ private:
void CreateDebugInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (system_interface == nullptr) {
system_interface = std::make_shared<IDebug>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebug>(system_interface);
rb.PushIpcInterface<IDebug>(system);
}
std::shared_ptr<IDebug> system_interface;
};
void LoopProcess(Core::System& system) {

View File

@@ -1,120 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFP {
class NfpDevice {
public:
NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfpDevice();
void Initialize();
void Finalize();
Result StartDetection(TagProtocol allowed_protocol);
Result StopDetection();
Result Mount(MountTarget mount_target);
Result Unmount();
Result Flush();
Result FlushDebug();
Result FlushWithBreak(BreakType break_type);
Result GetTagInfo(TagInfo& tag_info) const;
Result GetCommonInfo(CommonInfo& common_info) const;
Result GetModelInfo(ModelInfo& model_info) const;
Result GetRegisterInfo(RegisterInfo& register_info) const;
Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const AmiiboName& amiibo_name);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::vector<u8>& data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
Result ExistApplicationArea(bool& has_application_area);
Result GetAll(NfpData& data) const;
Result SetAll(const NfpData& data);
Result BreakTag(BreakType break_type);
Result ReadBackupData();
Result WriteBackupData();
Result WriteNtf();
u64 GetHandle() const;
u32 GetApplicationAreaSize() const;
DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadAmiibo(std::span<const u8> data);
void CloseAmiibo();
AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
bool is_data_moddified{};
bool is_app_area_open{};
bool is_plain_amiibo{};
TagProtocol allowed_protocols{};
s64 current_posix_time{};
MountTarget mount_target{MountTarget::None};
DeviceState device_state{DeviceState::Unavailable};
NTAG215File tag_data{};
EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFP

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/service.h"
namespace Service::NFP {
class NfpDevice;
class Interface : public ServiceFramework<Interface> {
class Interface : public NFC::NfcInterface {
public:
explicit Interface(Core::System& system_, const char* name);
~Interface() override;
void Initialize(HLERequestContext& ctx);
void InitializeSystem(HLERequestContext& ctx);
void InitializeDebug(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void FinalizeSystem(HLERequestContext& ctx);
void FinalizeDebug(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
void Mount(HLERequestContext& ctx);
void Unmount(HLERequestContext& ctx);
void OpenApplicationArea(HLERequestContext& ctx);
@@ -35,17 +26,10 @@ public:
void Flush(HLERequestContext& ctx);
void Restore(HLERequestContext& ctx);
void CreateApplicationArea(HLERequestContext& ctx);
void GetTagInfo(HLERequestContext& ctx);
void GetRegisterInfo(HLERequestContext& ctx);
void GetCommonInfo(HLERequestContext& ctx);
void GetModelInfo(HLERequestContext& ctx);
void AttachActivateEvent(HLERequestContext& ctx);
void AttachDeactivateEvent(HLERequestContext& ctx);
void GetState(HLERequestContext& ctx);
void GetDeviceState(HLERequestContext& ctx);
void GetNpadId(HLERequestContext& ctx);
void GetApplicationAreaSize(HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
void RecreateApplicationArea(HLERequestContext& ctx);
void Format(HLERequestContext& ctx);
void GetAdminInfo(HLERequestContext& ctx);
@@ -61,21 +45,6 @@ public:
void ReadBackupData(HLERequestContext& ctx);
void WriteBackupData(HLERequestContext& ctx);
void WriteNtf(HLERequestContext& ctx);
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfpDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFP

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -7,18 +7,19 @@
namespace Service::NFP {
constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
constexpr Result InvalidArgument(ErrorModule::NFP, 65);
constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
constexpr Result NfcDisabled(ErrorModule::NFP, 80);
constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result TagRemoved(ErrorModule::NFP, 97);
constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result CorruptedData(ErrorModule::NFP, 144);
constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultDeviceNotFound(ErrorModule::NFP, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFP, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFP, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultNfcDisabled(ErrorModule::NFP, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
} // namespace Service::NFP

View File

@@ -7,32 +7,19 @@
#include "common/swap.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfc/nfc_types.h"
namespace Service::NFP {
static constexpr std::size_t amiibo_name_length = 0xA;
static constexpr std::size_t application_id_version_offset = 0x1c;
static constexpr std::size_t counter_limit = 0xffff;
enum class ServiceType : u32 {
User,
Debug,
System,
};
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unavailable,
Finalized,
};
// This is nn::nfp::ModelType
enum class ModelType : u32 {
Amiibo,
};
// This is nn::nfp::MountTarget
enum class MountTarget : u32 {
None,
Rom,
@@ -72,35 +59,6 @@ enum class AmiiboSeries : u8 {
Diablo,
};
enum class TagType : u32 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
enum class PackedTagType : u8 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class TagProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony Felica
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
enum class AppAreaVersion : u8 {
Nintendo3DS = 0,
NintendoWiiU = 1,
@@ -115,6 +73,11 @@ enum class BreakType : u32 {
Unknown2,
};
enum class WriteType : u32 {
Unknown0,
Unknown1,
};
enum class CabinetMode : u8 {
StartNicknameAndOwnerSettings,
StartGameDataEraser,
@@ -122,27 +85,16 @@ enum class CabinetMode : u8 {
StartFormatter,
};
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using UniqueSerialNumber = std::array<u8, 7>;
using LockBytes = std::array<u8, 2>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
// This is nn::nfp::TagInfo
using TagInfo = NFC::TagInfo;
struct TagUuid {
UniqueSerialNumber uid;
NFC::UniqueSerialNumber uid;
u8 nintendo_id;
LockBytes lock_bytes;
};
@@ -243,7 +195,7 @@ struct AmiiboModelInfo {
AmiiboType amiibo_type;
u16_be model_number;
AmiiboSeries series;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
INSERT_PADDING_BYTES(0x4); // Unknown
};
static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
@@ -298,7 +250,7 @@ struct NTAG215File {
u32_be register_info_crc;
ApplicationArea application_area; // Encrypted Game data
HashData hmac_tag; // Hash
UniqueSerialNumber uid; // Unique serial number
NFC::UniqueSerialNumber uid; // Unique serial number
u8 nintendo_id; // Tag UUID
AmiiboModelInfo model_info;
HashData keygen_salt; // Salt
@@ -326,17 +278,7 @@ static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File),
static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
"EncryptedNTAG215File must be trivially copyable.");
struct TagInfo {
UniqueSerialNumber uuid;
INSERT_PADDING_BYTES(0x3);
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
TagProtocol protocol;
TagType tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
// This is nn::nfp::CommonInfo
struct CommonInfo {
WriteDate last_write_date;
u16 write_counter;
@@ -347,6 +289,7 @@ struct CommonInfo {
};
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
// This is nn::nfp::ModelInfo
struct ModelInfo {
u16 character_id;
u8 character_variant;
@@ -357,6 +300,7 @@ struct ModelInfo {
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
// This is nn::nfp::RegisterInfo
struct RegisterInfo {
Service::Mii::CharInfo mii_char_info;
WriteDate creation_date;
@@ -366,6 +310,7 @@ struct RegisterInfo {
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
// This is nn::nfp::RegisterInfoPrivate
struct RegisterInfoPrivate {
Service::Mii::MiiStoreData mii_store_data;
WriteDate creation_date;
@@ -375,12 +320,13 @@ struct RegisterInfoPrivate {
};
static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
// This is nn::nfp::AdminInfo
struct AdminInfo {
u64 application_id;
u32 application_area_id;
u16 crc_change_counter;
u8 flags;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
AppAreaVersion app_area_version;
INSERT_PADDING_BYTES(0x7);
INSERT_PADDING_BYTES(0x28);
@@ -411,7 +357,7 @@ struct NfpData {
u32 access_id;
u16 settings_crc_counter;
u8 font_region;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
AppAreaVersion console_type;
u8 application_id_byte;
INSERT_PADDING_BYTES(0x2E);
@@ -420,37 +366,4 @@ struct NfpData {
static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
#pragma pack()
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFP

View File

@@ -218,7 +218,7 @@ public:
private:
void Submit(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
if (state == RequestState::NotSubmitted) {
UpdateState(RequestState::OnHold);
@@ -229,7 +229,7 @@ private:
}
void GetRequestState(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -237,7 +237,7 @@ private:
}
void GetResult(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
const auto result = [this] {
const auto has_connection = Network::GetHostIPv4Address().has_value();

View File

@@ -142,7 +142,8 @@ template <typename Self>
class ServiceFramework : public ServiceFrameworkBase {
protected:
/// Contains information about a request type which is handled by the service.
struct FunctionInfo : FunctionInfoBase {
template <typename T>
struct FunctionInfoTyped : FunctionInfoBase {
// TODO(yuriks): This function could be constexpr, but clang is the only compiler that
// doesn't emit an ICE or a wrong diagnostic because of the static_cast.
@@ -155,12 +156,13 @@ protected:
* the request
* @param name_ human-friendly name for the request. Used mostly for logging purposes.
*/
FunctionInfo(u32 expected_header_, HandlerFnP<Self> handler_callback_, const char* name_)
FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
: FunctionInfoBase{
expected_header_,
// Type-erase member function pointer by casting it down to the base class.
static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback_), name_} {}
};
using FunctionInfo = FunctionInfoTyped<Self>;
/**
* Initializes the handler with no functions installed.
@@ -175,8 +177,8 @@ protected:
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
/// Registers handlers in the service.
template <std::size_t N>
void RegisterHandlers(const FunctionInfo (&functions)[N]) {
template <typename T = Self, std::size_t N>
void RegisterHandlers(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlers(functions, N);
}
@@ -184,13 +186,14 @@ protected:
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
* overload in order to avoid needing to specify the array size.
*/
void RegisterHandlers(const FunctionInfo* functions, std::size_t n) {
template <typename T = Self>
void RegisterHandlers(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBase(functions, n);
}
/// Registers handlers in the service.
template <std::size_t N>
void RegisterHandlersTipc(const FunctionInfo (&functions)[N]) {
template <typename T = Self, std::size_t N>
void RegisterHandlersTipc(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlersTipc(functions, N);
}
@@ -198,7 +201,8 @@ protected:
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
* overload in order to avoid needing to specify the array size.
*/
void RegisterHandlersTipc(const FunctionInfo* functions, std::size_t n) {
template <typename T = Self>
void RegisterHandlersTipc(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBaseTipc(functions, n);
}

View File

@@ -356,7 +356,7 @@ NetworkInstance::~NetworkInstance() {
std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) {
LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface");
return {};
}

View File

@@ -200,7 +200,7 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
});
if (res == network_interfaces.end()) {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return std::nullopt;
}

View File

@@ -13,10 +13,12 @@
#include "common/swap.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_download_area.h"
namespace Core::Memory {
@@ -243,7 +245,7 @@ struct Memory::Impl {
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
const u8* const host_ptr) {
if constexpr (!UNSAFE) {
system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount);
HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
}
std::memcpy(dest_buffer, host_ptr, copy_amount);
},
@@ -334,7 +336,7 @@ struct Memory::Impl {
},
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
u8* const host_ptr) {
system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount);
HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
@@ -373,7 +375,7 @@ struct Memory::Impl {
const std::size_t block_size) {
// dc ivac: Invalidate to point of coherency
// GPU flush -> CPU invalidate
system.GPU().FlushRegion(GetInteger(current_vaddr), block_size);
HandleRasterizerDownload(GetInteger(current_vaddr), block_size);
};
return PerformCacheOperation(process, dest_addr, size, on_rasterizer);
}
@@ -462,7 +464,8 @@ struct Memory::Impl {
}
if (Settings::IsFastmemEnabled()) {
const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
const bool is_read_enable =
!Settings::values.use_reactive_flushing.GetValue() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}
@@ -651,7 +654,7 @@ struct Memory::Impl {
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8,
GetInteger(vaddr));
},
[&]() { system.GPU().FlushRegion(GetInteger(vaddr), sizeof(T)); });
[&]() { HandleRasterizerDownload(GetInteger(vaddr), sizeof(T)); });
if (ptr) {
std::memcpy(&result, ptr, sizeof(T));
}
@@ -712,7 +715,19 @@ struct Memory::Impl {
return true;
}
void HandleRasterizerDownload(VAddr address, size_t size) {
const size_t core = system.GetCurrentHostThreadID();
auto& current_area = rasterizer_areas[core];
const VAddr end_address = address + size;
if (current_area.start_address <= address && end_address <= current_area.end_address)
[[likely]] {
return;
}
current_area = system.GPU().OnCPURead(address, size);
}
Common::PageTable* current_page_table = nullptr;
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_areas{};
Core::System& system;
};

View File

@@ -85,6 +85,20 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
return "Unknown";
}
static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return "Immediate";
case Settings::VSyncMode::Mailbox:
return "Mailbox";
case Settings::VSyncMode::FIFO:
return "FIFO";
case Settings::VSyncMode::FIFORelaxed:
return "FIFO Relaxed";
}
return "Unknown";
}
u64 GetTelemetryId() {
u64 telemetry_id{};
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -241,7 +255,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
AddField(field_type, "Renderer_NvdecEmulation",
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseVsync",
TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
AddField(field_type, "Renderer_ShaderBackend",
static_cast<u32>(Settings::values.shader_backend.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousShaders",

View File

@@ -82,6 +82,9 @@ void MappingFactory::RegisterButton(const MappingData& data) {
new_input.Set("axis", data.index);
new_input.Set("threshold", 0.5f);
break;
case EngineInputType::Motion:
new_input.Set("motion", data.index);
break;
default:
return;
}

View File

@@ -667,7 +667,7 @@ public:
.raw_value = input_engine->GetAxis(identifier, axis_z),
.properties = properties_z,
};
status.delta_timestamp = 5000;
status.delta_timestamp = 1000;
status.force_update = true;
return status;
}
@@ -939,6 +939,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
.threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
.offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
.inverted = params.Get("invert", "+") == "-",
.inverted_button = params.Get("inverted", false) != 0,
.toggle = params.Get("toggle", false) != 0,
};
input_engine->PreSetController(identifier);

View File

@@ -535,12 +535,12 @@ TEST_CASE("MemoryTracker: Cached write downloads") {
memory_track->MarkRegionAsGpuModified(c + PAGE, PAGE);
int num = 0;
memory_track->ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 1);
REQUIRE(num == 0);
num = 0;
memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 0);
REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));
REQUIRE(memory_track->IsRegionGpuModified(c + PAGE, PAGE));
memory_track->FlushCachedWrites();
REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));

View File

@@ -18,6 +18,7 @@ namespace VideoCommon {
enum class BufferFlagBits {
Picked = 1 << 0,
CachedWrites = 1 << 1,
PreemtiveDownload = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
@@ -54,6 +55,10 @@ public:
flags |= BufferFlagBits::Picked;
}
void MarkPreemtiveDownload() noexcept {
flags |= BufferFlagBits::PreemtiveDownload;
}
/// Unmark buffer as picked
void Unpick() noexcept {
flags &= ~BufferFlagBits::Picked;
@@ -84,6 +89,10 @@ public:
return True(flags & BufferFlagBits::CachedWrites);
}
bool IsPreemtiveDownload() const noexcept {
return True(flags & BufferFlagBits::PreemtiveDownload);
}
/// Returns the base CPU address of the buffer
[[nodiscard]] VAddr CpuAddr() const noexcept {
return cpu_addr;

View File

@@ -23,8 +23,6 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
common_ranges.clear();
inline_buffer_id = NULL_BUFFER_ID;
active_async_buffers = !Settings::IsGPULevelHigh();
if (!runtime.CanReportMemoryUsage()) {
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
@@ -75,8 +73,6 @@ void BufferCache<P>::TickFrame() {
uniform_cache_hits[0] = 0;
uniform_cache_shots[0] = 0;
active_async_buffers = !Settings::IsGPULevelHigh();
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
@@ -111,9 +107,25 @@ void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) {
template <class P>
void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
memory_tracker.CachedCpuWrite(cpu_addr, size);
const IntervalType add_interval{Common::AlignDown(cpu_addr, YUZU_PAGESIZE),
Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE)};
cached_ranges.add(add_interval);
}
template <class P>
std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(VAddr cpu_addr,
u64 size) {
std::optional<VideoCore::RasterizerDownloadArea> area{};
area.emplace();
VAddr cpu_addr_start_aligned = Common::AlignDown(cpu_addr, Core::Memory::YUZU_PAGESIZE);
VAddr cpu_addr_end_aligned = Common::AlignUp(cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
area->start_address = cpu_addr_start_aligned;
area->end_address = cpu_addr_end_aligned;
if (memory_tracker.IsRegionPreflushable(cpu_addr, size)) {
area->preemtive = true;
return area;
};
memory_tracker.MarkRegionAsPreflushable(cpu_addr_start_aligned,
cpu_addr_end_aligned - cpu_addr_start_aligned);
area->preemtive = !IsRegionGpuModified(cpu_addr, size);
return area;
}
template <class P>
@@ -205,7 +217,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
if (has_new_downloads) {
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
}
std::vector<u8> tmp_buffer(amount);
tmp_buffer.resize(amount);
cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
return true;
@@ -441,9 +453,7 @@ void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_add
template <class P>
void BufferCache<P>::FlushCachedWrites() {
cached_write_buffer_ids.clear();
memory_tracker.FlushCachedWrites();
cached_ranges.clear();
}
template <class P>
@@ -474,9 +484,8 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
if (committed_ranges.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
if (active_async_buffers) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return;
}
@@ -537,64 +546,65 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
committed_ranges.clear();
if (downloads.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
if (active_async_buffers) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return;
}
if (active_async_buffers) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if (!Settings::IsGPULevelHigh()) {
committed_ranges.clear();
uncommitted_ranges.clear();
}
} else {
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies,
false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset,
immediate_buffer.subspan(0, copy.size));
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
}
}
}
}
@@ -1629,7 +1639,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
replace(transform_feedback_buffers);
replace(compute_uniform_buffers);
replace(compute_storage_buffers);
std::erase(cached_write_buffer_ids, buffer_id);
// Mark the whole buffer as CPU written to stop tracking CPU writes
if (!do_not_mark) {

View File

@@ -188,6 +188,8 @@ public:
void DownloadMemory(VAddr cpu_addr, u64 size);
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
@@ -541,8 +543,6 @@ private:
std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>, Empty>
uniform_buffer_binding_sizes{};
std::vector<BufferId> cached_write_buffer_ids;
MemoryTracker memory_tracker;
IntervalSet uncommitted_ranges;
IntervalSet common_ranges;
@@ -572,9 +572,8 @@ private:
u64 critical_memory = 0;
BufferId inline_buffer_id;
bool active_async_buffers = false;
std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
std::vector<u8> tmp_buffer;
};
} // namespace VideoCommon

View File

@@ -66,6 +66,14 @@ public:
});
}
/// Returns true if a region has been marked as Preflushable
[[nodiscard]] bool IsRegionPreflushable(VAddr query_cpu_addr, u64 query_size) noexcept {
return IteratePages<false>(
query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
return manager->template IsRegionModified<Type::Preflushable>(offset, size);
});
}
/// Mark region as CPU modified, notifying the rasterizer about this change
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
IteratePages<true>(dirty_cpu_addr, query_size,
@@ -93,6 +101,15 @@ public:
});
}
/// Mark region as modified from the host GPU
void MarkRegionAsPreflushable(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::Preflushable, true>(
manager->GetCpuAddr() + offset, size);
});
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
@@ -102,6 +119,15 @@ public:
});
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsPreflushable(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::Preflushable, false>(
manager->GetCpuAddr() + offset, size);
});
}
/// Mark region as modified from the CPU
/// but don't mark it as modified until FlusHCachedWrites is called.
void CachedCpuWrite(VAddr dirty_cpu_addr, u64 query_size) {

View File

@@ -26,6 +26,7 @@ enum class Type {
GPU,
CachedCPU,
Untracked,
Preflushable,
};
/// Vector tracking modified pages tightly packed with small vector optimization
@@ -55,17 +56,20 @@ struct Words {
gpu.stack.fill(0);
cached_cpu.stack.fill(0);
untracked.stack.fill(~u64{0});
preflushable.stack.fill(0);
} else {
// Share allocation between CPU and GPU pages and set their default values
u64* const alloc = new u64[num_words * 4];
u64* const alloc = new u64[num_words * 5];
cpu.heap = alloc;
gpu.heap = alloc + num_words;
cached_cpu.heap = alloc + num_words * 2;
untracked.heap = alloc + num_words * 3;
preflushable.heap = alloc + num_words * 4;
std::fill_n(cpu.heap, num_words, ~u64{0});
std::fill_n(gpu.heap, num_words, 0);
std::fill_n(cached_cpu.heap, num_words, 0);
std::fill_n(untracked.heap, num_words, ~u64{0});
std::fill_n(preflushable.heap, num_words, 0);
}
// Clean up tailing bits
const u64 last_word_size = size_bytes % BYTES_PER_WORD;
@@ -88,13 +92,14 @@ struct Words {
gpu = rhs.gpu;
cached_cpu = rhs.cached_cpu;
untracked = rhs.untracked;
preflushable = rhs.preflushable;
rhs.cpu.heap = nullptr;
return *this;
}
Words(Words&& rhs) noexcept
: size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu},
cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked} {
cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked}, preflushable{rhs.preflushable} {
rhs.cpu.heap = nullptr;
}
@@ -129,6 +134,8 @@ struct Words {
return std::span<u64>(cached_cpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Untracked) {
return std::span<u64>(untracked.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Preflushable) {
return std::span<u64>(preflushable.Pointer(IsShort()), num_words);
}
}
@@ -142,6 +149,8 @@ struct Words {
return std::span<const u64>(cached_cpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Untracked) {
return std::span<const u64>(untracked.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Preflushable) {
return std::span<const u64>(preflushable.Pointer(IsShort()), num_words);
}
}
@@ -151,6 +160,7 @@ struct Words {
WordsArray<stack_words> gpu;
WordsArray<stack_words> cached_cpu;
WordsArray<stack_words> untracked;
WordsArray<stack_words> preflushable;
};
template <class RasterizerInterface, size_t stack_words = 1>
@@ -292,6 +302,9 @@ public:
(pending_pointer - pending_offset) * BYTES_PER_PAGE);
};
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
}
const u64 word = state_words[index] & mask;
if constexpr (clear) {
if constexpr (type == Type::CPU || type == Type::CachedCPU) {
@@ -340,8 +353,13 @@ public:
static_assert(type != Type::Untracked);
const std::span<const u64> state_words = words.template Span<type>();
[[maybe_unused]] const std::span<const u64> untracked_words =
words.template Span<Type::Untracked>();
bool result = false;
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
}
const u64 word = state_words[index] & mask;
if (word != 0) {
result = true;
@@ -362,9 +380,14 @@ public:
[[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
static_assert(type != Type::Untracked);
const std::span<const u64> state_words = words.template Span<type>();
[[maybe_unused]] const std::span<const u64> untracked_words =
words.template Span<Type::Untracked>();
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
}
const u64 word = state_words[index] & mask;
if (word == 0) {
return;

View File

@@ -126,15 +126,14 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{
PixelFormat::ASTC_2D_8X8_SRGB,
};
// Missing formats:
// PixelFormat::ASTC_2D_10X5_UNORM
// PixelFormat::ASTC_2D_10X5_SRGB
// Missing formats:
// PixelFormat::ASTC_2D_10X6_SRGB
constexpr std::array VIEW_CLASS_ASTC_10x5_RGBA{
PixelFormat::ASTC_2D_10X5_UNORM,
PixelFormat::ASTC_2D_10X5_SRGB,
};
constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{
PixelFormat::ASTC_2D_10X6_UNORM,
PixelFormat::ASTC_2D_10X6_SRGB,
};
constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{
@@ -147,9 +146,10 @@ constexpr std::array VIEW_CLASS_ASTC_10x10_RGBA{
PixelFormat::ASTC_2D_10X10_SRGB,
};
// Missing formats
// ASTC_2D_12X10_UNORM,
// ASTC_2D_12X10_SRGB,
constexpr std::array VIEW_CLASS_ASTC_12x10_RGBA{
PixelFormat::ASTC_2D_12X10_UNORM,
PixelFormat::ASTC_2D_12X10_SRGB,
};
constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{
PixelFormat::ASTC_2D_12X12_UNORM,
@@ -229,9 +229,11 @@ constexpr Table MakeViewTable() {
EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x5_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_12x10_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
return view;
}

View File

@@ -223,7 +223,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
write_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(dst_operand.address, write_buffer.data(), dst_size);
memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size);
UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
@@ -288,11 +288,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
write_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
}
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,

View File

@@ -59,6 +59,11 @@ public:
buffer_cache.AccumulateFlushes();
}
void SignalReference() {
std::function<void()> do_nothing([] {});
SignalFence(std::move(do_nothing));
}
void SyncOperation(std::function<void()>&& func) {
uncommitted_operations.emplace_back(std::move(func));
}

View File

@@ -283,6 +283,21 @@ struct GPU::Impl {
gpu_thread.FlushRegion(addr, size);
}
VideoCore::RasterizerDownloadArea OnCPURead(VAddr addr, u64 size) {
auto raster_area = rasterizer->GetFlushArea(addr, size);
if (raster_area.preemtive) {
return raster_area;
}
raster_area.preemtive = true;
const u64 fence = RequestSyncOperation([this, &raster_area]() {
rasterizer->FlushRegion(raster_area.start_address,
raster_area.end_address - raster_area.start_address);
});
gpu_thread.TickGPU();
WaitForSyncOperation(fence);
return raster_area;
}
/// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(VAddr addr, u64 size) {
gpu_thread.InvalidateRegion(addr, size);
@@ -538,6 +553,10 @@ void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
impl->SwapBuffers(framebuffer);
}
VideoCore::RasterizerDownloadArea GPU::OnCPURead(VAddr addr, u64 size) {
return impl->OnCPURead(addr, size);
}
void GPU::FlushRegion(VAddr addr, u64 size) {
impl->FlushRegion(addr, size);
}

View File

@@ -10,6 +10,7 @@
#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/cdma_pusher.h"
#include "video_core/framebuffer_config.h"
#include "video_core/rasterizer_download_area.h"
namespace Core {
class System;
@@ -240,6 +241,9 @@ public:
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
[[nodiscard]] VideoCore::RasterizerDownloadArea OnCPURead(VAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
void FlushRegion(VAddr addr, u64 size);

View File

@@ -255,7 +255,6 @@ private:
if (!in_range(query)) {
continue;
}
rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1);
AsyncJobId async_job_id = query.GetAsyncJob();
auto flush_result = query.Flush(async);
if (async_job_id == NULL_ASYNC_JOB_ID) {
@@ -273,7 +272,6 @@ private:
/// Registers the passed parameters as cached and returns a pointer to the stored cached query.
CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) {
rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1);
const u64 page = static_cast<u64>(cpu_addr) >> YUZU_PAGEBITS;
return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr,
host_ptr);

View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
namespace VideoCore {
struct RasterizerDownloadArea {
VAddr start_address;
VAddr end_address;
bool preemtive;
};
} // namespace VideoCore

View File

@@ -12,6 +12,7 @@
#include "video_core/cache_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_download_area.h"
namespace Tegra {
class MemoryManager;
@@ -95,6 +96,8 @@ public:
virtual bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
virtual RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) = 0;
/// Notify rasterizer that any caches of the specified region should be invalidated
virtual void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "core/memory.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_null/null_rasterizer.h"
@@ -46,6 +48,14 @@ bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheTyp
}
void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) {
VideoCore::RasterizerDownloadArea new_area{
.start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE),
.end_address = Common::AlignUp(addr + size, Core::Memory::YUZU_PAGESIZE),
.preemtive = true,
};
return new_area;
}
void RasterizerNull::InvalidateGPUCache() {}
void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {}

View File

@@ -54,6 +54,7 @@ public:
void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void OnCPUWrite(VAddr addr, u64 size) override;
VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override;
void InvalidateGPUCache() override;
void UnmapMemory(VAddr addr, u64 size) override;
void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override;

View File

@@ -108,7 +108,8 @@ bool IsASTCSupported() {
[[nodiscard]] bool IsDebugToolAttached(std::span<const std::string_view> extensions) {
const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
return nsight || HasExtension(extensions, "GL_EXT_debug_tool");
return nsight || HasExtension(extensions, "GL_EXT_debug_tool") ||
Settings::values.renderer_debug.GetValue();
}
} // Anonymous namespace

View File

@@ -433,6 +433,29 @@ bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheT
return false;
}
VideoCore::RasterizerDownloadArea RasterizerOpenGL::GetFlushArea(VAddr addr, u64 size) {
{
std::scoped_lock lock{texture_cache.mutex};
auto area = texture_cache.GetFlushArea(addr, size);
if (area) {
return *area;
}
}
{
std::scoped_lock lock{buffer_cache.mutex};
auto area = buffer_cache.GetFlushArea(addr, size);
if (area) {
return *area;
}
}
VideoCore::RasterizerDownloadArea new_area{
.start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE),
.end_address = Common::AlignUp(addr + size, Core::Memory::YUZU_PAGESIZE),
.preemtive = true,
};
return new_area;
}
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (addr == 0 || size == 0) {
@@ -1281,7 +1304,7 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
const Tegra::DMA::BufferOperand& buffer_operand,
const Tegra::DMA::ImageOperand& image_operand) {
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
const auto image_id = texture_cache.DmaImageId(image_operand);
const auto image_id = texture_cache.DmaImageId(image_operand, IS_IMAGE_UPLOAD);
if (image_id == VideoCommon::NULL_IMAGE_ID) {
return false;
}

View File

@@ -95,6 +95,7 @@ public:
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void OnCPUWrite(VAddr addr, u64 size) override;

View File

@@ -861,9 +861,12 @@ GLuint Image::StorageHandle() noexcept {
case PixelFormat::ASTC_2D_8X5_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_10X5_SRGB:
case PixelFormat::ASTC_2D_10X6_SRGB:
case PixelFormat::ASTC_2D_10X8_SRGB:
case PixelFormat::ASTC_2D_6X6_SRGB:
case PixelFormat::ASTC_2D_10X10_SRGB:
case PixelFormat::ASTC_2D_12X10_SRGB:
case PixelFormat::ASTC_2D_12X12_SRGB:
case PixelFormat::ASTC_2D_8X6_SRGB:
case PixelFormat::ASTC_2D_6X5_SRGB:
@@ -1123,7 +1126,8 @@ bool Image::ScaleDown(bool ignore) {
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
ImageId image_id_, Image& image, const SlotVector<Image>&)
: VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} {
: VideoCommon::ImageViewBase{info, image.info, image_id_, image.gpu_addr},
views{runtime.null_image_views} {
const Device& device = runtime.device;
if (True(image.flags & ImageFlagBits::Converted)) {
internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
@@ -1214,12 +1218,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
: VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
const VideoCommon::ImageViewInfo& view_info)
: VideoCommon::ImageViewBase{info, view_info} {}
: VideoCommon::ImageViewBase{info, view_info, 0} {}
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
: VideoCommon::ImageViewBase{params}, views{runtime.null_image_views} {}
@@ -1279,7 +1283,7 @@ GLuint ImageView::MakeView(Shader::TextureType view_type, GLenum view_format) {
ApplySwizzle(view.handle, format, casted_swizzle);
}
if (set_object_label) {
const std::string name = VideoCommon::Name(*this);
const std::string name = VideoCommon::Name(*this, gpu_addr);
glObjectLabel(GL_TEXTURE, view.handle, static_cast<GLsizei>(name.size()), name.data());
}
return view.handle;

View File

@@ -314,7 +314,6 @@ private:
std::unique_ptr<StorageViews> storage_views;
GLenum internal_format = GL_NONE;
GLuint default_handle = 0;
GPUVAddr gpu_addr = 0;
u32 buffer_size = 0;
GLuint original_texture = 0;
int num_samples = 0;

View File

@@ -100,10 +100,13 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
{GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}, // ASTC_2D_10X6_SRGB
{GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB
{GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
{GL_COMPRESSED_RGBA_ASTC_12x10_KHR}, // ASTC_2D_12X10_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}, // ASTC_2D_12X10_SRGB
{GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB
{GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM

View File

@@ -197,10 +197,13 @@ struct FormatTuple {
{VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM
{VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB
{VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM
{VK_FORMAT_ASTC_10x6_SRGB_BLOCK}, // ASTC_2D_10X6_SRGB
{VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM
{VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB
{VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM
{VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB
{VK_FORMAT_ASTC_12x10_UNORM_BLOCK}, // ASTC_2D_12X10_UNORM
{VK_FORMAT_ASTC_12x10_SRGB_BLOCK}, // ASTC_2D_12X10_SRGB
{VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM
{VK_FORMAT_ASTC_12x12_SRGB_BLOCK}, // ASTC_2D_12X12_SRGB
{VK_FORMAT_ASTC_8x6_UNORM_BLOCK}, // ASTC_2D_8X6_UNORM

View File

@@ -88,7 +88,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
Settings::values.renderer_debug.GetValue())),
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
surface(CreateSurface(instance, render_window)),
surface(CreateSurface(instance, render_window.GetWindowInfo())),
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
state_tracker(), scheduler(device, state_tracker),
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,

View File

@@ -502,6 +502,22 @@ bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheT
return false;
}
VideoCore::RasterizerDownloadArea RasterizerVulkan::GetFlushArea(VAddr addr, u64 size) {
{
std::scoped_lock lock{texture_cache.mutex};
auto area = texture_cache.GetFlushArea(addr, size);
if (area) {
return *area;
}
}
VideoCore::RasterizerDownloadArea new_area{
.start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE),
.end_address = Common::AlignUp(addr + size, Core::Memory::YUZU_PAGESIZE),
.preemtive = true,
};
return new_area;
}
void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if (addr == 0 || size == 0) {
return;
@@ -598,7 +614,7 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) {
}
void RasterizerVulkan::SignalReference() {
fence_manager.SignalOrdering();
fence_manager.SignalReference();
}
void RasterizerVulkan::ReleaseFences() {
@@ -631,7 +647,7 @@ void RasterizerVulkan::WaitForIdle() {
cmdbuf.SetEvent(event, flags);
cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {});
});
SignalReference();
fence_manager.SignalOrdering();
}
void RasterizerVulkan::FragmentBarrier() {
@@ -777,7 +793,7 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
const Tegra::DMA::BufferOperand& buffer_operand,
const Tegra::DMA::ImageOperand& image_operand) {
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
const auto image_id = texture_cache.DmaImageId(image_operand);
const auto image_id = texture_cache.DmaImageId(image_operand, IS_IMAGE_UPLOAD);
if (image_id == VideoCommon::NULL_IMAGE_ID) {
return false;
}

View File

@@ -92,6 +92,7 @@ public:
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
bool MustFlushRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override;

View File

@@ -14,6 +14,7 @@
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "vulkan/vulkan_core.h"
namespace Vulkan {
@@ -33,23 +34,47 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
return found != formats.end() ? *found : formats[0];
}
VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
// Mailbox (triple buffering) doesn't lock the application like fifo (vsync),
// prefer it if vsync option is not selected
const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Borderless &&
found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) {
return VK_PRESENT_MODE_MAILBOX_KHR;
}
if (!Settings::values.use_speed_limit.GetValue()) {
// FIFO present mode locks the framerate to the monitor's refresh rate,
// Find an alternative to surpass this limitation if FPS is unlocked.
const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
if (found_imm != modes.end()) {
return VK_PRESENT_MODE_IMMEDIATE_KHR;
static constexpr VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
bool has_fifo_relaxed) {
// Mailbox doesn't lock the application like FIFO (vsync)
// FIFO present mode locks the framerate to the monitor's refresh rate
Settings::VSyncMode setting = [has_imm, has_mailbox]() {
// Choose Mailbox or Immediate if unlocked and those modes are supported
const auto mode = Settings::values.vsync_mode.GetValue();
if (Settings::values.use_speed_limit.GetValue()) {
return mode;
}
switch (mode) {
case Settings::VSyncMode::FIFO:
case Settings::VSyncMode::FIFORelaxed:
if (has_mailbox) {
return Settings::VSyncMode::Mailbox;
} else if (has_imm) {
return Settings::VSyncMode::Immediate;
}
[[fallthrough]];
default:
return mode;
}
}();
if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
(setting == Settings::VSyncMode::Immediate && !has_imm) ||
(setting == Settings::VSyncMode::FIFORelaxed && !has_fifo_relaxed)) {
setting = Settings::VSyncMode::FIFO;
}
switch (setting) {
case Settings::VSyncMode::Immediate:
return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::FIFO:
return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FIFORelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default:
return VK_PRESENT_MODE_FIFO_KHR;
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
@@ -167,11 +192,17 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
const auto physical_device{device.GetPhysical()};
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface);
has_mailbox = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_MAILBOX_KHR) != present_modes.end();
has_imm = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.end();
has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_FIFO_RELAXED_KHR) != present_modes.end();
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
surface_format = ChooseSwapSurfaceFormat(formats);
present_mode = ChooseSwapPresentMode(present_modes);
present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
u32 requested_image_count{capabilities.minImageCount + 1};
// Ensure Triple buffering if possible.
@@ -232,7 +263,6 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
extent = swapchain_ci.imageExtent;
current_srgb = srgb;
current_fps_unlocked = !Settings::values.use_speed_limit.GetValue();
images = swapchain.GetImages();
image_count = static_cast<u32>(images.size());
@@ -254,14 +284,9 @@ void Swapchain::Destroy() {
swapchain.reset();
}
bool Swapchain::HasFpsUnlockChanged() const {
return current_fps_unlocked != !Settings::values.use_speed_limit.GetValue();
}
bool Swapchain::NeedsPresentModeUpdate() const {
// Mailbox present mode is the ideal for all scenarios. If it is not available,
// A different present mode is needed to support unlocked FPS above the monitor's refresh rate.
return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
return present_mode != requested_mode;
}
} // namespace Vulkan

View File

@@ -116,8 +116,6 @@ private:
void Destroy();
bool HasFpsUnlockChanged() const;
bool NeedsPresentModeUpdate() const;
const VkSurfaceKHR surface;
@@ -142,9 +140,11 @@ private:
VkExtent2D extent{};
VkPresentModeKHR present_mode{};
VkSurfaceFormatKHR surface_format{};
bool has_imm{false};
bool has_mailbox{false};
bool has_fifo_relaxed{false};
bool current_srgb{};
bool current_fps_unlocked{};
bool is_outdated{};
bool is_suboptimal{};
};

View File

@@ -1584,8 +1584,9 @@ bool Image::NeedsScaleHelper() const {
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
ImageId image_id_, Image& image)
: VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device},
image_handle{image.Handle()}, samples(ConvertSampleCount(image.info.num_samples)) {
: VideoCommon::ImageViewBase{info, image.info, image_id_, image.gpu_addr},
device{&runtime.device}, image_handle{image.Handle()},
samples(ConvertSampleCount(image.info.num_samples)) {
using Shader::TextureType;
const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
@@ -1631,7 +1632,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
}
vk::ImageView handle = device->GetLogical().CreateImageView(ci);
if (device->HasDebuggingToolAttached()) {
handle.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
handle.SetObjectNameEXT(VideoCommon::Name(*this, gpu_addr).c_str());
}
image_views[static_cast<size_t>(tex_type)] = std::move(handle);
};
@@ -1672,7 +1673,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
: VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params)

View File

@@ -265,7 +265,6 @@ private:
VkImage image_handle = VK_NULL_HANDLE;
VkImageView render_target = VK_NULL_HANDLE;
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
GPUVAddr gpu_addr = 0;
u32 buffer_size = 0;
};

View File

@@ -228,14 +228,14 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu
auto info = std::make_unique<ShaderInfo>();
if (const std::optional<u64> cached_hash{env.Analyze()}) {
info->unique_hash = *cached_hash;
info->size_bytes = env.CachedSize();
info->size_bytes = env.CachedSizeBytes();
} else {
// Slow path, not really hit on commercial games
// Build a control flow graph to get the real shader size
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
info->unique_hash = env.CalculateHash();
info->size_bytes = env.ReadSize();
info->size_bytes = env.ReadSizeBytes();
}
const size_t size_bytes{info->size_bytes};
const ShaderInfo* const result{info.get()};

View File

@@ -170,15 +170,19 @@ std::optional<u64> GenericEnvironment::Analyze() {
void GenericEnvironment::SetCachedSize(size_t size_bytes) {
cached_lowest = start_address;
cached_highest = start_address + static_cast<u32>(size_bytes);
code.resize(CachedSize());
code.resize(CachedSizeWords());
gpu_memory->ReadBlock(program_base + cached_lowest, code.data(), code.size() * sizeof(u64));
}
size_t GenericEnvironment::CachedSize() const noexcept {
return cached_highest - cached_lowest + INST_SIZE;
size_t GenericEnvironment::CachedSizeWords() const noexcept {
return CachedSizeBytes() / INST_SIZE;
}
size_t GenericEnvironment::ReadSize() const noexcept {
size_t GenericEnvironment::CachedSizeBytes() const noexcept {
return static_cast<size_t>(cached_highest) - cached_lowest + INST_SIZE;
}
size_t GenericEnvironment::ReadSizeBytes() const noexcept {
return read_highest - read_lowest + INST_SIZE;
}
@@ -187,7 +191,7 @@ bool GenericEnvironment::CanBeSerialized() const noexcept {
}
u64 GenericEnvironment::CalculateHash() const {
const size_t size{ReadSize()};
const size_t size{ReadSizeBytes()};
const auto data{std::make_unique<char[]>(size)};
gpu_memory->ReadBlock(program_base + read_lowest, data.get(), size);
return Common::CityHash64(data.get(), size);
@@ -198,7 +202,7 @@ void GenericEnvironment::Dump(u64 hash) {
}
void GenericEnvironment::Serialize(std::ofstream& file) const {
const u64 code_size{static_cast<u64>(CachedSize())};
const u64 code_size{static_cast<u64>(CachedSizeBytes())};
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};

View File

@@ -48,9 +48,11 @@ public:
void SetCachedSize(size_t size_bytes);
[[nodiscard]] size_t CachedSize() const noexcept;
[[nodiscard]] size_t CachedSizeWords() const noexcept;
[[nodiscard]] size_t ReadSize() const noexcept;
[[nodiscard]] size_t CachedSizeBytes() const noexcept;
[[nodiscard]] size_t ReadSizeBytes() const noexcept;
[[nodiscard]] bool CanBeSerialized() const noexcept;

View File

@@ -250,10 +250,13 @@ bool IsPixelFormatASTC(PixelFormat format) {
case PixelFormat::ASTC_2D_6X6_UNORM:
case PixelFormat::ASTC_2D_6X6_SRGB:
case PixelFormat::ASTC_2D_10X6_UNORM:
case PixelFormat::ASTC_2D_10X6_SRGB:
case PixelFormat::ASTC_2D_10X5_UNORM:
case PixelFormat::ASTC_2D_10X5_SRGB:
case PixelFormat::ASTC_2D_10X10_UNORM:
case PixelFormat::ASTC_2D_10X10_SRGB:
case PixelFormat::ASTC_2D_12X10_UNORM:
case PixelFormat::ASTC_2D_12X10_SRGB:
case PixelFormat::ASTC_2D_12X12_UNORM:
case PixelFormat::ASTC_2D_12X12_SRGB:
case PixelFormat::ASTC_2D_8X6_UNORM:
@@ -279,11 +282,13 @@ bool IsPixelFormatSRGB(PixelFormat format) {
case PixelFormat::ASTC_2D_8X5_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_10X6_SRGB:
case PixelFormat::ASTC_2D_10X8_SRGB:
case PixelFormat::ASTC_2D_6X6_SRGB:
case PixelFormat::ASTC_2D_10X5_SRGB:
case PixelFormat::ASTC_2D_10X10_SRGB:
case PixelFormat::ASTC_2D_12X12_SRGB:
case PixelFormat::ASTC_2D_12X10_SRGB:
case PixelFormat::ASTC_2D_8X6_SRGB:
case PixelFormat::ASTC_2D_6X5_SRGB:
return true;

View File

@@ -95,10 +95,13 @@ enum class PixelFormat {
ASTC_2D_6X6_UNORM,
ASTC_2D_6X6_SRGB,
ASTC_2D_10X6_UNORM,
ASTC_2D_10X6_SRGB,
ASTC_2D_10X5_UNORM,
ASTC_2D_10X5_SRGB,
ASTC_2D_10X10_UNORM,
ASTC_2D_10X10_SRGB,
ASTC_2D_12X10_UNORM,
ASTC_2D_12X10_SRGB,
ASTC_2D_12X12_UNORM,
ASTC_2D_12X12_SRGB,
ASTC_2D_8X6_UNORM,
@@ -232,10 +235,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
6, // ASTC_2D_6X6_UNORM
6, // ASTC_2D_6X6_SRGB
10, // ASTC_2D_10X6_UNORM
10, // ASTC_2D_10X6_SRGB
10, // ASTC_2D_10X5_UNORM
10, // ASTC_2D_10X5_SRGB
10, // ASTC_2D_10X10_UNORM
10, // ASTC_2D_10X10_SRGB
12, // ASTC_2D_12X10_UNORM
12, // ASTC_2D_12X10_SRGB
12, // ASTC_2D_12X12_UNORM
12, // ASTC_2D_12X12_SRGB
8, // ASTC_2D_8X6_UNORM
@@ -338,10 +344,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
6, // ASTC_2D_6X6_UNORM
6, // ASTC_2D_6X6_SRGB
6, // ASTC_2D_10X6_UNORM
6, // ASTC_2D_10X6_SRGB
5, // ASTC_2D_10X5_UNORM
5, // ASTC_2D_10X5_SRGB
10, // ASTC_2D_10X10_UNORM
10, // ASTC_2D_10X10_SRGB
10, // ASTC_2D_12X10_UNORM
10, // ASTC_2D_12X10_SRGB
12, // ASTC_2D_12X12_UNORM
12, // ASTC_2D_12X12_SRGB
6, // ASTC_2D_8X6_UNORM
@@ -444,10 +453,13 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
128, // ASTC_2D_6X6_UNORM
128, // ASTC_2D_6X6_SRGB
128, // ASTC_2D_10X6_UNORM
128, // ASTC_2D_10X6_SRGB
128, // ASTC_2D_10X5_UNORM
128, // ASTC_2D_10X5_SRGB
128, // ASTC_2D_10X10_UNORM
128, // ASTC_2D_10X10_SRGB
128, // ASTC_2D_12X10_UNORM
128, // ASTC_2D_12X10_SRGB
128, // ASTC_2D_12X12_UNORM
128, // ASTC_2D_12X12_SRGB
128, // ASTC_2D_8X6_UNORM

View File

@@ -210,6 +210,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
return PixelFormat::ASTC_2D_6X6_SRGB;
case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
return PixelFormat::ASTC_2D_10X6_UNORM;
case Hash(TextureFormat::ASTC_2D_10X6, UNORM, SRGB):
return PixelFormat::ASTC_2D_10X6_SRGB;
case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR):
return PixelFormat::ASTC_2D_10X5_UNORM;
case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB):
@@ -218,6 +220,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
return PixelFormat::ASTC_2D_10X10_UNORM;
case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
return PixelFormat::ASTC_2D_10X10_SRGB;
case Hash(TextureFormat::ASTC_2D_12X10, UNORM, LINEAR):
return PixelFormat::ASTC_2D_12X10_UNORM;
case Hash(TextureFormat::ASTC_2D_12X10, UNORM, SRGB):
return PixelFormat::ASTC_2D_12X10_SRGB;
case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR):
return PixelFormat::ASTC_2D_12X12_UNORM;
case Hash(TextureFormat::ASTC_2D_12X12, UNORM, SRGB):

View File

@@ -46,7 +46,7 @@ std::string Name(const ImageBase& image) {
return "Invalid";
}
std::string Name(const ImageViewBase& image_view) {
std::string Name(const ImageViewBase& image_view, GPUVAddr addr) {
const u32 width = image_view.size.width;
const u32 height = image_view.size.height;
const u32 depth = image_view.size.depth;
@@ -56,23 +56,25 @@ std::string Name(const ImageViewBase& image_view) {
const std::string level = num_levels > 1 ? fmt::format(":{}", num_levels) : "";
switch (image_view.type) {
case ImageViewType::e1D:
return fmt::format("ImageView 1D {}{}", width, level);
return fmt::format("ImageView 1D 0x{:X} {}{}", addr, width, level);
case ImageViewType::e2D:
return fmt::format("ImageView 2D {}x{}{}", width, height, level);
return fmt::format("ImageView 2D 0x{:X} {}x{}{}", addr, width, height, level);
case ImageViewType::Cube:
return fmt::format("ImageView Cube {}x{}{}", width, height, level);
return fmt::format("ImageView Cube 0x{:X} {}x{}{}", addr, width, height, level);
case ImageViewType::e3D:
return fmt::format("ImageView 3D {}x{}x{}{}", width, height, depth, level);
return fmt::format("ImageView 3D 0x{:X} {}x{}x{}{}", addr, width, height, depth, level);
case ImageViewType::e1DArray:
return fmt::format("ImageView 1DArray {}{}|{}", width, level, num_layers);
return fmt::format("ImageView 1DArray 0x{:X} {}{}|{}", addr, width, level, num_layers);
case ImageViewType::e2DArray:
return fmt::format("ImageView 2DArray {}x{}{}|{}", width, height, level, num_layers);
return fmt::format("ImageView 2DArray 0x{:X} {}x{}{}|{}", addr, width, height, level,
num_layers);
case ImageViewType::CubeArray:
return fmt::format("ImageView CubeArray {}x{}{}|{}", width, height, level, num_layers);
return fmt::format("ImageView CubeArray 0x{:X} {}x{}{}|{}", addr, width, height, level,
num_layers);
case ImageViewType::Rect:
return fmt::format("ImageView Rect {}x{}{}", width, height, level);
return fmt::format("ImageView Rect 0x{:X} {}x{}{}", addr, width, height, level);
case ImageViewType::Buffer:
return fmt::format("BufferView {}", width);
return fmt::format("BufferView 0x{:X} {}", addr, width);
}
return "Invalid";
}

View File

@@ -179,6 +179,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
return "ASTC_2D_6X6_SRGB";
case PixelFormat::ASTC_2D_10X6_UNORM:
return "ASTC_2D_10X6_UNORM";
case PixelFormat::ASTC_2D_10X6_SRGB:
return "ASTC_2D_10X6_SRGB";
case PixelFormat::ASTC_2D_10X5_UNORM:
return "ASTC_2D_10X5_UNORM";
case PixelFormat::ASTC_2D_10X5_SRGB:
@@ -187,6 +189,10 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
return "ASTC_2D_10X10_UNORM";
case PixelFormat::ASTC_2D_10X10_SRGB:
return "ASTC_2D_10X10_SRGB";
case PixelFormat::ASTC_2D_12X10_UNORM:
return "ASTC_2D_12X10_UNORM";
case PixelFormat::ASTC_2D_12X10_SRGB:
return "ASTC_2D_12X10_SRGB";
case PixelFormat::ASTC_2D_12X12_UNORM:
return "ASTC_2D_12X12_UNORM";
case PixelFormat::ASTC_2D_12X12_SRGB:
@@ -268,7 +274,7 @@ struct RenderTargets;
[[nodiscard]] std::string Name(const ImageBase& image);
[[nodiscard]] std::string Name(const ImageViewBase& image_view);
[[nodiscard]] std::string Name(const ImageViewBase& image_view, GPUVAddr addr);
[[nodiscard]] std::string Name(const RenderTargets& render_targets);

View File

@@ -4,6 +4,7 @@
#include <fmt/format.h>
#include "common/assert.h"
#include "common/settings.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/format_lookup_table.h"
#include "video_core/texture_cache/image_info.h"
@@ -22,6 +23,8 @@ using VideoCore::Surface::PixelFormat;
using VideoCore::Surface::SurfaceType;
ImageInfo::ImageInfo(const TICEntry& config) noexcept {
forced_flushed = config.IsPitchLinear() && !Settings::values.use_reactive_flushing.GetValue();
dma_downloaded = forced_flushed;
format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type,
config.a_type, config.srgb_conversion);
num_samples = NumSamples(config.msaa_mode);
@@ -117,6 +120,9 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
Tegra::Texture::MsaaMode msaa_mode) noexcept {
forced_flushed =
ct.tile_mode.is_pitch_linear && !Settings::values.use_reactive_flushing.GetValue();
dma_downloaded = forced_flushed;
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(ct.format);
rescaleable = false;
if (ct.tile_mode.is_pitch_linear) {
@@ -155,6 +161,9 @@ ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::ZetaSize& zt_size,
Tegra::Texture::MsaaMode msaa_mode) noexcept {
forced_flushed =
zt.tile_mode.is_pitch_linear && !Settings::values.use_reactive_flushing.GetValue();
dma_downloaded = forced_flushed;
format = VideoCore::Surface::PixelFormatFromDepthFormat(zt.format);
size.width = zt_size.width;
size.height = zt_size.height;
@@ -195,6 +204,9 @@ ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::Zet
ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept {
UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero");
forced_flushed = config.linear == Fermi2D::MemoryLayout::Pitch &&
!Settings::values.use_reactive_flushing.GetValue();
dma_downloaded = forced_flushed;
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format);
rescaleable = false;
if (config.linear == Fermi2D::MemoryLayout::Pitch) {

View File

@@ -39,6 +39,8 @@ struct ImageInfo {
u32 tile_width_spacing = 0;
bool rescaleable = false;
bool downscaleable = false;
bool forced_flushed = false;
bool dma_downloaded = false;
};
} // namespace VideoCommon

View File

@@ -4,7 +4,6 @@
#include <algorithm>
#include "common/assert.h"
#include "common/settings.h"
#include "video_core/compatible_formats.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/formatter.h"
@@ -16,8 +15,8 @@
namespace VideoCommon {
ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info,
ImageId image_id_)
: image_id{image_id_}, format{info.format}, type{info.type}, range{info.range},
ImageId image_id_, GPUVAddr addr)
: image_id{image_id_}, gpu_addr{addr}, format{info.format}, type{info.type}, range{info.range},
size{
.width = std::max(image_info.size.width >> range.base.level, 1u),
.height = std::max(image_info.size.height >> range.base.level, 1u),
@@ -26,8 +25,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
"Image view format {} is incompatible with image format {}", info.format,
image_info.format);
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
if (image_info.type == ImageType::Linear && is_async) {
if (image_info.forced_flushed) {
flags |= ImageViewFlagBits::PreemtiveDownload;
}
if (image_info.type == ImageType::e3D && info.type != ImageViewType::e3D) {
@@ -35,8 +33,8 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
}
}
ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info)
: image_id{NULL_IMAGE_ID}, format{info.format}, type{ImageViewType::Buffer},
ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info, GPUVAddr addr)
: image_id{NULL_IMAGE_ID}, gpu_addr{addr}, format{info.format}, type{ImageViewType::Buffer},
size{
.width = info.size.width,
.height = 1,

View File

@@ -24,9 +24,9 @@ enum class ImageViewFlagBits : u16 {
DECLARE_ENUM_FLAG_OPERATORS(ImageViewFlagBits)
struct ImageViewBase {
explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info,
ImageId image_id);
explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info);
explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info, ImageId image_id,
GPUVAddr addr);
explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info, GPUVAddr addr);
explicit ImageViewBase(const NullImageViewParams&);
[[nodiscard]] bool IsBuffer() const noexcept {
@@ -34,6 +34,7 @@ struct ImageViewBase {
}
ImageId image_id{};
GPUVAddr gpu_addr = 0;
PixelFormat format{};
ImageViewType type{};
SubresourceRange range;

View File

@@ -490,6 +490,32 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
}
}
template <class P>
std::optional<VideoCore::RasterizerDownloadArea> TextureCache<P>::GetFlushArea(VAddr cpu_addr,
u64 size) {
std::optional<VideoCore::RasterizerDownloadArea> area{};
ForEachImageInRegion(cpu_addr, size, [&](ImageId, ImageBase& image) {
if (False(image.flags & ImageFlagBits::GpuModified)) {
return;
}
if (!area) {
area.emplace();
area->start_address = cpu_addr;
area->end_address = cpu_addr + size;
area->preemtive = true;
}
area->start_address = std::min(area->start_address, image.cpu_addr);
area->end_address = std::max(area->end_address, image.cpu_addr_end);
for (auto image_view_id : image.image_view_ids) {
auto& image_view = slot_image_views[image_view_id];
image_view.flags |= ImageViewFlagBits::PreemtiveDownload;
}
area->preemtive &= image.info.forced_flushed;
image.info.forced_flushed = true;
});
return area;
}
template <class P>
void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
std::vector<ImageId> deleted_images;
@@ -683,6 +709,7 @@ void TextureCache<P>::CommitAsyncFlushes() {
download_info.async_buffer_id = last_async_buffer_id;
}
}
if (any_none_dma) {
auto download_map = runtime.DownloadStagingBuffer(total_size_bytes, true);
for (const PendingDownload& download_info : download_ids) {
@@ -695,6 +722,7 @@ void TextureCache<P>::CommitAsyncFlushes() {
}
uncommitted_async_buffers.emplace_back(download_map);
}
async_buffers.emplace_back(std::move(uncommitted_async_buffers));
uncommitted_async_buffers.clear();
}
@@ -783,17 +811,22 @@ void TextureCache<P>::PopAsyncFlushes() {
}
template <class P>
ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand) {
ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) {
const ImageInfo dst_info(operand);
const ImageId dst_id = FindDMAImage(dst_info, operand.address);
if (!dst_id) {
return NULL_IMAGE_ID;
}
const auto& image = slot_images[dst_id];
auto& image = slot_images[dst_id];
if (False(image.flags & ImageFlagBits::GpuModified)) {
// No need to waste time on an image that's synced with guest
return NULL_IMAGE_ID;
}
if (!is_upload && !image.info.dma_downloaded) {
// Force a full sync.
image.info.dma_downloaded = true;
return NULL_IMAGE_ID;
}
const auto base = image.TryFindBase(operand.address);
if (!base) {
return NULL_IMAGE_ID;
@@ -1290,7 +1323,6 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
all_siblings.push_back(overlap_id);
} else {
bad_overlap_ids.push_back(overlap_id);
overlap.flags |= ImageFlagBits::BadOverlap;
}
};
ForEachImageInRegion(cpu_addr, size_bytes, region_check);
@@ -1359,6 +1391,12 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
ScaleDown(new_image);
}
std::ranges::sort(overlap_ids, [this](const ImageId lhs, const ImageId rhs) {
const ImageBase& lhs_image = slot_images[lhs];
const ImageBase& rhs_image = slot_images[rhs];
return lhs_image.modification_tick < rhs_image.modification_tick;
});
for (const ImageId overlap_id : overlap_ids) {
Image& overlap = slot_images[overlap_id];
if (True(overlap.flags & ImageFlagBits::GpuModified)) {
@@ -1395,7 +1433,12 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
ImageBase& aliased = slot_images[aliased_id];
aliased.overlapping_images.push_back(new_image_id);
new_image.overlapping_images.push_back(aliased_id);
new_image.flags |= ImageFlagBits::BadOverlap;
if (aliased.info.resources.levels == 1 && aliased.overlapping_images.size() > 1) {
aliased.flags |= ImageFlagBits::BadOverlap;
}
if (new_image.info.resources.levels == 1 && new_image.overlapping_images.size() > 1) {
new_image.flags |= ImageFlagBits::BadOverlap;
}
}
RegisterImage(new_image_id);
return new_image_id;

View File

@@ -179,6 +179,8 @@ public:
/// Download contents of host images to guest memory in a region
void DownloadMemory(VAddr cpu_addr, size_t size);
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
/// Remove images in a region
void UnmapMemory(VAddr cpu_addr, size_t size);
@@ -205,7 +207,7 @@ public:
/// Pop asynchronous downloads
void PopAsyncFlushes();
[[nodiscard]] ImageId DmaImageId(const Tegra::DMA::ImageOperand& operand);
[[nodiscard]] ImageId DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload);
[[nodiscard]] std::pair<Image*, BufferImageCopy> DmaBufferImageCopy(
const Tegra::DMA::ImageCopy& copy_info, const Tegra::DMA::BufferOperand& buffer_operand,

View File

@@ -10,6 +10,7 @@
#include <vector>
#include "common/common_types.h"
#include "common/settings.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
// Define all features which may be used by the implementation here.
@@ -510,7 +511,7 @@ public:
/// Returns true when a known debugging tool is attached.
bool HasDebuggingToolAttached() const {
return has_renderdoc || has_nsight_graphics;
return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue();
}
/// Returns true when the device does not properly support cube compatibility.

Some files were not shown because too many files have changed in this diff Show More