Compare commits
47 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdb2e4eaff | ||
|
|
168c9ee341 | ||
|
|
8d99aae45b | ||
|
|
f047f376d4 | ||
|
|
3027917f39 | ||
|
|
7e0f70e5a1 | ||
|
|
aab68674c0 | ||
|
|
7f1c6def1f | ||
|
|
db7bcd51ae | ||
|
|
eb0713f781 | ||
|
|
57a05b1653 | ||
|
|
69c92b8156 | ||
|
|
4acbf3a193 | ||
|
|
54d6273975 | ||
|
|
98a6cd02c8 | ||
|
|
52acdafa95 | ||
|
|
975e17aa13 | ||
|
|
72118935a1 | ||
|
|
109c31c90f | ||
|
|
344e171cc7 | ||
|
|
bcbc25eeb3 | ||
|
|
b0365a81c2 | ||
|
|
327d225c3e | ||
|
|
aa075a0c08 | ||
|
|
38c48cf8d8 | ||
|
|
4975f60162 | ||
|
|
0d033e6b45 | ||
|
|
9c67334031 | ||
|
|
1fb33bd1e1 | ||
|
|
405d685101 | ||
|
|
e5a446a0df | ||
|
|
0e61d711e2 | ||
|
|
bc95753107 | ||
|
|
60e0d4a177 | ||
|
|
7c50a916c7 | ||
|
|
cb971ad654 | ||
|
|
cf202f3718 | ||
|
|
6fa3faec65 | ||
|
|
75e6ec85e1 | ||
|
|
a253d1557d | ||
|
|
9afadca5dc | ||
|
|
fb57cd26a1 | ||
|
|
b193d40d22 | ||
|
|
6c045c9beb | ||
|
|
651f6598ac | ||
|
|
70ea1c2000 | ||
|
|
b832942b6e |
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: New Issue (Developers Only)
|
||||
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Issue"
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
name: Bug Report / Feature Request
|
||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
https://yuzu-emu.org/wiki/faq/
|
||||
|
||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
If the FAQ does not answer your question, please go to:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
When submitting an issue, please check the following:
|
||||
|
||||
- You have read the above.
|
||||
- You have provided the version (commit hash) of yuzu you are using.
|
||||
- You have provided sufficient detail for the issue to be reproduced.
|
||||
- You have provided system specs (if relevant).
|
||||
- Please also provide:
|
||||
- For any issues, a log file
|
||||
- For crashes, a backtrace.
|
||||
- For graphical issues, comparison screenshots with real hardware.
|
||||
- For emulation inaccuracies, a test-case (if able).
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Affected Build(s)
|
||||
description: List the affected build(s) that this issue applies to.
|
||||
placeholder: Mainline 1234 / Early Access 1234
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: issue-desc
|
||||
attributes:
|
||||
label: Description of Issue
|
||||
description: A brief description of the issue encountered along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A brief description of how it is expected to work along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Log File
|
||||
description: A log file will help our developers to better diagnose and fix the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-config
|
||||
attributes:
|
||||
label: System Configuration
|
||||
placeholder: |
|
||||
CPU: Intel i5-10400 / AMD Ryzen 5 3600
|
||||
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
|
||||
RAM: 16GB DDR4-3200
|
||||
OS: Windows 11 22H2 (Build 22621.819)
|
||||
value: |
|
||||
CPU:
|
||||
GPU/Driver:
|
||||
RAM:
|
||||
OS:
|
||||
validations:
|
||||
required: true
|
||||
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Feature Request
|
||||
description: File a feature request
|
||||
labels: "request"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-feature
|
||||
attributes:
|
||||
label: What feature are you suggesting?
|
||||
description: A brief description of the requested feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: why-feature
|
||||
attributes:
|
||||
label: Why would this feature be useful?
|
||||
description: A brief description of why this feature would make yuzu better.
|
||||
validations:
|
||||
required: true
|
||||
@@ -132,10 +132,6 @@ Files: vcpkg.json
|
||||
Copyright: 2022 yuzu Emulator Project
|
||||
License: GPL-3.0-or-later
|
||||
|
||||
Files: .github/ISSUE_TEMPLATE/config.yml
|
||||
Copyright: 2020 tgsm <doodrabbit@hotmail.com>
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
Files: .github/ISSUE_TEMPLATE/bug-report-feature-request.md
|
||||
Copyright: 2016 MerryMage
|
||||
Files: .github/ISSUE_TEMPLATE/*
|
||||
Copyright: 2022 yuzu Emulator Project
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
12
externals/CMakeLists.txt
vendored
12
externals/CMakeLists.txt
vendored
@@ -92,10 +92,14 @@ endif()
|
||||
add_subdirectory(sirit)
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
else()
|
||||
if (NOT WIN32)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32 OR NOT OPENSSL_FOUND)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
set(OPENSSLDIR "/etc/ssl/")
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 424fdb5c50...07c614f91b
@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
|
||||
impl = std::make_unique<
|
||||
PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
|
||||
PerformanceEntryVersion1, PerformanceDetailVersion1>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ add_library(common STATIC
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
cache_management.cpp
|
||||
cache_management.h
|
||||
common_funcs.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
|
||||
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
60
src/common/cache_management.cpp
Normal file
60
src/common/cache_management.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "alignment.h"
|
||||
#include "cache_management.h"
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
|
||||
// Most cache operations are no-ops on x86
|
||||
|
||||
void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
|
||||
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
|
||||
void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
|
||||
void DataCacheZeroByVA(void* start, size_t size) {
|
||||
std::memset(start, 0, size);
|
||||
}
|
||||
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
|
||||
// BS/DminLine is log2(cache size in words), we want size in bytes
|
||||
#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
|
||||
#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
|
||||
|
||||
#define DEFINE_DC_OP(op_name, function_name) \
|
||||
void function_name(void* start, size_t size) { \
|
||||
size_t ctr_el0; \
|
||||
asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
|
||||
size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
|
||||
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
|
||||
uintptr_t va_end = va_start + size; \
|
||||
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
|
||||
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DEFINE_DC_OP_DCZID(op_name, function_name) \
|
||||
void function_name(void* start, size_t size) { \
|
||||
size_t dczid_el0; \
|
||||
asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
|
||||
size_t cacheline_size = EXTRACT_BS(dczid_el0); \
|
||||
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
|
||||
uintptr_t va_end = va_start + size; \
|
||||
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
|
||||
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
|
||||
DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
|
||||
DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
|
||||
DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
27
src/common/cache_management.h
Normal file
27
src/common/cache_management.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdlib.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
|
||||
// VA = virtual address
|
||||
// PoC = point of coherency
|
||||
// PoU = point of unification
|
||||
|
||||
// dc cvau
|
||||
void DataCacheLineCleanByVAToPoU(void* start, size_t size);
|
||||
|
||||
// dc civac
|
||||
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
|
||||
|
||||
// dc cvac
|
||||
void DataCacheLineCleanByVAToPoC(void* start, size_t size);
|
||||
|
||||
// dc zva
|
||||
void DataCacheZeroByVA(void* start, size_t size);
|
||||
|
||||
} // namespace Common
|
||||
@@ -120,6 +120,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/cabinet.cpp
|
||||
frontend/applets/cabinet.h
|
||||
frontend/applets/controller.cpp
|
||||
frontend/applets/controller.h
|
||||
frontend/applets/error.cpp
|
||||
@@ -312,6 +314,8 @@ add_library(core STATIC
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applet_cabinet.cpp
|
||||
hle/service/am/applets/applet_cabinet.h
|
||||
hle/service/am/applets/applet_controller.cpp
|
||||
hle/service/am/applets/applet_controller.h
|
||||
hle/service/am/applets/applet_error.cpp
|
||||
@@ -526,6 +530,11 @@ add_library(core STATIC
|
||||
hle/service/ncm/ncm.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_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/nfp/nfp.cpp
|
||||
|
||||
@@ -348,7 +348,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
|
||||
20
src/core/frontend/applets/cabinet.cpp
Normal file
20
src/core/frontend/applets/cabinet.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
CabinetApplet::~CabinetApplet() = default;
|
||||
|
||||
void DefaultCabinetApplet::ShowCabinetApplet(
|
||||
const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
callback(false, {});
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
37
src/core/frontend/applets/cabinet.h
Normal file
37
src/core/frontend/applets/cabinet.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
} // namespace Service::NFP
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
struct CabinetParameters {
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
Service::NFP::CabinetMode mode;
|
||||
};
|
||||
|
||||
using CabinetCallback = std::function<void(bool, const std::string&)>;
|
||||
|
||||
class CabinetApplet {
|
||||
public:
|
||||
virtual ~CabinetApplet();
|
||||
virtual void ShowCabinetApplet(const CabinetCallback& callback,
|
||||
const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
|
||||
};
|
||||
|
||||
class DefaultCabinetApplet final : public CabinetApplet {
|
||||
public:
|
||||
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouchParams() {
|
||||
// TODO(german77): Support any number of fingers
|
||||
std::size_t index = 0;
|
||||
|
||||
// Hardcode mouse, touchscreen and cemuhook parameters
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
if (!Settings::values.mouse_enabled) {
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
|
||||
}
|
||||
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
|
||||
|
||||
for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
|
||||
Common::ParamPackage touchscreen_param{};
|
||||
touchscreen_param.Set("engine", "touch");
|
||||
touchscreen_param.Set("axis_x", i * 2);
|
||||
touchscreen_param.Set("axis_y", (i * 2) + 1);
|
||||
touchscreen_param.Set("button", i);
|
||||
touch_params[index++] = touchscreen_param;
|
||||
}
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
@@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
|
||||
|
||||
// Map the rest of the fingers from touch from button configuration
|
||||
for (const auto& config_entry : touch_buttons) {
|
||||
if (index >= touch_params.size()) {
|
||||
if (index >= MaxTouchDevices) {
|
||||
continue;
|
||||
}
|
||||
Common::ParamPackage params{config_entry};
|
||||
@@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
|
||||
touch_button_params.Set("button", params.Serialize());
|
||||
touch_button_params.Set("x", x);
|
||||
touch_button_params.Set("y", y);
|
||||
touch_button_params.Set("touch_id", static_cast<int>(index));
|
||||
touch_params[index] = touch_button_params;
|
||||
index++;
|
||||
}
|
||||
@@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= console.touch_values.size()) {
|
||||
if (index >= MaxTouchDevices) {
|
||||
return;
|
||||
}
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
console.touch_values[index] = TransformToTouch(callback);
|
||||
const auto touch_input = TransformToTouch(callback);
|
||||
auto touch_index = GetIndexFromFingerId(index);
|
||||
bool is_new_input = false;
|
||||
|
||||
if (!touch_index.has_value() && touch_input.pressed.value) {
|
||||
touch_index = GetNextFreeIndex();
|
||||
is_new_input = true;
|
||||
}
|
||||
|
||||
// No free entries or invalid state. Ignore input
|
||||
if (!touch_index.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& touch_value = console.touch_values[touch_index.value()];
|
||||
|
||||
if (is_new_input) {
|
||||
touch_value.pressed.value = true;
|
||||
touch_value.id = static_cast<u32>(index);
|
||||
}
|
||||
|
||||
touch_value.x = touch_input.x;
|
||||
touch_value.y = touch_input.y;
|
||||
|
||||
if (!touch_input.pressed.value) {
|
||||
touch_value.pressed.value = false;
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
@@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(german77): Remap touch id in sequential order
|
||||
console.touch_state[index] = {
|
||||
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
|
||||
.id = static_cast<u32>(console.touch_values[index].id),
|
||||
.pressed = console.touch_values[index].pressed.value,
|
||||
// Touch outside allowed range. Ignore input
|
||||
if (touch_index.value() >= MaxActiveTouchInputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.touch_state[touch_index.value()] = {
|
||||
.position = {touch_value.x.value, touch_value.y.value},
|
||||
.id = static_cast<u32>(touch_index.value()),
|
||||
.pressed = touch_input.pressed.value,
|
||||
};
|
||||
|
||||
lock.unlock();
|
||||
@@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
|
||||
return console.touch_state;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
|
||||
const auto& finger = console.touch_values[index];
|
||||
if (!finger.pressed.value) {
|
||||
continue;
|
||||
}
|
||||
if (finger.id == static_cast<int>(finger_id)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
|
||||
if (!console.touch_values[index].pressed.value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
@@ -20,6 +21,8 @@
|
||||
#include "core/hid/motion_input.h"
|
||||
|
||||
namespace Core::HID {
|
||||
static constexpr std::size_t MaxTouchDevices = 32;
|
||||
static constexpr std::size_t MaxActiveTouchInputs = 16;
|
||||
|
||||
struct ConsoleMotionInfo {
|
||||
Common::Input::MotionStatus raw_status{};
|
||||
@@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
|
||||
};
|
||||
|
||||
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
|
||||
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
|
||||
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
|
||||
|
||||
using ConsoleMotionParams = Common::ParamPackage;
|
||||
using TouchParams = std::array<Common::ParamPackage, 16>;
|
||||
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
|
||||
|
||||
using ConsoleMotionValues = ConsoleMotionInfo;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, 16>;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
||||
|
||||
struct TouchFinger {
|
||||
u64 last_touch{};
|
||||
@@ -55,7 +58,7 @@ struct ConsoleMotion {
|
||||
bool is_at_rest{};
|
||||
};
|
||||
|
||||
using TouchFingerState = std::array<TouchFinger, 16>;
|
||||
using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
|
||||
|
||||
struct ConsoleStatus {
|
||||
// Data from input_common
|
||||
@@ -166,6 +169,10 @@ private:
|
||||
*/
|
||||
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the console status
|
||||
* @param type Input type of the event to trigger
|
||||
|
||||
@@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
|
||||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
|
||||
// Limit id to maximum number of fingers
|
||||
status.id = std::clamp(status.id, 0, 16);
|
||||
|
||||
if (status.pressed.inverted) {
|
||||
status.pressed.value = !status.pressed.value;
|
||||
}
|
||||
|
||||
@@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
// If we somehow get an invalid type, abort.
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
// If we've hit the end of a gap, free it.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -82,6 +83,22 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
|
||||
KThread* cur_thread) const {
|
||||
// Handle pseudo-handles.
|
||||
ASSERT(cur_thread != nullptr);
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = cur_thread->GetOwnerProcess();
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
return cur_thread;
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
}
|
||||
|
||||
Result KHandleTable::Reserve(Handle* out_handle) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
@@ -113,21 +113,7 @@ public:
|
||||
return this->GetObjectImpl(handle);
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const {
|
||||
// Handle pseudo-handles.
|
||||
ASSERT(cur_thread != nullptr);
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process =
|
||||
static_cast<KAutoObject*>(static_cast<void*>(cur_thread->GetOwnerProcess()));
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
return static_cast<KAutoObject*>(cur_thread);
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
}
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
|
||||
@@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
addr += size;
|
||||
@@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create TLS region
|
||||
|
||||
@@ -2701,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system,
|
||||
[[maybe_unused]] Handle handle, [[maybe_unused]] u32 address,
|
||||
[[maybe_unused]] u32 size) {
|
||||
// Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
|
||||
// as all emulation is done in the same cache level in host architecture, thus data cache
|
||||
// does not need flushing.
|
||||
LOG_DEBUG(Kernel_SVC, "called");
|
||||
return ResultSuccess;
|
||||
static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
|
||||
u64 size) {
|
||||
// Validate address/size.
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
|
||||
R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Get the process from its handle.
|
||||
KScopedAutoObject process =
|
||||
system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Verify the region is within range.
|
||||
auto& page_table = process->PageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Perform the operation.
|
||||
R_RETURN(system.Memory().FlushDataCache(*process, address, size));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by Invalidate/Store/FlushProcessDataCache32
|
||||
template <Result func(Core::System&, Handle, u64, u64)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
const u64 address = (Param(system, 3) << 32) | Param(system, 2);
|
||||
const u64 size = (Param(system, 4) << 32) | Param(system, 1);
|
||||
FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
177
src/core/hle/service/am/applets/applet_cabinet.cpp
Normal file
177
src/core/hle/service/am/applets/applet_cabinet.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#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"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::CabinetApplet& frontend_)
|
||||
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
|
||||
system_,
|
||||
"CabinetApplet"} {
|
||||
|
||||
availability_change_event =
|
||||
service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
Cabinet::~Cabinet() = default;
|
||||
|
||||
void Cabinet::Initialize() {
|
||||
Applet::Initialize();
|
||||
|
||||
LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
|
||||
|
||||
LOG_DEBUG(Service_HID,
|
||||
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
|
||||
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
|
||||
common_args.arguments_version, common_args.library_version,
|
||||
common_args.play_startup_sound, common_args.size, common_args.system_tick,
|
||||
common_args.theme_color);
|
||||
|
||||
const auto storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
|
||||
const auto applet_input_data = storage->GetData();
|
||||
ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
|
||||
|
||||
std::memcpy(&applet_input_common, applet_input_data.data(),
|
||||
sizeof(StartParamForAmiiboSettings));
|
||||
}
|
||||
|
||||
bool Cabinet::TransactionComplete() const {
|
||||
return is_complete;
|
||||
}
|
||||
|
||||
Result Cabinet::GetStatus() const {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Cabinet::ExecuteInteractive() {
|
||||
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
|
||||
}
|
||||
|
||||
void Cabinet::Execute() {
|
||||
if (is_complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
|
||||
DisplayCompleted(apply_changes, amiibo_name);
|
||||
};
|
||||
|
||||
// TODO: listen on all controllers
|
||||
if (nfp_device == nullptr) {
|
||||
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
|
||||
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
|
||||
nfp_device->Initialize();
|
||||
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
|
||||
}
|
||||
|
||||
const Core::Frontend::CabinetParameters parameters{
|
||||
.tag_info = applet_input_common.tag_info,
|
||||
.register_info = applet_input_common.register_info,
|
||||
.mode = applet_input_common.applet_mode,
|
||||
};
|
||||
|
||||
switch (applet_input_common.applet_mode) {
|
||||
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
frontend.ShowCabinetApplet(callback, parameters, nfp_device);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
|
||||
DisplayCompleted(false, {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
|
||||
Service::Mii::MiiManager manager;
|
||||
ReturnValueForAmiiboSettings applet_output{};
|
||||
|
||||
if (!apply_changes) {
|
||||
Cancel();
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
|
||||
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
|
||||
Cancel();
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
|
||||
nfp_device->Mount(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->SetNicknameAndOwner(name);
|
||||
break;
|
||||
}
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
nfp_device->DeleteApplicationArea();
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
nfp_device->RestoreAmiibo();
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
nfp_device->DeleteAllData();
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
nfp_device->Finalize();
|
||||
|
||||
if (reg_result.IsSuccess()) {
|
||||
applet_output.result |= CabinetResult::RegisterInfo;
|
||||
}
|
||||
|
||||
if (tag_result.IsSuccess()) {
|
||||
applet_output.result |= CabinetResult::TagInfo;
|
||||
}
|
||||
|
||||
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
|
||||
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
|
||||
|
||||
is_complete = true;
|
||||
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
void Cabinet::Cancel() {
|
||||
ReturnValueForAmiiboSettings applet_output{};
|
||||
applet_output.device_handle = applet_input_common.device_handle;
|
||||
applet_output.result = CabinetResult::Cancel;
|
||||
nfp_device->Finalize();
|
||||
|
||||
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
|
||||
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
|
||||
|
||||
is_complete = true;
|
||||
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
104
src/core/hle/service/am/applets/applet_cabinet.h
Normal file
104
src/core/hle/service/am/applets/applet_cabinet.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class CabinetAppletVersion : u32 {
|
||||
Version1 = 0x1,
|
||||
};
|
||||
|
||||
enum class CabinetResult : u8 {
|
||||
Cancel = 0,
|
||||
TagInfo = 1 << 1,
|
||||
RegisterInfo = 1 << 2,
|
||||
All = TagInfo | RegisterInfo,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
|
||||
|
||||
// This is nn::nfp::AmiiboSettingsStartParam
|
||||
struct AmiiboSettingsStartParam {
|
||||
u64 device_handle;
|
||||
std::array<u8, 0x20> param_1;
|
||||
u8 param_2;
|
||||
};
|
||||
static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
|
||||
"AmiiboSettingsStartParam is an invalid size");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// This is nn::nfp::StartParamForAmiiboSettings
|
||||
struct StartParamForAmiiboSettings {
|
||||
u8 param_1;
|
||||
Service::NFP::CabinetMode applet_mode;
|
||||
u8 flags;
|
||||
u8 amiibo_settings_1;
|
||||
u64 device_handle;
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
std::array<u8, 0x20> amiibo_settings_3;
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
};
|
||||
static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
|
||||
"StartParamForAmiiboSettings is an invalid size");
|
||||
|
||||
// This is nn::nfp::ReturnValueForAmiiboSettings
|
||||
struct ReturnValueForAmiiboSettings {
|
||||
CabinetResult result;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
u64 device_handle;
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
};
|
||||
static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
|
||||
"ReturnValueForAmiiboSettings is an invalid size");
|
||||
#pragma pack(pop)
|
||||
|
||||
class Cabinet final : public Applet {
|
||||
public:
|
||||
explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::CabinetApplet& frontend_);
|
||||
~Cabinet() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
|
||||
void Cancel();
|
||||
|
||||
private:
|
||||
const Core::Frontend::CabinetApplet& frontend;
|
||||
Core::System& system;
|
||||
|
||||
bool is_complete{false};
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
|
||||
Kernel::KEvent* availability_change_event;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
StartParamForAmiiboSettings applet_input_common{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -144,6 +144,7 @@ void Error::Initialize() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@ void Auth::Execute() {
|
||||
}
|
||||
default:
|
||||
unimplemented_log();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ void PhotoViewer::Execute() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/applets/applet_cabinet.h"
|
||||
#include "core/hle/service/am/applets/applet_controller.h"
|
||||
#include "core/hle/service/am/applets/applet_error.h"
|
||||
#include "core/hle/service/am/applets/applet_general_backend.h"
|
||||
@@ -171,13 +173,15 @@ void Applet::Initialize() {
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet() = default;
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
|
||||
AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet,
|
||||
ControllerApplet controller_applet, ErrorApplet error_applet,
|
||||
MiiEdit mii_edit_,
|
||||
ParentalControlsApplet parental_controls_applet,
|
||||
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
|
||||
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
|
||||
: controller{std::move(controller_applet)}, error{std::move(error_applet)},
|
||||
mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
|
||||
: cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
|
||||
error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
|
||||
parental_controls{std::move(parental_controls_applet)},
|
||||
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
|
||||
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
|
||||
|
||||
@@ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
|
||||
}
|
||||
|
||||
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
|
||||
if (set.cabinet != nullptr) {
|
||||
frontend.cabinet = std::move(set.cabinet);
|
||||
}
|
||||
|
||||
if (set.controller != nullptr) {
|
||||
frontend.controller = std::move(set.controller);
|
||||
}
|
||||
@@ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() {
|
||||
}
|
||||
|
||||
void AppletManager::SetDefaultAppletsIfMissing() {
|
||||
if (frontend.cabinet == nullptr) {
|
||||
frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
|
||||
}
|
||||
|
||||
if (frontend.controller == nullptr) {
|
||||
frontend.controller =
|
||||
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
|
||||
@@ -279,6 +291,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
|
||||
switch (id) {
|
||||
case AppletId::Auth:
|
||||
return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
|
||||
case AppletId::Cabinet:
|
||||
return std::make_shared<Cabinet>(system, mode, *frontend.cabinet);
|
||||
case AppletId::Controller:
|
||||
return std::make_shared<Controller>(system, mode, *frontend.controller);
|
||||
case AppletId::Error:
|
||||
|
||||
@@ -16,6 +16,7 @@ class System;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class CabinetApplet;
|
||||
class ControllerApplet;
|
||||
class ECommerceApplet;
|
||||
class ErrorApplet;
|
||||
@@ -176,6 +177,7 @@ protected:
|
||||
};
|
||||
|
||||
struct AppletFrontendSet {
|
||||
using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
|
||||
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
|
||||
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
|
||||
using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
|
||||
@@ -186,10 +188,11 @@ struct AppletFrontendSet {
|
||||
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
|
||||
|
||||
AppletFrontendSet();
|
||||
AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
|
||||
MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
|
||||
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
|
||||
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
|
||||
AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
|
||||
ErrorApplet error_applet, MiiEdit mii_edit_,
|
||||
ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
|
||||
ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
|
||||
WebBrowser web_browser_);
|
||||
~AppletFrontendSet();
|
||||
|
||||
AppletFrontendSet(const AppletFrontendSet&) = delete;
|
||||
@@ -198,6 +201,7 @@ struct AppletFrontendSet {
|
||||
AppletFrontendSet(AppletFrontendSet&&) noexcept;
|
||||
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
|
||||
|
||||
CabinetApplet cabinet;
|
||||
ControllerApplet controller;
|
||||
ErrorApplet error;
|
||||
MiiEdit mii_edit;
|
||||
|
||||
@@ -36,8 +36,9 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
|
||||
// Correct pad_update_ns is 4ms this is overclocked to lower input lag
|
||||
constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
|
||||
// Correct npad_update_ns is 4ms this is overclocked to lower input lag
|
||||
constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
|
||||
constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
|
||||
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
||||
|
||||
@@ -75,8 +76,16 @@ IAppletResource::IAppletResource(Core::System& system_,
|
||||
GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
|
||||
|
||||
// Register update callbacks
|
||||
pad_update_event = Core::Timing::CreateEvent(
|
||||
npad_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdatePadCallback",
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateNpad(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
default_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateDefaultCallback",
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
@@ -100,7 +109,9 @@ IAppletResource::IAppletResource(Core::System& system_,
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
|
||||
default_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
|
||||
mouse_keyboard_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
|
||||
@@ -118,7 +129,8 @@ void IAppletResource::DeactivateController(HidController controller) {
|
||||
}
|
||||
|
||||
IAppletResource::~IAppletResource() {
|
||||
system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
||||
}
|
||||
@@ -144,10 +156,20 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
|
||||
if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
|
||||
continue;
|
||||
}
|
||||
// Npad has it's own update event
|
||||
if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
|
||||
continue;
|
||||
}
|
||||
controller->OnUpdate(core_timing);
|
||||
}
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
|
||||
std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
@@ -71,12 +71,14 @@ private:
|
||||
|
||||
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> pad_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> npad_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> default_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nfc/nfc_user.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
@@ -97,76 +98,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_) : ServiceFramework{system_, "NFC::IUser"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::InitializeOld, "InitializeOld"},
|
||||
{1, &IUser::FinalizeOld, "FinalizeOld"},
|
||||
{2, &IUser::GetStateOld, "GetStateOld"},
|
||||
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
|
||||
{400, &IUser::InitializeOld, "Initialize"},
|
||||
{401, &IUser::FinalizeOld, "Finalize"},
|
||||
{402, &IUser::GetStateOld, "GetState"},
|
||||
{403, &IUser::IsNfcEnabledOld, "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"},
|
||||
{1000, nullptr, "ReadMifare"},
|
||||
{1001, nullptr, "WriteMifare"},
|
||||
{1300, nullptr, "SendCommandByPassThrough"},
|
||||
{1301, nullptr, "KeepPassThroughSession"},
|
||||
{1302, nullptr, "ReleasePassThroughSession"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class NfcStates : u32 {
|
||||
Finalized = 6,
|
||||
};
|
||||
|
||||
void InitializeOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||
rb.Push(ResultSuccess);
|
||||
// We don't deal with hardware initialization so we can just stub this.
|
||||
}
|
||||
|
||||
void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u8>(true);
|
||||
}
|
||||
|
||||
void GetStateOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
|
||||
}
|
||||
|
||||
void FinalizeOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
class NFC_U final : public ServiceFramework<NFC_U> {
|
||||
public:
|
||||
explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
|
||||
|
||||
197
src/core/hle/service/nfc/nfc_device.cpp
Normal file
197
src/core/hle/service/nfc/nfc_device.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// 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/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.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 (type == Core::HID::ControllerTriggerType::Connected ||
|
||||
type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {};
|
||||
}
|
||||
|
||||
void NfcDevice::Finalize() {
|
||||
if (device_state == NFP::DeviceState::SearchingForTag ||
|
||||
device_state == NFP::DeviceState::TagRemoved) {
|
||||
StopDetection();
|
||||
}
|
||||
device_state = NFP::DeviceState::Unavailable;
|
||||
}
|
||||
|
||||
Result NfcDevice::StartDetection(s32 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(Common::Input::PollingMode::NFC)) {
|
||||
LOG_ERROR(Service_NFC, "Nfc not supported");
|
||||
return NfcDisabled;
|
||||
}
|
||||
|
||||
device_state = NFP::DeviceState::SearchingForTag;
|
||||
protocol = protocol_;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfcDevice::StopDetection() {
|
||||
npad_device->SetPollingMode(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::GetTagInfo(NFP::TagInfo& tag_info) 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
70
src/core/hle/service/nfc/nfc_device.h
Normal file
70
src/core/hle/service/nfc/nfc_device.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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(s32 protocol_);
|
||||
Result StopDetection();
|
||||
|
||||
Result GetTagInfo(NFP::TagInfo& tag_info) const;
|
||||
|
||||
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;
|
||||
|
||||
s32 protocol{};
|
||||
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
|
||||
|
||||
NFP::EncryptedNTAG215File encrypted_tag_data{};
|
||||
};
|
||||
|
||||
} // namespace Service::NFC
|
||||
17
src/core/hle/service/nfc/nfc_result.h
Normal file
17
src/core/hle/service/nfc/nfc_result.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
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 CorruptedData(ErrorModule::NFC, 144);
|
||||
|
||||
} // namespace Service::NFC
|
||||
365
src/core/hle/service/nfc/nfc_user.cpp
Normal file
365
src/core/hle/service/nfc/nfc_user.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
// 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/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.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(Kernel::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(Kernel::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(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(state);
|
||||
}
|
||||
|
||||
void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(state != State::NonInitialized);
|
||||
}
|
||||
|
||||
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(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.GetWriteBufferSize() / sizeof(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(Kernel::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(Kernel::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(Kernel::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(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto nfp_protocol{rp.Pop<s32>()};
|
||||
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(Kernel::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(Kernel::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);
|
||||
ctx.WriteBuffer(tag_info);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::AttachActivateEvent(Kernel::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(Kernel::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(Kernel::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
|
||||
52
src/core/hle/service/nfc/nfc_user.h
Normal file
52
src/core/hle/service/nfc/nfc_user.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_);
|
||||
~IUser();
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
NonInitialized,
|
||||
Initialized,
|
||||
};
|
||||
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void Finalize(Kernel::HLERequestContext& ctx);
|
||||
void GetState(Kernel::HLERequestContext& ctx);
|
||||
void IsNfcEnabled(Kernel::HLERequestContext& ctx);
|
||||
void ListDevices(Kernel::HLERequestContext& ctx);
|
||||
void GetDeviceState(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadId(Kernel::HLERequestContext& ctx);
|
||||
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
|
||||
void StartDetection(Kernel::HLERequestContext& ctx);
|
||||
void StopDetection(Kernel::HLERequestContext& ctx);
|
||||
void GetTagInfo(Kernel::HLERequestContext& ctx);
|
||||
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void SendCommandByPassThrough(Kernel::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
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/nfp/amiibo_crypto.h"
|
||||
|
||||
namespace Service::NFP::AmiiboCrypto {
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/input.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -19,7 +16,6 @@
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
#include "core/hle/service/nfp/amiibo_crypto.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_device.h"
|
||||
#include "core/hle/service/nfp/nfp_result.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
@@ -49,6 +45,8 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
||||
}
|
||||
|
||||
NfpDevice::~NfpDevice() {
|
||||
activate_event->Close();
|
||||
deactivate_event->Close();
|
||||
if (!is_controller_set) {
|
||||
return;
|
||||
}
|
||||
@@ -77,6 +75,9 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
|
||||
LoadAmiibo(nfc_status.data);
|
||||
break;
|
||||
case Common::Input::NfcState::AmiiboRemoved:
|
||||
if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
|
||||
break;
|
||||
}
|
||||
if (device_state != DeviceState::SearchingForTag) {
|
||||
CloseAmiibo();
|
||||
}
|
||||
@@ -97,6 +98,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Filter by allowed_protocols here
|
||||
|
||||
memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
|
||||
|
||||
device_state = DeviceState::TagFound;
|
||||
@@ -143,7 +146,7 @@ void NfpDevice::Finalize() {
|
||||
device_state = DeviceState::Unavailable;
|
||||
}
|
||||
|
||||
Result NfpDevice::StartDetection(s32 protocol_) {
|
||||
Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
|
||||
if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
return WrongDeviceState;
|
||||
@@ -155,7 +158,7 @@ Result NfpDevice::StartDetection(s32 protocol_) {
|
||||
}
|
||||
|
||||
device_state = DeviceState::SearchingForTag;
|
||||
protocol = protocol_;
|
||||
allowed_protocols = allowed_protocol;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -469,6 +472,32 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const {
|
||||
application_area_id = {};
|
||||
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
if (device_state == DeviceState::TagRemoved) {
|
||||
return TagRemoved;
|
||||
}
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
|
||||
LOG_WARNING(Service_NFP, "Application area is not initialized");
|
||||
return ApplicationAreaIsNotInitialized;
|
||||
}
|
||||
|
||||
application_area_id = tag_data.application_area_id;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#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"
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
Result StartDetection(s32 protocol_);
|
||||
Result StartDetection(TagProtocol allowed_protocol);
|
||||
Result StopDetection();
|
||||
Result Mount(MountTarget mount_target);
|
||||
Result Unmount();
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
Result DeleteAllData();
|
||||
|
||||
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);
|
||||
@@ -88,7 +89,7 @@ private:
|
||||
|
||||
bool is_data_moddified{};
|
||||
bool is_app_area_open{};
|
||||
s32 protocol{};
|
||||
TagProtocol allowed_protocols{};
|
||||
s64 current_posix_time{};
|
||||
MountTarget mount_target{MountTarget::None};
|
||||
DeviceState device_state{DeviceState::Unavailable};
|
||||
|
||||
@@ -88,11 +88,22 @@ enum class PackedTagType : u8 {
|
||||
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, // ISO14443A
|
||||
TypeB, // ISO14443B
|
||||
TypeF, // Sony Felica
|
||||
TypeA = 1U << 0, // ISO14443A
|
||||
TypeB = 1U << 1, // ISO14443B
|
||||
TypeF = 1U << 2, // Sony Felica
|
||||
Unknown1 = 1U << 3,
|
||||
Unknown2 = 1U << 5,
|
||||
All = 0xFFFFFFFFU,
|
||||
};
|
||||
|
||||
enum class CabinetMode : u8 {
|
||||
StartNicknameAndOwnerSettings,
|
||||
StartGameDataEraser,
|
||||
StartRestorer,
|
||||
StartFormatter,
|
||||
};
|
||||
|
||||
using UniqueSerialNumber = std::array<u8, 7>;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
@@ -55,8 +52,12 @@ IUser::IUser(Core::System& system_)
|
||||
}
|
||||
}
|
||||
|
||||
IUser ::~IUser() {
|
||||
availability_change_event->Close();
|
||||
}
|
||||
|
||||
void IUser::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::Initialized;
|
||||
|
||||
@@ -64,7 +65,7 @@ void IUser::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
device->Initialize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto nfp_protocol{rp.Pop<s32>()};
|
||||
const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
@@ -551,9 +552,9 @@ void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IUser::GetState(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3, 0};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(state);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -12,6 +16,7 @@ class NfpDevice;
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_);
|
||||
~IUser();
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
|
||||
@@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
|
||||
return error_notifier_event;
|
||||
case 2:
|
||||
return unknown_event;
|
||||
default: {
|
||||
default:
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
|
||||
return sm_exception_breakpoint_pause_report_event;
|
||||
case 3:
|
||||
return error_notifier_event;
|
||||
default: {
|
||||
default:
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() {
|
||||
}
|
||||
|
||||
void BufferQueueCore::SignalDequeueCondition() {
|
||||
dequeue_possible.store(true);
|
||||
dequeue_condition.notify_all();
|
||||
}
|
||||
|
||||
bool BufferQueueCore::WaitForDequeueCondition() {
|
||||
bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
|
||||
if (is_shutting_down) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dequeue_condition.wait(mutex);
|
||||
dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
|
||||
dequeue_possible.store(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
private:
|
||||
void SignalDequeueCondition();
|
||||
bool WaitForDequeueCondition();
|
||||
bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
|
||||
|
||||
s32 GetMinUndequeuedBufferCountLocked(bool async) const;
|
||||
s32 GetMinMaxBufferCountLocked(bool async) const;
|
||||
@@ -60,7 +60,8 @@ private:
|
||||
BufferQueueDefs::SlotsType slots{};
|
||||
std::vector<BufferItem> queue;
|
||||
s32 override_max_buffer_count{};
|
||||
mutable std::condition_variable_any dequeue_condition;
|
||||
std::condition_variable dequeue_condition;
|
||||
std::atomic<bool> dequeue_possible{};
|
||||
const bool use_async_buffer{}; // This is always disabled on HOS
|
||||
bool dequeue_buffer_cannot_block{};
|
||||
PixelFormat default_buffer_format{PixelFormat::Rgba8888};
|
||||
|
||||
@@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
|
||||
Status* return_flags) const {
|
||||
Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
|
||||
std::unique_lock<std::mutex>& lk) const {
|
||||
bool try_again = true;
|
||||
|
||||
while (try_again) {
|
||||
@@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
|
||||
return Status::WouldBlock;
|
||||
}
|
||||
|
||||
if (!core->WaitForDequeueCondition()) {
|
||||
if (!core->WaitForDequeueCondition(lk)) {
|
||||
// We are no longer running
|
||||
return Status::NoError;
|
||||
}
|
||||
@@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||
Status return_flags = Status::NoError;
|
||||
bool attached_by_consumer = false;
|
||||
{
|
||||
std::scoped_lock lock{core->mutex};
|
||||
std::unique_lock lock{core->mutex};
|
||||
core->WaitWhileAllocatingLocked();
|
||||
|
||||
if (format == PixelFormat::NoFormat) {
|
||||
@@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||
usage |= core->consumer_usage_bit;
|
||||
|
||||
s32 found{};
|
||||
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
|
||||
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock);
|
||||
if (status != Status::NoError) {
|
||||
return status;
|
||||
}
|
||||
@@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{core->mutex};
|
||||
std::unique_lock lock{core->mutex};
|
||||
core->WaitWhileAllocatingLocked();
|
||||
|
||||
Status return_flags = Status::NoError;
|
||||
s32 found{};
|
||||
|
||||
const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
|
||||
const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock);
|
||||
if (status != Status::NoError) {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@ public:
|
||||
private:
|
||||
BufferQueueProducer(const BufferQueueProducer&) = delete;
|
||||
|
||||
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
|
||||
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
|
||||
std::unique_lock<std::mutex>& lk) const;
|
||||
|
||||
Kernel::KEvent* buffer_wait_event{};
|
||||
Service::KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
@@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
|
||||
break;
|
||||
}
|
||||
|
||||
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
||||
|
||||
@@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return value + rule.transition_time + offset;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/cache_management.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
@@ -329,6 +330,55 @@ struct Memory::Impl {
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size,
|
||||
Callback&& cb) {
|
||||
class InvalidMemoryException : public std::exception {};
|
||||
|
||||
try {
|
||||
WalkBlock(
|
||||
process, dest_addr, size,
|
||||
[&](const std::size_t block_size, const VAddr current_vaddr) {
|
||||
LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr);
|
||||
throw InvalidMemoryException();
|
||||
},
|
||||
[&](const std::size_t block_size, u8* const host_ptr) { cb(block_size, host_ptr); },
|
||||
[&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) {
|
||||
system.GPU().FlushRegion(current_vaddr, block_size);
|
||||
cb(block_size, host_ptr);
|
||||
},
|
||||
[](const std::size_t block_size) {});
|
||||
} catch (InvalidMemoryException&) {
|
||||
return Kernel::ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
|
||||
auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
|
||||
// Do nothing; this operation (dc ivac) cannot be supported
|
||||
// from EL0
|
||||
};
|
||||
return PerformCacheOperation(process, dest_addr, size, perform);
|
||||
}
|
||||
|
||||
Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
|
||||
auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
|
||||
// dc cvac: Store to point of coherency
|
||||
Common::DataCacheLineCleanByVAToPoC(host_ptr, block_size);
|
||||
};
|
||||
return PerformCacheOperation(process, dest_addr, size, perform);
|
||||
}
|
||||
|
||||
Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
|
||||
auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
|
||||
// dc civac: Store to point of coherency, and invalidate from cache
|
||||
Common::DataCacheLineCleanAndInvalidateByVAToPoC(host_ptr, block_size);
|
||||
};
|
||||
return PerformCacheOperation(process, dest_addr, size, perform);
|
||||
}
|
||||
|
||||
void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
|
||||
if (vaddr == 0) {
|
||||
return;
|
||||
@@ -786,6 +836,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s
|
||||
impl->ZeroBlock(process, dest_addr, size);
|
||||
}
|
||||
|
||||
Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr,
|
||||
const std::size_t size) {
|
||||
return impl->InvalidateDataCache(process, dest_addr, size);
|
||||
}
|
||||
|
||||
Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr,
|
||||
const std::size_t size) {
|
||||
return impl->StoreDataCache(process, dest_addr, size);
|
||||
}
|
||||
|
||||
Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr,
|
||||
const std::size_t size) {
|
||||
return impl->FlushDataCache(process, dest_addr, size);
|
||||
}
|
||||
|
||||
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
||||
impl->RasterizerMarkRegionCached(vaddr, size, cached);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Common {
|
||||
struct PageTable;
|
||||
@@ -449,6 +450,39 @@ public:
|
||||
*/
|
||||
void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Invalidates a range of bytes within the current process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process that will have data invalidated within its address space.
|
||||
* @param dest_addr The destination virtual address to invalidate the data from.
|
||||
* @param size The size of the range to invalidate, in bytes.
|
||||
*
|
||||
*/
|
||||
Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Stores a range of bytes within the current process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process that will have data stored within its address space.
|
||||
* @param dest_addr The destination virtual address to store the data from.
|
||||
* @param size The size of the range to store, in bytes.
|
||||
*
|
||||
*/
|
||||
Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Flushes a range of bytes within the current process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process that will have data flushed within its address space.
|
||||
* @param dest_addr The destination virtual address to flush the data from.
|
||||
* @param size The size of the range to flush, in bytes.
|
||||
*
|
||||
*/
|
||||
Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Marks each page within the specified address range as cached or uncached.
|
||||
*
|
||||
|
||||
@@ -761,7 +761,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
u64 src_address =
|
||||
GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
|
||||
u64 src_value = 0;
|
||||
switch (store_static->bit_width) {
|
||||
switch (begin_cond->bit_width) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
|
||||
@@ -60,6 +60,8 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
amiibo_data = data;
|
||||
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
@@ -91,6 +93,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
|
||||
if (state == State::AmiiboIsOpen) {
|
||||
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
return LoadAmiibo(file_path);
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
|
||||
: State::Initialized;
|
||||
@@ -98,4 +109,8 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
std::string VirtualAmiibo::GetLastFilePath() const {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -47,8 +47,11 @@ public:
|
||||
State GetCurrentState() const;
|
||||
|
||||
Info LoadAmiibo(const std::string& amiibo_file);
|
||||
Info ReloadAmiibo();
|
||||
Info CloseAmiibo();
|
||||
|
||||
std::string GetLastFilePath() const;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t amiibo_size = 0x21C;
|
||||
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace InputCommon {
|
||||
class TouchFromButtonDevice final : public Common::Input::InputDevice {
|
||||
public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
|
||||
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
|
||||
TouchFromButtonDevice(Button button_, float x_, float y_)
|
||||
: button(std::move(button_)), x(x_), y(y_) {
|
||||
last_button_value = false;
|
||||
button->SetCallback({
|
||||
.on_change =
|
||||
@@ -34,7 +34,6 @@ public:
|
||||
.pressed = button_status,
|
||||
.x = {},
|
||||
.y = {},
|
||||
.id = touch_id,
|
||||
};
|
||||
status.x.properties = properties;
|
||||
status.y.properties = properties;
|
||||
@@ -62,7 +61,6 @@ public:
|
||||
private:
|
||||
Button button;
|
||||
bool last_button_value;
|
||||
const int touch_id;
|
||||
const float x;
|
||||
const float y;
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
@@ -73,10 +71,9 @@ std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
|
||||
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
|
||||
auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("button", null_engine));
|
||||
const auto touch_id = params.Get("touch_id", 0);
|
||||
const float x = params.Get("x", 0.0f) / 1280.0f;
|
||||
const float y = params.Get("y", 0.0f) / 720.0f;
|
||||
return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
|
||||
return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
return Common::Input::CameraError::NotSupported;
|
||||
}
|
||||
|
||||
// Request nfc data from a controller
|
||||
// Returns success if nfc is supported
|
||||
virtual Common::Input::NfcState SupportsNfc(
|
||||
[[maybe_unused]] const PadIdentifier& identifier) const {
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
|
||||
@@ -229,13 +229,12 @@ private:
|
||||
|
||||
class InputFromTouch final : public Common::Input::InputDevice {
|
||||
public:
|
||||
explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_,
|
||||
bool inverted_, int axis_x_, int axis_y_,
|
||||
Common::Input::AnalogProperties properties_x_,
|
||||
explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
|
||||
int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_,
|
||||
Common::Input::AnalogProperties properties_y_,
|
||||
InputEngine* input_engine_)
|
||||
: identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_),
|
||||
inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
|
||||
: identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
|
||||
axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
|
||||
properties_y(properties_y_), input_engine(input_engine_) {
|
||||
UpdateCallback engine_callback{[this]() { OnChange(); }};
|
||||
const InputIdentifier button_input_identifier{
|
||||
@@ -271,8 +270,7 @@ public:
|
||||
}
|
||||
|
||||
Common::Input::TouchStatus GetStatus() const {
|
||||
Common::Input::TouchStatus status;
|
||||
status.id = touch_id;
|
||||
Common::Input::TouchStatus status{};
|
||||
status.pressed = {
|
||||
.value = input_engine->GetButton(identifier, button),
|
||||
.inverted = inverted,
|
||||
@@ -307,7 +305,6 @@ public:
|
||||
|
||||
private:
|
||||
const PadIdentifier identifier;
|
||||
const int touch_id;
|
||||
const int button;
|
||||
const bool toggle;
|
||||
const bool inverted;
|
||||
@@ -919,7 +916,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
|
||||
const Common::ParamPackage& params) {
|
||||
const auto touch_id = params.Get("touch_id", 0);
|
||||
const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
|
||||
const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
|
||||
const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
|
||||
@@ -954,8 +950,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
|
||||
input_engine->PreSetAxis(identifier, axis_x);
|
||||
input_engine->PreSetAxis(identifier, axis_y);
|
||||
input_engine->PreSetButton(identifier, button);
|
||||
return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x,
|
||||
axis_y, properties_x, properties_y, input_engine.get());
|
||||
return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y,
|
||||
properties_x, properties_y, input_engine.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
|
||||
|
||||
@@ -320,6 +320,7 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
header += "OPTION ARB_draw_buffers;";
|
||||
header += "OPTION ARB_fragment_layer_viewport;";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::Layer:
|
||||
ctx.Add("MOV.F {}.x,fragment.layer;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
|
||||
@@ -205,6 +205,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::Layer:
|
||||
ctx.AddF32("{}=itof(gl_Layer);", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
|
||||
@@ -315,6 +315,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
case IR::Attribute::Layer:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.layer));
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
|
||||
@@ -1359,6 +1359,11 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (loads[IR::Attribute::PrimitiveId]) {
|
||||
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
||||
}
|
||||
if (loads[IR::Attribute::Layer]) {
|
||||
AddCapability(spv::Capability::Geometry);
|
||||
layer = DefineInput(*this, U32[1], false, spv::BuiltIn::Layer);
|
||||
Decorate(layer, spv::Decoration::Flat);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
const bool is_fragment{stage != Stage::Fragment};
|
||||
const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
|
||||
|
||||
@@ -992,7 +992,20 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
TouchBuffer(buffer, index_buffer.buffer_id);
|
||||
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
|
||||
const u32 size = index_buffer.size;
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
if (maxwell3d->inline_index_draw_indexes.size()) {
|
||||
if constexpr (USE_MEMORY_MAPS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(size);
|
||||
std::array<BufferCopy, 1> copies{
|
||||
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
|
||||
std::memcpy(upload_staging.mapped_span.data(),
|
||||
maxwell3d->inline_index_draw_indexes.data(), size);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
|
||||
} else {
|
||||
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
|
||||
}
|
||||
} else {
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
}
|
||||
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
const u32 new_offset = offset + maxwell3d->regs.index_buffer.first *
|
||||
maxwell3d->regs.index_buffer.FormatSizeInBytes();
|
||||
@@ -1275,7 +1288,15 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
}
|
||||
flags[Dirty::IndexBuffer] = false;
|
||||
last_index_count = index_array.count;
|
||||
|
||||
if (maxwell3d->inline_index_draw_indexes.size()) {
|
||||
auto inline_index_size = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
|
||||
index_buffer = Binding{
|
||||
.cpu_addr = 0,
|
||||
.size = inline_index_size,
|
||||
.buffer_id = CreateBuffer(0, inline_index_size),
|
||||
};
|
||||
return;
|
||||
}
|
||||
const GPUVAddr gpu_addr_begin = index_array.StartAddress();
|
||||
const GPUVAddr gpu_addr_end = index_array.EndAddress();
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||
@@ -1491,6 +1512,14 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
|
||||
VAddr end = cpu_addr + wanted_size;
|
||||
int stream_score = 0;
|
||||
bool has_stream_leap = false;
|
||||
if (begin == 0) {
|
||||
return OverlapResult{
|
||||
.ids = std::move(overlap_ids),
|
||||
.begin = begin,
|
||||
.end = end,
|
||||
.has_stream_leap = has_stream_leap,
|
||||
};
|
||||
}
|
||||
for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE);
|
||||
cpu_addr += YUZU_PAGESIZE) {
|
||||
const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS];
|
||||
|
||||
@@ -314,6 +314,7 @@ void MaxwellDMA::ReleaseSemaphore() {
|
||||
}
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ bool MacroInterpreterImpl::Step(bool is_delay_slot) {
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value());
|
||||
break;
|
||||
}
|
||||
|
||||
// An instruction with the Exit flag will not actually
|
||||
@@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -652,6 +652,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -222,8 +222,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
pipeline->Configure(is_indexed);
|
||||
|
||||
BindInlineIndexBuffer();
|
||||
|
||||
SyncState();
|
||||
|
||||
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
|
||||
@@ -1140,16 +1138,6 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
|
||||
query_cache.EraseChannel(channel_id);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::BindInlineIndexBuffer() {
|
||||
if (maxwell3d->inline_index_draw_indexes.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
|
||||
auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count);
|
||||
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
|
||||
buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count);
|
||||
}
|
||||
|
||||
AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
|
||||
|
||||
bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
|
||||
|
||||
@@ -199,8 +199,6 @@ private:
|
||||
/// End a transform feedback
|
||||
void EndTransformFeedback();
|
||||
|
||||
void BindInlineIndexBuffer();
|
||||
|
||||
Tegra::GPU& gpu;
|
||||
|
||||
const Device& device;
|
||||
|
||||
@@ -891,6 +891,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
// Compressed formats don't have a pixel format or type
|
||||
const bool is_compressed = gl_format == GL_NONE;
|
||||
|
||||
@@ -340,6 +340,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
// UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
|
||||
// static_cast<u32>(framebuffer.pixel_format));
|
||||
break;
|
||||
}
|
||||
|
||||
texture.resource.Release();
|
||||
|
||||
@@ -172,6 +172,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
}
|
||||
|
||||
void RendererVulkan::Report() const {
|
||||
using namespace Common::Literals;
|
||||
const std::string vendor_name{device.GetVendorName()};
|
||||
const std::string model_name{device.GetModelName()};
|
||||
const std::string driver_version = GetDriverVersion(device);
|
||||
@@ -181,9 +182,12 @@ void RendererVulkan::Report() const {
|
||||
|
||||
const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions());
|
||||
|
||||
const auto available_vram = static_cast<f64>(device.GetDeviceLocalMemory()) / f64{1_GiB};
|
||||
|
||||
LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
|
||||
LOG_INFO(Render_Vulkan, "Device: {}", model_name);
|
||||
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
|
||||
LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram);
|
||||
|
||||
static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
|
||||
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
|
||||
|
||||
@@ -191,8 +191,6 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
pipeline->Configure(is_indexed);
|
||||
|
||||
BindInlineIndexBuffer();
|
||||
|
||||
BeginTransformFeedback();
|
||||
|
||||
UpdateDynamicStates();
|
||||
@@ -1029,17 +1027,4 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
|
||||
query_cache.EraseChannel(channel_id);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::BindInlineIndexBuffer() {
|
||||
if (maxwell3d->inline_index_draw_indexes.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
|
||||
auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count);
|
||||
std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count);
|
||||
buffer_cache_runtime.BindIndexBuffer(
|
||||
maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
|
||||
maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer,
|
||||
static_cast<u32>(buffer.offset), data_count);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -141,8 +141,6 @@ private:
|
||||
|
||||
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
void BindInlineIndexBuffer();
|
||||
|
||||
Tegra::GPU& gpu;
|
||||
|
||||
ScreenInfo& screen_info;
|
||||
|
||||
@@ -221,6 +221,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
|
||||
[[fallthrough]];
|
||||
default:
|
||||
vk::Check(result);
|
||||
break;
|
||||
}
|
||||
});
|
||||
chunk->MarkSubmit();
|
||||
|
||||
@@ -108,6 +108,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid surface type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (info.storage) {
|
||||
|
||||
@@ -170,6 +170,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
|
||||
#undef BPP_CASE
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +218,7 @@ void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_p
|
||||
#undef BPP_CASE
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +242,7 @@ void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes
|
||||
#undef BPP_CASE
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ add_executable(yuzu
|
||||
about_dialog.cpp
|
||||
about_dialog.h
|
||||
aboutdialog.ui
|
||||
applets/qt_amiibo_settings.cpp
|
||||
applets/qt_amiibo_settings.h
|
||||
applets/qt_amiibo_settings.ui
|
||||
applets/qt_controller.cpp
|
||||
applets/qt_controller.h
|
||||
applets/qt_controller.ui
|
||||
|
||||
264
src/yuzu/applets/qt_amiibo_settings.cpp
Normal file
264
src/yuzu/applets/qt_amiibo_settings.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/nfp/nfp_device.h"
|
||||
#include "core/hle/service/nfp/nfp_result.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
#include "input_common/main.h"
|
||||
#include "ui_qt_amiibo_settings.h"
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
#include "web_service/web_backend.h"
|
||||
#endif
|
||||
#include "yuzu/applets/qt_amiibo_settings.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
|
||||
Core::Frontend::CabinetParameters parameters_,
|
||||
InputCommon::InputSubsystem* input_subsystem_,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()),
|
||||
input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)},
|
||||
parameters(std::move(parameters_)) {
|
||||
ui->setupUi(this);
|
||||
|
||||
LoadInfo();
|
||||
|
||||
resize(0, 0);
|
||||
}
|
||||
|
||||
QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default;
|
||||
|
||||
int QtAmiiboSettingsDialog::exec() {
|
||||
if (!is_initalized) {
|
||||
return QDialog::Rejected;
|
||||
}
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
std::string QtAmiiboSettingsDialog::GetName() const {
|
||||
return ui->amiiboCustomNameValue->text().toStdString();
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::LoadInfo() {
|
||||
if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() !=
|
||||
InputCommon::VirtualAmiibo::Info::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
|
||||
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
|
||||
return;
|
||||
}
|
||||
nfp_device->Mount(Service::NFP::MountTarget::All);
|
||||
|
||||
LoadAmiiboInfo();
|
||||
LoadAmiiboData();
|
||||
LoadAmiiboGameInfo();
|
||||
|
||||
ui->amiiboDirectoryValue->setText(
|
||||
QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath()));
|
||||
|
||||
SetSettingsDescription();
|
||||
is_initalized = true;
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::LoadAmiiboInfo() {
|
||||
Service::NFP::ModelInfo model_info{};
|
||||
const auto model_result = nfp_device->GetModelInfo(model_info);
|
||||
|
||||
if (model_result.IsFailure()) {
|
||||
ui->amiiboImageLabel->setVisible(false);
|
||||
ui->amiiboInfoGroup->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto amiibo_id =
|
||||
fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id),
|
||||
model_info.character_variant, model_info.amiibo_type, model_info.model_number,
|
||||
model_info.series);
|
||||
|
||||
LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id);
|
||||
// Note: This function is not being used until we host the images on our server
|
||||
// LoadAmiiboApiInfo(amiibo_id);
|
||||
ui->amiiboImageLabel->setVisible(false);
|
||||
ui->amiiboInfoGroup->setVisible(false);
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
// TODO: Host this data on our website
|
||||
WebService::Client client{"https://amiiboapi.com", {}, {}};
|
||||
WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}};
|
||||
const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id);
|
||||
|
||||
const auto amiibo_json = client.GetJson(url_path, true).returned_data;
|
||||
if (amiibo_json.empty()) {
|
||||
ui->amiiboImageLabel->setVisible(false);
|
||||
ui->amiiboInfoGroup->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string amiibo_series{};
|
||||
std::string amiibo_name{};
|
||||
std::string amiibo_image_url{};
|
||||
std::string amiibo_type{};
|
||||
|
||||
const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo");
|
||||
parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series);
|
||||
parsed_amiibo_json_json.at("name").get_to(amiibo_name);
|
||||
parsed_amiibo_json_json.at("image").get_to(amiibo_image_url);
|
||||
parsed_amiibo_json_json.at("type").get_to(amiibo_type);
|
||||
|
||||
ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series));
|
||||
ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name));
|
||||
ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type));
|
||||
|
||||
if (amiibo_image_url.size() < 34) {
|
||||
ui->amiiboImageLabel->setVisible(false);
|
||||
}
|
||||
|
||||
const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34);
|
||||
const auto image_data = image_client.GetImage(image_url_path, true).returned_data;
|
||||
|
||||
if (image_data.empty()) {
|
||||
ui->amiiboImageLabel->setVisible(false);
|
||||
}
|
||||
|
||||
QPixmap pixmap;
|
||||
pixmap.loadFromData(reinterpret_cast<const u8*>(image_data.data()),
|
||||
static_cast<uint>(image_data.size()));
|
||||
pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio,
|
||||
Qt::TransformationMode::SmoothTransformation);
|
||||
ui->amiiboImageLabel->setPixmap(pixmap);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::LoadAmiiboData() {
|
||||
Service::NFP::RegisterInfo register_info{};
|
||||
Service::NFP::CommonInfo common_info{};
|
||||
const auto register_result = nfp_device->GetRegisterInfo(register_info);
|
||||
const auto common_result = nfp_device->GetCommonInfo(common_info);
|
||||
|
||||
if (register_result.IsFailure()) {
|
||||
ui->creationDateValue->setDisabled(true);
|
||||
ui->modificationDateValue->setDisabled(true);
|
||||
ui->amiiboCustomNameValue->setReadOnly(false);
|
||||
ui->amiiboOwnerValue->setReadOnly(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) {
|
||||
ui->creationDateValue->setDisabled(true);
|
||||
ui->modificationDateValue->setDisabled(true);
|
||||
}
|
||||
|
||||
const auto amiibo_name = std::string(register_info.amiibo_name.data());
|
||||
const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data());
|
||||
const auto creation_date =
|
||||
QDate(register_info.creation_date.year, register_info.creation_date.month,
|
||||
register_info.creation_date.day);
|
||||
|
||||
ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name));
|
||||
ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name));
|
||||
ui->amiiboCustomNameValue->setReadOnly(true);
|
||||
ui->amiiboOwnerValue->setReadOnly(true);
|
||||
ui->creationDateValue->setDate(creation_date);
|
||||
|
||||
if (common_result.IsFailure()) {
|
||||
ui->modificationDateValue->setDisabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto modification_date =
|
||||
QDate(common_info.last_write_date.year, common_info.last_write_date.month,
|
||||
common_info.last_write_date.day);
|
||||
ui->modificationDateValue->setDate(modification_date);
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() {
|
||||
u32 application_area_id{};
|
||||
const auto application_result = nfp_device->GetApplicationAreaId(application_area_id);
|
||||
|
||||
if (application_result.IsFailure()) {
|
||||
ui->gameIdValue->setVisible(false);
|
||||
ui->gameIdLabel->setText(tr("No game data present"));
|
||||
return;
|
||||
}
|
||||
|
||||
SetGameDataName(application_area_id);
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) {
|
||||
static constexpr std::array<std::pair<u32, const char*>, 12> game_name_list = {
|
||||
// 3ds, wii u
|
||||
std::pair<u32, const char*>{0x10110E00, "Super Smash Bros (3DS/WiiU)"},
|
||||
{0x00132600, "Mario & Luigi: Paper Jam"},
|
||||
{0x0014F000, "Animal Crossing: Happy Home Designer"},
|
||||
{0x00152600, "Chibi-Robo!: Zip Lash"},
|
||||
{0x10161f00, "Mario Party 10"},
|
||||
{0x1019C800, "The Legend of Zelda: Twilight Princess HD"},
|
||||
// switch
|
||||
{0x10162B00, "Splatoon 2"},
|
||||
{0x1016e100, "Shovel Knight: Treasure Trove"},
|
||||
{0x1019C800, "The Legend of Zelda: Breath of the Wild"},
|
||||
{0x34F80200, "Super Smash Bros. Ultimate"},
|
||||
{0x38600500, "Splatoon 3"},
|
||||
{0x3B440400, "The Legend of Zelda: Link's Awakening"},
|
||||
};
|
||||
|
||||
for (const auto& [game_id, game_name] : game_name_list) {
|
||||
if (application_area_id == game_id) {
|
||||
ui->gameIdValue->setText(QString::fromStdString(game_name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto application_area_string = fmt::format("{:016x}", application_area_id);
|
||||
ui->gameIdValue->setText(QString::fromStdString(application_area_string));
|
||||
}
|
||||
|
||||
void QtAmiiboSettingsDialog::SetSettingsDescription() {
|
||||
switch (parameters.mode) {
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
ui->cabinetActionDescriptionLabel->setText(
|
||||
tr("The following amiibo data will be formatted:"));
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:"));
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
|
||||
ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:"));
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) {
|
||||
connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent,
|
||||
&GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
|
||||
&QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtAmiiboSettings::~QtAmiiboSettings() = default;
|
||||
|
||||
void QtAmiiboSettings::ShowCabinetApplet(
|
||||
const Core::Frontend::CabinetCallback& callback_,
|
||||
const Core::Frontend::CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
|
||||
callback = std::move(callback_);
|
||||
emit MainWindowShowAmiiboSettings(parameters, nfp_device);
|
||||
}
|
||||
|
||||
void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) {
|
||||
callback(is_success, name);
|
||||
}
|
||||
83
src/yuzu/applets/qt_amiibo_settings.h
Normal file
83
src/yuzu/applets/qt_amiibo_settings.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class QtAmiiboSettingsDialog;
|
||||
}
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
} // namespace Service::NFP
|
||||
|
||||
class QtAmiiboSettingsDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_,
|
||||
InputCommon::InputSubsystem* input_subsystem_,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device_);
|
||||
~QtAmiiboSettingsDialog() override;
|
||||
|
||||
int exec() override;
|
||||
|
||||
std::string GetName() const;
|
||||
|
||||
private:
|
||||
void LoadInfo();
|
||||
void LoadAmiiboInfo();
|
||||
void LoadAmiiboApiInfo(std::string_view amiibo_id);
|
||||
void LoadAmiiboData();
|
||||
void LoadAmiiboGameInfo();
|
||||
void SetGameDataName(u32 application_area_id);
|
||||
void SetSettingsDescription();
|
||||
|
||||
std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui;
|
||||
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
|
||||
|
||||
// Parameters sent in from the backend HLE applet.
|
||||
Core::Frontend::CabinetParameters parameters;
|
||||
|
||||
// If false amiibo settings failed to load
|
||||
bool is_initalized{};
|
||||
};
|
||||
|
||||
class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtAmiiboSettings(GMainWindow& parent);
|
||||
~QtAmiiboSettings() override;
|
||||
|
||||
void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_,
|
||||
const Core::Frontend::CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const;
|
||||
|
||||
private:
|
||||
void MainWindowFinished(bool is_success, const std::string& name);
|
||||
|
||||
mutable Core::Frontend::CabinetCallback callback;
|
||||
};
|
||||
494
src/yuzu/applets/qt_amiibo_settings.ui
Normal file
494
src/yuzu/applets/qt_amiibo_settings.ui
Normal file
@@ -0,0 +1,494 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtAmiiboSettingsDialog</class>
|
||||
<widget class="QDialog" name="QtAmiiboSettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>839</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Amiibo Settings</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="mainControllerApplet" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1" stretch="0,3,0">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="topControllerApplet" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="cabinetActionDescriptionLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="middleControllerApplet" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="amiiboImageLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>236</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="amiiboInfoGroup">
|
||||
<property name="title">
|
||||
<string>Amiibo Info</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="amiiboSeriesLabel">
|
||||
<property name="text">
|
||||
<string>Series</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="amiiboSeriesValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="amiiboTypeLabel">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="amiiboTypeValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="amiiboNameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="amiiboNameValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="amiiboDataGroup">
|
||||
<property name="title">
|
||||
<string>Amiibo Data</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="amiiboCustomNameLabel">
|
||||
<property name="text">
|
||||
<string>Custom Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="amiiboCustomNameValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="amiiboOwnerLabel">
|
||||
<property name="text">
|
||||
<string>Owner</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="amiiboOwnerValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="creationDateLabel">
|
||||
<property name="text">
|
||||
<string>Creation Date</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDateTimeEdit" name="creationDateValue">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumDate">
|
||||
<date>
|
||||
<year>1970</year>
|
||||
<month>1</month>
|
||||
<day>1</day>
|
||||
</date>
|
||||
</property>
|
||||
<property name="displayFormat">
|
||||
<string>dd/MM/yyyy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="modificationDateLabel">
|
||||
<property name="text">
|
||||
<string>Modification Date</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDateTimeEdit" name="modificationDateValue">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumDate">
|
||||
<date>
|
||||
<year>1970</year>
|
||||
<month>1</month>
|
||||
<day>1</day>
|
||||
</date>
|
||||
</property>
|
||||
<property name="displayFormat">
|
||||
<string>dd/MM/yyyy </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gameDataGroup">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Game Data</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="gameIdLabel">
|
||||
<property name="text">
|
||||
<string>Game Id</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="gameIdValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="MountAmiiboGroup">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Mount Amiibo</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="3">
|
||||
<widget class="QToolButton" name="amiiboDirectoryButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="amiiboDirectoryLabel">
|
||||
<property name="text">
|
||||
<string>File Path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="amiiboDirectoryValue"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="bottomControllerApplet" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item alignment="Qt::AlignBottom">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>QtAmiiboSettingsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>QtAmiiboSettingsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -126,6 +126,7 @@ void CompatDB::Submit() {
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#endif
|
||||
|
||||
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
||||
#include "applets/qt_amiibo_settings.h"
|
||||
#include "applets/qt_controller.h"
|
||||
#include "applets/qt_error.h"
|
||||
#include "applets/qt_profile_select.h"
|
||||
@@ -26,6 +27,7 @@
|
||||
#include "configuration/configure_tas.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
#include "core/frontend/applets/mii_edit.h"
|
||||
@@ -548,6 +550,11 @@ void GMainWindow::RegisterMetaTypes() {
|
||||
|
||||
// Register applet types
|
||||
|
||||
// Cabinet Applet
|
||||
qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters");
|
||||
qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>(
|
||||
"std::shared_ptr<Service::NFP::NfpDevice>");
|
||||
|
||||
// Controller Applet
|
||||
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
|
||||
|
||||
@@ -569,6 +576,21 @@ void GMainWindow::RegisterMetaTypes() {
|
||||
qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
|
||||
}
|
||||
|
||||
void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) {
|
||||
QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device);
|
||||
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
|
||||
Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
emit AmiiboSettingsFinished(false, {});
|
||||
return;
|
||||
}
|
||||
|
||||
emit AmiiboSettingsFinished(true, dialog.GetName());
|
||||
}
|
||||
|
||||
void GMainWindow::ControllerSelectorReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters) {
|
||||
QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system);
|
||||
@@ -1546,6 +1568,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
|
||||
system->SetFilesystem(vfs);
|
||||
|
||||
system->SetAppletFrontendSet({
|
||||
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
|
||||
std::make_unique<QtControllerSelector>(*this), // Controller Selector
|
||||
std::make_unique<QtErrorDisplay>(*this), // Error Display
|
||||
nullptr, // Mii Editor
|
||||
@@ -1956,6 +1979,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
|
||||
const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path));
|
||||
@@ -3199,6 +3223,7 @@ void GMainWindow::OnToggleGpuAccuracy() {
|
||||
case Settings::GPUAccuracy::Extreme:
|
||||
default: {
|
||||
Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3531,6 +3556,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {
|
||||
default: {
|
||||
gpu_accuracy_button->setText(tr("GPU ERROR"));
|
||||
gpu_accuracy_button->setChecked(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct CabinetParameters;
|
||||
struct ControllerParameters;
|
||||
struct InlineAppearParameters;
|
||||
struct InlineTextParameters;
|
||||
@@ -82,6 +83,10 @@ enum class SwkbdReplyType : u32;
|
||||
enum class WebExitReason : u32;
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
} // namespace Service::NFP
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
@@ -149,6 +154,8 @@ signals:
|
||||
|
||||
void UpdateInstallProgress();
|
||||
|
||||
void AmiiboSettingsFinished(bool is_success, const std::string& name);
|
||||
|
||||
void ControllerSelectorReconfigureFinished();
|
||||
|
||||
void ErrorDisplayFinished();
|
||||
@@ -170,6 +177,8 @@ public slots:
|
||||
void OnExecuteProgram(std::size_t program_index);
|
||||
void OnExit();
|
||||
void OnSaveConfig();
|
||||
void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device);
|
||||
void ControllerSelectorReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters);
|
||||
void SoftwareKeyboardInitialize(
|
||||
|
||||
@@ -84,6 +84,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
|
||||
std::exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
OnResize();
|
||||
|
||||
@@ -351,6 +351,7 @@ int main(int argc, char** argv) {
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
|
||||
|
||||
Reference in New Issue
Block a user