Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 307371e01d | |||
| 00800d5289 | |||
| 101c0df79c | |||
| 54b4c84ab6 | |||
| 4f77275cd5 | |||
| 47b80da906 | |||
| baf4d1c22e | |||
| add4f82c33 | |||
| abb785f269 | |||
| 8908663f12 | |||
| 25c4ae6e11 | |||
| 6aaa8acff9 | |||
| 455a736250 | |||
| 0afb9631b5 | |||
| bbdfe1fab1 | |||
| ff386e794b | |||
| a9623d5f55 | |||
| d2ae39bf4b | |||
| 756f013d8a | |||
| 9c94faaa2b | |||
| 6775a6ee02 | |||
| 1ab052952d | |||
| b2772bcb0d | |||
| ff4fbaf152 | |||
| 986336b0d8 | |||
| 2ddecb9631 | |||
| 0730dc6c44 | |||
| 083d913eab | |||
| ff2089fdf5 | |||
| b000af0054 | |||
| fbf0a9c976 | |||
| 11edba4974 | |||
| 8e84381038 | |||
| e446f368d7 | |||
| 668eb5b8da | |||
| 8bdc51b620 | |||
| 9efd95cda5 | |||
| 9c96d40586 | |||
| 40efd2ab56 | |||
| c68b4bee2c | |||
| 530fe24768 | |||
| 237934b736 | |||
| ea5dd02db9 | |||
| c2c7386dfd | |||
| d8fc3f403b | |||
| 50a59487eb | |||
| 950db851ea | |||
| 11ffbee5ae |
@@ -22,6 +22,7 @@ cmake .. \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||
-DYUZU_ENABLE_LTO=ON \
|
||||
-GNinja
|
||||
|
||||
ninja
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@ find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
|
||||
if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
find_package(Vulkan 1.3.238 REQUIRED)
|
||||
find_package(Vulkan 1.3.246 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
|
||||
Vendored
+363
-368
File diff suppressed because it is too large
Load Diff
Vendored
+363
-368
File diff suppressed because it is too large
Load Diff
Vendored
+363
-368
File diff suppressed because it is too large
Load Diff
Vendored
+366
-371
File diff suppressed because it is too large
Load Diff
Vendored
+363
-368
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+368
-373
File diff suppressed because it is too large
Load Diff
Vendored
+362
-367
File diff suppressed because it is too large
Load Diff
Vendored
+368
-373
File diff suppressed because it is too large
Load Diff
Vendored
+379
-381
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+363
-368
File diff suppressed because it is too large
Load Diff
Vendored
+362
-367
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+373
-378
File diff suppressed because it is too large
Load Diff
Vendored
+374
-379
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+362
-367
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+364
-369
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: 00671c64ba...63af1cf1ee
Vendored
+1
-1
Submodule externals/dynarmic updated: 165621a872...c08c5a9362
@@ -126,6 +126,17 @@ else()
|
||||
add_compile_options("-stdlib=libc++")
|
||||
endif()
|
||||
|
||||
# GCC bugs
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# These diagnostics would be great if they worked, but are just completely broken
|
||||
# and produce bogus errors on external libraries like fmt.
|
||||
add_compile_options(
|
||||
-Wno-array-bounds
|
||||
-Wno-stringop-overread
|
||||
-Wno-stringop-overflow
|
||||
)
|
||||
endif()
|
||||
|
||||
# Set file offset size to 64 bits.
|
||||
#
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
|
||||
@@ -121,8 +121,7 @@ u64 DeviceSession::GetPlayedSampleCount() const {
|
||||
}
|
||||
|
||||
std::optional<std::chrono::nanoseconds> DeviceSession::ThreadFunc() {
|
||||
// Add 5ms of samples at a 48K sample rate.
|
||||
played_sample_count += 48'000 * INCREMENT_TIME / 1s;
|
||||
played_sample_count = stream->GetExpectedPlayedSampleCount();
|
||||
if (type == Sink::StreamType::Out) {
|
||||
system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioOutManager, true);
|
||||
} else {
|
||||
|
||||
@@ -189,6 +189,8 @@ void AudioRenderer::ThreadFunc() {
|
||||
max_time = std::min(command_buffer.time_limit, max_time);
|
||||
command_list_processor.SetProcessTimeMax(max_time);
|
||||
|
||||
streams[index]->WaitFreeSpace();
|
||||
|
||||
// Process the command list
|
||||
{
|
||||
MICROPROFILE_SCOPE(Audio_Renderer);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "audio_core/renderer/adsp/command_buffer.h"
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/reader_writer_queue.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
|
||||
@@ -15,14 +15,9 @@ MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager",
|
||||
MP_RGB(60, 19, 97));
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
constexpr std::chrono::nanoseconds RENDER_TIME{5'000'000UL};
|
||||
|
||||
SystemManager::SystemManager(Core::System& core_)
|
||||
: core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()},
|
||||
thread_event{Core::Timing::CreateEvent(
|
||||
"AudioRendererSystemManager", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) {
|
||||
return ThreadFunc2(time);
|
||||
})} {}
|
||||
: core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()} {}
|
||||
|
||||
SystemManager::~SystemManager() {
|
||||
Stop();
|
||||
@@ -33,8 +28,6 @@ bool SystemManager::InitializeUnsafe() {
|
||||
if (adsp.Start()) {
|
||||
active = true;
|
||||
thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); });
|
||||
core.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), RENDER_TIME,
|
||||
thread_event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +38,6 @@ void SystemManager::Stop() {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
core.CoreTiming().UnscheduleEvent(thread_event, {});
|
||||
active = false;
|
||||
update.store(true);
|
||||
update.notify_all();
|
||||
@@ -111,16 +103,7 @@ void SystemManager::ThreadFunc() {
|
||||
|
||||
adsp.Signal();
|
||||
adsp.Wait();
|
||||
|
||||
update.wait(false);
|
||||
update.store(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::chrono::nanoseconds> SystemManager::ThreadFunc2(s64 time) {
|
||||
update.store(true);
|
||||
update.notify_all();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
||||
|
||||
@@ -68,11 +68,6 @@ private:
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/**
|
||||
* Signalling core timing thread to run ThreadFunc.
|
||||
*/
|
||||
std::optional<std::chrono::nanoseconds> ThreadFunc2(s64 time);
|
||||
|
||||
enum class StreamState {
|
||||
Filling,
|
||||
Steady,
|
||||
@@ -95,8 +90,6 @@ private:
|
||||
ADSP::ADSP& adsp;
|
||||
/// AudioRenderer mailbox for communication
|
||||
ADSP::AudioRenderer_Mailbox* mailbox{};
|
||||
/// Core timing event to signal main thread
|
||||
std::shared_ptr<Core::Timing::EventType> thread_event;
|
||||
/// Atomic for main thread to wait on
|
||||
std::atomic<bool> update{};
|
||||
};
|
||||
|
||||
@@ -101,8 +101,6 @@ public:
|
||||
~CubebSinkStream() override {
|
||||
LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name);
|
||||
|
||||
Unstall();
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
@@ -143,8 +141,6 @@ public:
|
||||
* Stop the sink stream.
|
||||
*/
|
||||
void Stop() override {
|
||||
Unstall();
|
||||
|
||||
if (!ctx || paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ public:
|
||||
* Finalize the sink stream.
|
||||
*/
|
||||
void Finalize() override {
|
||||
Unstall();
|
||||
if (device == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -116,7 +115,6 @@ public:
|
||||
* Stop the sink stream.
|
||||
*/
|
||||
void Stop() override {
|
||||
Unstall();
|
||||
if (device == 0 || paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "common/fixed_point.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
@@ -149,10 +151,6 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
|
||||
return;
|
||||
}
|
||||
|
||||
if (queued_buffers > max_queue_size) {
|
||||
Stall();
|
||||
}
|
||||
|
||||
while (frames_written < num_frames) {
|
||||
// If the playing buffer has been consumed or has no frames, we need a new one
|
||||
if (playing_buffer.consumed || playing_buffer.frames == 0) {
|
||||
@@ -187,10 +185,6 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
|
||||
}
|
||||
|
||||
std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes);
|
||||
|
||||
if (queued_buffers <= max_queue_size) {
|
||||
Unstall();
|
||||
}
|
||||
}
|
||||
|
||||
void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames) {
|
||||
@@ -198,10 +192,15 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
const std::size_t frame_size = num_channels;
|
||||
const std::size_t frame_size_bytes = frame_size * sizeof(s16);
|
||||
size_t frames_written{0};
|
||||
size_t actual_frames_written{0};
|
||||
|
||||
// If we're paused or going to shut down, we don't want to consume buffers as coretiming is
|
||||
// paused and we'll desync, so just play silence.
|
||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||
if (system.IsShuttingDown()) {
|
||||
release_cv.notify_one();
|
||||
}
|
||||
|
||||
static constexpr std::array<s16, 6> silence{};
|
||||
for (size_t i = frames_written; i < num_frames; i++) {
|
||||
std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes);
|
||||
@@ -209,20 +208,6 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
return;
|
||||
}
|
||||
|
||||
// Due to many frames being queued up with nvdec (5 frames or so?), a lot of buffers also get
|
||||
// queued up (30+) but not all at once, which causes constant stalling here, so just let the
|
||||
// video play out without attempting to stall.
|
||||
// Can hopefully remove this later with a more complete NVDEC implementation.
|
||||
const auto nvdec_active{system.AudioCore().IsNVDECActive()};
|
||||
|
||||
// Core timing cannot be paused in single-core mode, so Stall ends up being called over and over
|
||||
// and never recovers to a normal state, so just skip attempting to sync things on single-core.
|
||||
if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) {
|
||||
Stall();
|
||||
} else if (system.IsMulticore() && queued_buffers <= max_queue_size) {
|
||||
Unstall();
|
||||
}
|
||||
|
||||
while (frames_written < num_frames) {
|
||||
// If the playing buffer has been consumed or has no frames, we need a new one
|
||||
if (playing_buffer.consumed || playing_buffer.frames == 0) {
|
||||
@@ -237,6 +222,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
}
|
||||
// Successfully dequeued a new buffer.
|
||||
queued_buffers--;
|
||||
|
||||
{ std::unique_lock lk{release_mutex}; }
|
||||
|
||||
release_cv.notify_one();
|
||||
}
|
||||
|
||||
// Get the minimum frames available between the currently playing buffer, and the
|
||||
@@ -248,6 +237,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
frames_available * frame_size);
|
||||
|
||||
frames_written += frames_available;
|
||||
actual_frames_written += frames_available;
|
||||
playing_buffer.frames_played += frames_available;
|
||||
|
||||
// If that's all the frames in the current buffer, add its samples and mark it as
|
||||
@@ -260,26 +250,29 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size],
|
||||
frame_size_bytes);
|
||||
|
||||
if (system.IsMulticore() && queued_buffers <= max_queue_size) {
|
||||
Unstall();
|
||||
{
|
||||
std::scoped_lock lk{sample_count_lock};
|
||||
last_sample_count_update_time = system.CoreTiming().GetGlobalTimeNs();
|
||||
min_played_sample_count = max_played_sample_count;
|
||||
max_played_sample_count += actual_frames_written;
|
||||
}
|
||||
}
|
||||
|
||||
void SinkStream::Stall() {
|
||||
std::scoped_lock lk{stall_guard};
|
||||
if (stalled_lock) {
|
||||
return;
|
||||
}
|
||||
stalled_lock = system.StallApplication();
|
||||
u64 SinkStream::GetExpectedPlayedSampleCount() {
|
||||
std::scoped_lock lk{sample_count_lock};
|
||||
auto cur_time{system.CoreTiming().GetGlobalTimeNs()};
|
||||
auto time_delta{cur_time - last_sample_count_update_time};
|
||||
auto exp_played_sample_count{min_played_sample_count +
|
||||
(TargetSampleRate * time_delta) / std::chrono::seconds{1}};
|
||||
|
||||
// Add 15ms of latency in sample reporting to allow for some leeway in scheduler timings
|
||||
return std::min<u64>(exp_played_sample_count, max_played_sample_count) + TargetSampleCount * 3;
|
||||
}
|
||||
|
||||
void SinkStream::Unstall() {
|
||||
std::scoped_lock lk{stall_guard};
|
||||
if (!stalled_lock) {
|
||||
return;
|
||||
}
|
||||
system.UnstallApplication();
|
||||
stalled_lock.unlock();
|
||||
void SinkStream::WaitFreeSpace() {
|
||||
std::unique_lock lk{release_mutex};
|
||||
release_cv.wait(
|
||||
lk, [this]() { return queued_buffers < max_queue_size || system.IsShuttingDown(); });
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/reader_writer_queue.h"
|
||||
#include "common/ring_buffer.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -53,9 +55,7 @@ struct SinkBuffer {
|
||||
class SinkStream {
|
||||
public:
|
||||
explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {}
|
||||
virtual ~SinkStream() {
|
||||
Unstall();
|
||||
}
|
||||
virtual ~SinkStream() {}
|
||||
|
||||
/**
|
||||
* Finalize the sink stream.
|
||||
@@ -201,14 +201,16 @@ public:
|
||||
void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames);
|
||||
|
||||
/**
|
||||
* Stall core processes if the audio thread falls too far behind.
|
||||
* Get the total number of samples expected to have been played by this stream.
|
||||
*
|
||||
* @return The number of samples.
|
||||
*/
|
||||
void Stall();
|
||||
u64 GetExpectedPlayedSampleCount();
|
||||
|
||||
/**
|
||||
* Unstall core processes.
|
||||
* Waits for free space in the sample ring buffer
|
||||
*/
|
||||
void Unstall();
|
||||
void WaitFreeSpace();
|
||||
|
||||
protected:
|
||||
/// Core system
|
||||
@@ -237,12 +239,21 @@ private:
|
||||
std::atomic<u32> queued_buffers{};
|
||||
/// The ring size for audio out buffers (usually 4, rarely 2 or 8)
|
||||
u32 max_queue_size{};
|
||||
/// Locks access to sample count tracking info
|
||||
std::mutex sample_count_lock;
|
||||
/// Minimum number of total samples that have been played since the last callback
|
||||
u64 min_played_sample_count{};
|
||||
/// Maximum number of total samples that can be played since the last callback
|
||||
u64 max_played_sample_count{};
|
||||
/// The time the two above tracking variables were last written to
|
||||
std::chrono::nanoseconds last_sample_count_update_time{};
|
||||
/// Set by the audio render/in/out system which uses this stream
|
||||
f32 system_volume{1.0f};
|
||||
/// Set via IAudioDevice service calls
|
||||
f32 device_volume{1.0f};
|
||||
std::mutex stall_guard;
|
||||
std::unique_lock<std::mutex> stalled_lock;
|
||||
/// Signalled when ring buffer entries are consumed
|
||||
std::condition_variable release_cv;
|
||||
std::mutex release_mutex;
|
||||
};
|
||||
|
||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||
|
||||
@@ -96,10 +96,6 @@ public:
|
||||
return m_node == rhs.m_node;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr pointer operator->() const {
|
||||
return m_node;
|
||||
}
|
||||
@@ -324,10 +320,6 @@ public:
|
||||
return m_impl == rhs.m_impl;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr pointer operator->() const {
|
||||
return Traits::GetParent(std::addressof(*m_impl));
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ public:
|
||||
Map(address, address_end, null_value);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const {
|
||||
[[nodiscard]] size_t GetContinuousSizeFrom(KeyTBase address) const {
|
||||
const KeyT new_address = static_cast<KeyT>(address);
|
||||
if (new_address < 0) {
|
||||
return 0;
|
||||
}
|
||||
return ContinousSizeInternal(new_address);
|
||||
return ContinuousSizeInternal(new_address);
|
||||
}
|
||||
|
||||
[[nodiscard]] ValueT GetValueAt(KeyT address) const {
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
using IteratorType = typename MapType::iterator;
|
||||
using ConstIteratorType = typename MapType::const_iterator;
|
||||
|
||||
size_t ContinousSizeInternal(KeyT address) const {
|
||||
size_t ContinuousSizeInternal(KeyT address) const {
|
||||
const auto it = GetFirstElementBeforeOrOn(address);
|
||||
if (it == container.end() || it->second == null_value) {
|
||||
return 0;
|
||||
|
||||
@@ -116,7 +116,6 @@ public:
|
||||
|
||||
// Comparison operators.
|
||||
constexpr bool operator==(const TypedAddress&) const = default;
|
||||
constexpr bool operator!=(const TypedAddress&) const = default;
|
||||
constexpr auto operator<=>(const TypedAddress&) const = default;
|
||||
|
||||
// For convenience, also define comparison operators versus uint64_t.
|
||||
@@ -124,10 +123,6 @@ public:
|
||||
return m_address == rhs;
|
||||
}
|
||||
|
||||
constexpr inline bool operator!=(uint64_t rhs) const {
|
||||
return m_address != rhs;
|
||||
}
|
||||
|
||||
// Allow getting the address explicitly, for use in accessors.
|
||||
constexpr inline uint64_t GetValue() const {
|
||||
return m_address;
|
||||
|
||||
@@ -570,10 +570,10 @@ add_library(core STATIC
|
||||
hle/service/nfp/nfp.h
|
||||
hle/service/nfp/nfp_device.cpp
|
||||
hle/service/nfp/nfp_device.h
|
||||
hle/service/nfp/nfp_interface.cpp
|
||||
hle/service/nfp/nfp_interface.h
|
||||
hle/service/nfp/nfp_result.h
|
||||
hle/service/nfp/nfp_types.h
|
||||
hle/service/nfp/nfp_user.cpp
|
||||
hle/service/nfp/nfp_user.h
|
||||
hle/service/ngct/ngct.cpp
|
||||
hle/service/ngct/ngct.h
|
||||
hle/service/nifm/nifm.cpp
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <memory>
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include <dynarmic/interface/A32/config.h>
|
||||
#include <dynarmic/interface/A32/context.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -410,21 +409,19 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
Dynarmic::A32::Context context;
|
||||
jit.load()->SaveContext(context);
|
||||
ctx.cpu_registers = context.Regs();
|
||||
ctx.extension_registers = context.ExtRegs();
|
||||
ctx.cpsr = context.Cpsr();
|
||||
ctx.fpscr = context.Fpscr();
|
||||
Dynarmic::A32::Jit* j = jit.load();
|
||||
ctx.cpu_registers = j->Regs();
|
||||
ctx.extension_registers = j->ExtRegs();
|
||||
ctx.cpsr = j->Cpsr();
|
||||
ctx.fpscr = j->Fpscr();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
Dynarmic::A32::Context context;
|
||||
context.Regs() = ctx.cpu_registers;
|
||||
context.ExtRegs() = ctx.extension_registers;
|
||||
context.SetCpsr(ctx.cpsr);
|
||||
context.SetFpscr(ctx.fpscr);
|
||||
jit.load()->LoadContext(context);
|
||||
Dynarmic::A32::Jit* j = jit.load();
|
||||
j->Regs() = ctx.cpu_registers;
|
||||
j->ExtRegs() = ctx.extension_registers;
|
||||
j->SetCpsr(ctx.cpsr);
|
||||
j->SetFpscr(ctx.fpscr);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SignalInterrupt() {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class Applet {
|
||||
public:
|
||||
virtual ~Applet() = default;
|
||||
virtual void Close() const = 0;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -10,6 +10,8 @@ namespace Core::Frontend {
|
||||
|
||||
CabinetApplet::~CabinetApplet() = default;
|
||||
|
||||
void DefaultCabinetApplet::Close() const {}
|
||||
|
||||
void DefaultCabinetApplet::ShowCabinetApplet(
|
||||
const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "core/frontend/applets/applet.h"
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
@@ -20,7 +21,7 @@ struct CabinetParameters {
|
||||
|
||||
using CabinetCallback = std::function<void(bool, const std::string&)>;
|
||||
|
||||
class CabinetApplet {
|
||||
class CabinetApplet : public Applet {
|
||||
public:
|
||||
virtual ~CabinetApplet();
|
||||
virtual void ShowCabinetApplet(const CabinetCallback& callback,
|
||||
@@ -30,6 +31,7 @@ public:
|
||||
|
||||
class DefaultCabinetApplet final : public CabinetApplet {
|
||||
public:
|
||||
void Close() const override;
|
||||
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@ DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_
|
||||
|
||||
DefaultControllerApplet::~DefaultControllerApplet() = default;
|
||||
|
||||
void DefaultControllerApplet::Close() const {}
|
||||
|
||||
void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callback,
|
||||
const ControllerParameters& parameters) const {
|
||||
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
|
||||
@@ -69,7 +71,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
||||
}
|
||||
}
|
||||
|
||||
callback();
|
||||
callback(true);
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/applets/applet.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class HIDCore;
|
||||
@@ -34,9 +35,9 @@ struct ControllerParameters {
|
||||
bool allow_gamecube_controller{};
|
||||
};
|
||||
|
||||
class ControllerApplet {
|
||||
class ControllerApplet : public Applet {
|
||||
public:
|
||||
using ReconfigureCallback = std::function<void()>;
|
||||
using ReconfigureCallback = std::function<void(bool)>;
|
||||
|
||||
virtual ~ControllerApplet();
|
||||
|
||||
@@ -49,6 +50,7 @@ public:
|
||||
explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
|
||||
~DefaultControllerApplet() override;
|
||||
|
||||
void Close() const override;
|
||||
void ReconfigureControllers(ReconfigureCallback callback,
|
||||
const ControllerParameters& parameters) const override;
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Core::Frontend {
|
||||
|
||||
ErrorApplet::~ErrorApplet() = default;
|
||||
|
||||
void DefaultErrorApplet::Close() const {}
|
||||
|
||||
void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const {
|
||||
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
|
||||
error.module.Value(), error.description.Value(), error.raw);
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
#include "core/frontend/applets/applet.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ErrorApplet {
|
||||
class ErrorApplet : public Applet {
|
||||
public:
|
||||
using FinishedCallback = std::function<void()>;
|
||||
|
||||
@@ -28,6 +29,7 @@ public:
|
||||
|
||||
class DefaultErrorApplet final : public ErrorApplet {
|
||||
public:
|
||||
void Close() const override;
|
||||
void ShowError(Result error, FinishedCallback finished) const override;
|
||||
void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
|
||||
FinishedCallback finished) const override;
|
||||
|
||||
@@ -10,6 +10,8 @@ ParentalControlsApplet::~ParentalControlsApplet() = default;
|
||||
|
||||
DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default;
|
||||
|
||||
void DefaultParentalControlsApplet::Close() const {}
|
||||
|
||||
void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) {
|
||||
LOG_INFO(Service_AM,
|
||||
@@ -39,6 +41,8 @@ PhotoViewerApplet::~PhotoViewerApplet() = default;
|
||||
|
||||
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default;
|
||||
|
||||
void DefaultPhotoViewerApplet::Close() const {}
|
||||
|
||||
void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
|
||||
std::function<void()> finished) const {
|
||||
LOG_INFO(Service_AM,
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include <functional>
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/frontend/applets/applet.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ParentalControlsApplet {
|
||||
class ParentalControlsApplet : public Applet {
|
||||
public:
|
||||
virtual ~ParentalControlsApplet();
|
||||
|
||||
@@ -33,6 +35,7 @@ class DefaultParentalControlsApplet final : public ParentalControlsApplet {
|
||||
public:
|
||||
~DefaultParentalControlsApplet() override;
|
||||
|
||||
void Close() const override;
|
||||
void VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) override;
|
||||
void VerifyPINForSettings(std::function<void(bool)> finished) override;
|
||||
@@ -40,7 +43,7 @@ public:
|
||||
void ChangePIN(std::function<void()> finished) override;
|
||||
};
|
||||
|
||||
class PhotoViewerApplet {
|
||||
class PhotoViewerApplet : public Applet {
|
||||
public:
|
||||
virtual ~PhotoViewerApplet();
|
||||
|
||||
@@ -52,6 +55,7 @@ class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
|
||||
public:
|
||||
~DefaultPhotoViewerApplet() override;
|
||||
|
||||
void Close() const override;
|
||||
void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
|
||||
void ShowAllPhotos(std::function<void()> finished) const override;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Core::Frontend {
|
||||
|
||||
MiiEditApplet::~MiiEditApplet() = default;
|
||||
|
||||
void DefaultMiiEditApplet::Close() const {}
|
||||
|
||||
void DefaultMiiEditApplet::ShowMiiEdit(const MiiEditCallback& callback) const {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "core/frontend/applets/applet.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class MiiEditApplet {
|
||||
class MiiEditApplet : public Applet {
|
||||
public:
|
||||
using MiiEditCallback = std::function<void()>;
|
||||
|
||||
@@ -18,6 +20,7 @@ public:
|
||||
|
||||
class DefaultMiiEditApplet final : public MiiEditApplet {
|
||||
public:
|
||||
void Close() const override;
|
||||
void ShowMiiEdit(const MiiEditCallback& callback) const override;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ namespace Core::Frontend {
|
||||
|
||||
ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||
|
||||
void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback) const {
|
||||
void DefaultProfileSelectApplet::Close() const {}
|
||||
|
||||
void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback,
|
||||
const ProfileSelectParameters& parameters) const {
|
||||
Service::Account::ProfileManager manager;
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
|
||||
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||
|
||||
@@ -5,22 +5,35 @@
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "common/uuid.h"
|
||||
#include "core/frontend/applets/applet.h"
|
||||
#include "core/hle/service/am/applets/applet_profile_select.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ProfileSelectApplet {
|
||||
struct ProfileSelectParameters {
|
||||
Service::AM::Applets::UiMode mode;
|
||||
std::array<Common::UUID, 8> invalid_uid_list;
|
||||
Service::AM::Applets::UiSettingsDisplayOptions display_options;
|
||||
Service::AM::Applets::UserSelectionPurpose purpose;
|
||||
};
|
||||
|
||||
class ProfileSelectApplet : public Applet {
|
||||
public:
|
||||
using SelectProfileCallback = std::function<void(std::optional<Common::UUID>)>;
|
||||
|
||||
virtual ~ProfileSelectApplet();
|
||||
|
||||
virtual void SelectProfile(SelectProfileCallback callback) const = 0;
|
||||
virtual void SelectProfile(SelectProfileCallback callback,
|
||||
const ProfileSelectParameters& parameters) const = 0;
|
||||
};
|
||||
|
||||
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
|
||||
public:
|
||||
void SelectProfile(SelectProfileCallback callback) const override;
|
||||
void Close() const override;
|
||||
void SelectProfile(SelectProfileCallback callback,
|
||||
const ProfileSelectParameters& parameters) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -13,6 +13,8 @@ SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
|
||||
|
||||
DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default;
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::Close() const {}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::InitializeKeyboard(
|
||||
bool is_inline, KeyboardInitializeParameters initialize_parameters,
|
||||
SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/frontend/applets/applet.h"
|
||||
#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
@@ -52,7 +53,7 @@ struct InlineTextParameters {
|
||||
s32 cursor_position;
|
||||
};
|
||||
|
||||
class SoftwareKeyboardApplet {
|
||||
class SoftwareKeyboardApplet : public Applet {
|
||||
public:
|
||||
using SubmitInlineCallback =
|
||||
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>;
|
||||
@@ -84,6 +85,8 @@ class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
||||
public:
|
||||
~DefaultSoftwareKeyboardApplet() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
void InitializeKeyboard(bool is_inline, KeyboardInitializeParameters initialize_parameters,
|
||||
SubmitNormalCallback submit_normal_callback_,
|
||||
SubmitInlineCallback submit_inline_callback_) override;
|
||||
|
||||
@@ -10,6 +10,8 @@ WebBrowserApplet::~WebBrowserApplet() = default;
|
||||
|
||||
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
|
||||
|
||||
void DefaultWebBrowserApplet::Close() const {}
|
||||
|
||||
void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url,
|
||||
ExtractROMFSCallback extract_romfs_callback,
|
||||
OpenWebPageCallback callback) const {
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "core/frontend/applets/applet.h"
|
||||
#include "core/hle/service/am/applets/applet_web_browser_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class WebBrowserApplet {
|
||||
class WebBrowserApplet : public Applet {
|
||||
public:
|
||||
using ExtractROMFSCallback = std::function<void()>;
|
||||
using OpenWebPageCallback =
|
||||
@@ -29,6 +30,8 @@ class DefaultWebBrowserApplet final : public WebBrowserApplet {
|
||||
public:
|
||||
~DefaultWebBrowserApplet() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
void OpenLocalWebPage(const std::string& local_url, ExtractROMFSCallback extract_romfs_callback,
|
||||
OpenWebPageCallback callback) const override;
|
||||
|
||||
|
||||
@@ -35,11 +35,11 @@ namespace {
|
||||
using namespace Common::Literals;
|
||||
|
||||
u32 GetMemorySizeForInit() {
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_8GB : Smc::MemorySize_4GB;
|
||||
}
|
||||
|
||||
Smc::MemoryArrangement GetMemoryArrangeForInit() {
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_8GB
|
||||
: Smc::MemoryArrangement_4GB;
|
||||
}
|
||||
} // namespace
|
||||
@@ -91,7 +91,8 @@ std::size_t KSystemControl::Init::GetApplicationPoolSize() {
|
||||
case Smc::MemoryArrangement_6GBForAppletDev:
|
||||
return 3285_MiB;
|
||||
case Smc::MemoryArrangement_8GB:
|
||||
return 4916_MiB;
|
||||
// Real kernel sets this to 4916_MiB. We are not debugging applets.
|
||||
return 6547_MiB;
|
||||
}
|
||||
}();
|
||||
|
||||
@@ -115,7 +116,8 @@ size_t KSystemControl::Init::GetAppletPoolSize() {
|
||||
case Smc::MemoryArrangement_6GBForAppletDev:
|
||||
return 2193_MiB;
|
||||
case Smc::MemoryArrangement_8GB:
|
||||
return 2193_MiB;
|
||||
//! Real kernel sets this to 2193_MiB. We are not debugging applets.
|
||||
return 562_MiB;
|
||||
}
|
||||
}();
|
||||
|
||||
|
||||
@@ -35,24 +35,30 @@ bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address
|
||||
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
|
||||
// Load the value from the address.
|
||||
const s32 current_value =
|
||||
static_cast<s32>(monitor.ExclusiveRead32(current_core, GetInteger(address)));
|
||||
s32 current_value{};
|
||||
|
||||
// Compare it to the desired one.
|
||||
if (current_value < value) {
|
||||
// If less than, we want to try to decrement.
|
||||
const s32 decrement_value = current_value - 1;
|
||||
while (true) {
|
||||
// Load the value from the address.
|
||||
current_value =
|
||||
static_cast<s32>(monitor.ExclusiveRead32(current_core, GetInteger(address)));
|
||||
|
||||
// Compare it to the desired one.
|
||||
if (current_value < value) {
|
||||
// If less than, we want to try to decrement.
|
||||
const s32 decrement_value = current_value - 1;
|
||||
|
||||
// Decrement and try to store.
|
||||
if (monitor.ExclusiveWrite32(current_core, GetInteger(address),
|
||||
static_cast<u32>(decrement_value))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Decrement and try to store.
|
||||
if (!monitor.ExclusiveWrite32(current_core, GetInteger(address),
|
||||
static_cast<u32>(decrement_value))) {
|
||||
// If we failed to store, try again.
|
||||
DecrementIfLessThan(system, out, address, value);
|
||||
} else {
|
||||
// Otherwise, clear our exclusive hold and finish
|
||||
monitor.ClearExclusive(current_core);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, clear our exclusive hold and finish
|
||||
monitor.ClearExclusive(current_core);
|
||||
}
|
||||
|
||||
// We're done.
|
||||
@@ -70,23 +76,29 @@ bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32
|
||||
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
|
||||
s32 current_value{};
|
||||
|
||||
// Load the value from the address.
|
||||
const s32 current_value =
|
||||
static_cast<s32>(monitor.ExclusiveRead32(current_core, GetInteger(address)));
|
||||
while (true) {
|
||||
current_value =
|
||||
static_cast<s32>(monitor.ExclusiveRead32(current_core, GetInteger(address)));
|
||||
|
||||
// Compare it to the desired one.
|
||||
if (current_value == value) {
|
||||
// If equal, we want to try to write the new value.
|
||||
// Compare it to the desired one.
|
||||
if (current_value == value) {
|
||||
// If equal, we want to try to write the new value.
|
||||
|
||||
// Try to store.
|
||||
if (monitor.ExclusiveWrite32(current_core, GetInteger(address),
|
||||
static_cast<u32>(new_value))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to store.
|
||||
if (!monitor.ExclusiveWrite32(current_core, GetInteger(address),
|
||||
static_cast<u32>(new_value))) {
|
||||
// If we failed to store, try again.
|
||||
UpdateIfEqual(system, out, address, value, new_value);
|
||||
} else {
|
||||
// Otherwise, clear our exclusive hold and finish.
|
||||
monitor.ClearExclusive(current_core);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, clear our exclusive hold and finish.
|
||||
monitor.ClearExclusive(current_core);
|
||||
}
|
||||
|
||||
// We're done.
|
||||
|
||||
@@ -33,21 +33,26 @@ bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
|
||||
// Load the value from the address.
|
||||
const auto expected = monitor.ExclusiveRead32(current_core, GetInteger(address));
|
||||
u32 expected{};
|
||||
|
||||
// Orr in the new mask.
|
||||
u32 value = expected | new_orr_mask;
|
||||
while (true) {
|
||||
// Load the value from the address.
|
||||
expected = monitor.ExclusiveRead32(current_core, GetInteger(address));
|
||||
|
||||
// If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
|
||||
if (!expected) {
|
||||
value = if_zero;
|
||||
}
|
||||
// Orr in the new mask.
|
||||
u32 value = expected | new_orr_mask;
|
||||
|
||||
// If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
|
||||
if (!expected) {
|
||||
value = if_zero;
|
||||
}
|
||||
|
||||
// Try to store.
|
||||
if (monitor.ExclusiveWrite32(current_core, GetInteger(address), value)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to store.
|
||||
if (!monitor.ExclusiveWrite32(current_core, GetInteger(address), value)) {
|
||||
// If we failed to store, try again.
|
||||
return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
|
||||
}
|
||||
|
||||
// We're done.
|
||||
|
||||
@@ -945,7 +945,7 @@ public:
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
|
||||
{10, &ILibraryAppletAccessor::Start, "Start"},
|
||||
{20, nullptr, "RequestExit"},
|
||||
{20, &ILibraryAppletAccessor::RequestExit, "RequestExit"},
|
||||
{25, nullptr, "Terminate"},
|
||||
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
|
||||
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
|
||||
@@ -1010,6 +1010,15 @@ private:
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void RequestExit(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
ASSERT(applet != nullptr);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(applet->RequestExit());
|
||||
}
|
||||
|
||||
void PushInData(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -174,4 +174,9 @@ void Cabinet::Cancel() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result Cabinet::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -89,6 +89,7 @@ public:
|
||||
void Execute() override;
|
||||
void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
|
||||
void Cancel();
|
||||
Result RequestExit() override;
|
||||
|
||||
private:
|
||||
const Core::Frontend::CabinetApplet& frontend;
|
||||
|
||||
@@ -224,7 +224,8 @@ void Controller::Execute() {
|
||||
parameters.allow_dual_joycons, parameters.allow_left_joycon,
|
||||
parameters.allow_right_joycon);
|
||||
|
||||
frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
|
||||
frontend.ReconfigureControllers(
|
||||
[this](bool is_success) { ConfigurationComplete(is_success); }, parameters);
|
||||
break;
|
||||
}
|
||||
case ControllerSupportMode::ShowControllerStrapGuide:
|
||||
@@ -232,16 +233,16 @@ void Controller::Execute() {
|
||||
case ControllerSupportMode::ShowControllerKeyRemappingForSystem:
|
||||
UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
|
||||
controller_private_arg.mode);
|
||||
ConfigurationComplete();
|
||||
ConfigurationComplete(true);
|
||||
break;
|
||||
default: {
|
||||
ConfigurationComplete();
|
||||
ConfigurationComplete(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::ConfigurationComplete() {
|
||||
void Controller::ConfigurationComplete(bool is_success) {
|
||||
ControllerSupportResultInfo result_info{};
|
||||
|
||||
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
|
||||
@@ -250,7 +251,8 @@ void Controller::ConfigurationComplete() {
|
||||
|
||||
result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
|
||||
|
||||
result_info.result = 0;
|
||||
result_info.result =
|
||||
is_success ? ControllerSupportResult::Success : ControllerSupportResult::Cancel;
|
||||
|
||||
LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
|
||||
result_info.player_count, result_info.selected_id, result_info.result);
|
||||
@@ -262,4 +264,9 @@ void Controller::ConfigurationComplete() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result Controller::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -48,6 +48,11 @@ enum class ControllerSupportCaller : u8 {
|
||||
MaxControllerSupportCaller,
|
||||
};
|
||||
|
||||
enum class ControllerSupportResult : u32 {
|
||||
Success = 0,
|
||||
Cancel = 2,
|
||||
};
|
||||
|
||||
struct ControllerSupportArgPrivate {
|
||||
u32 arg_private_size{};
|
||||
u32 arg_size{};
|
||||
@@ -112,7 +117,7 @@ struct ControllerSupportResultInfo {
|
||||
s8 player_count{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u32 selected_id{};
|
||||
u32 result{};
|
||||
ControllerSupportResult result{};
|
||||
};
|
||||
static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
|
||||
"ControllerSupportResultInfo has incorrect size.");
|
||||
@@ -129,8 +134,9 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void ConfigurationComplete();
|
||||
void ConfigurationComplete(bool is_success);
|
||||
|
||||
private:
|
||||
const Core::Frontend::ControllerApplet& frontend;
|
||||
|
||||
@@ -209,4 +209,9 @@ void Error::DisplayCompleted() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result Error::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void DisplayCompleted();
|
||||
|
||||
|
||||
@@ -150,6 +150,11 @@ void Auth::AuthFinished(bool is_successful) {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result Auth::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::PhotoViewerApplet& frontend_)
|
||||
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
|
||||
@@ -202,6 +207,11 @@ void PhotoViewer::ViewFinished() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result PhotoViewer::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_)
|
||||
: Applet{system_, applet_mode_}, id{id_}, system{system_} {}
|
||||
|
||||
@@ -250,4 +260,9 @@ void StubApplet::Execute() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result StubApplet::RequestExit() {
|
||||
// Nothing to do.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void AuthFinished(bool is_successful = true);
|
||||
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void ViewFinished();
|
||||
|
||||
@@ -80,6 +82,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
private:
|
||||
AppletId id;
|
||||
|
||||
@@ -135,4 +135,9 @@ void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result MiiEdit::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void MiiEditOutput(MiiEditResult result, s32 index);
|
||||
|
||||
|
||||
@@ -25,13 +25,29 @@ void ProfileSelect::Initialize() {
|
||||
final_data.clear();
|
||||
|
||||
Applet::Initialize();
|
||||
profile_select_version = ProfileSelectAppletVersion{common_args.library_version};
|
||||
|
||||
const auto user_config_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(user_config_storage != nullptr);
|
||||
const auto& user_config = user_config_storage->GetData();
|
||||
|
||||
ASSERT(user_config.size() >= sizeof(UserSelectionConfig));
|
||||
std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig));
|
||||
LOG_INFO(Service_AM, "Initializing Profile Select Applet with version={}",
|
||||
profile_select_version);
|
||||
|
||||
switch (profile_select_version) {
|
||||
case ProfileSelectAppletVersion::Version1:
|
||||
ASSERT(user_config.size() == sizeof(UiSettingsV1));
|
||||
std::memcpy(&config_old, user_config.data(), sizeof(UiSettingsV1));
|
||||
break;
|
||||
case ProfileSelectAppletVersion::Version2:
|
||||
case ProfileSelectAppletVersion::Version3:
|
||||
ASSERT(user_config.size() == sizeof(UiSettings));
|
||||
std::memcpy(&config, user_config.data(), sizeof(UiSettings));
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileSelect::TransactionComplete() const {
|
||||
@@ -52,11 +68,37 @@ void ProfileSelect::Execute() {
|
||||
return;
|
||||
}
|
||||
|
||||
frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); });
|
||||
Core::Frontend::ProfileSelectParameters parameters{};
|
||||
|
||||
switch (profile_select_version) {
|
||||
case ProfileSelectAppletVersion::Version1:
|
||||
parameters = {
|
||||
.mode = config_old.mode,
|
||||
.invalid_uid_list = config_old.invalid_uid_list,
|
||||
.display_options = config_old.display_options,
|
||||
.purpose = UserSelectionPurpose::General,
|
||||
};
|
||||
break;
|
||||
case ProfileSelectAppletVersion::Version2:
|
||||
case ProfileSelectAppletVersion::Version3:
|
||||
parameters = {
|
||||
.mode = config.mode,
|
||||
.invalid_uid_list = config.invalid_uid_list,
|
||||
.display_options = config.display_options,
|
||||
.purpose = config.purpose,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
|
||||
break;
|
||||
}
|
||||
|
||||
frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); },
|
||||
parameters);
|
||||
}
|
||||
|
||||
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
|
||||
UserSelectionOutput output{};
|
||||
UiReturnArg output{};
|
||||
|
||||
if (uuid.has_value() && uuid->IsValid()) {
|
||||
output.result = 0;
|
||||
@@ -67,10 +109,15 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
|
||||
output.uuid_selected = Common::InvalidUUID;
|
||||
}
|
||||
|
||||
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
|
||||
final_data = std::vector<u8>(sizeof(UiReturnArg));
|
||||
std::memcpy(final_data.data(), &output, final_data.size());
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result ProfileSelect::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -16,19 +16,100 @@ class System;
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
struct UserSelectionConfig {
|
||||
// TODO(DarkLordZach): RE this structure
|
||||
// It seems to be flags and the like that determine the UI of the applet on the switch... from
|
||||
// my research this is safe to ignore for now.
|
||||
INSERT_PADDING_BYTES(0xA0);
|
||||
enum class ProfileSelectAppletVersion : u32 {
|
||||
Version1 = 0x1, // 1.0.0+
|
||||
Version2 = 0x10000, // 2.0.0+
|
||||
Version3 = 0x20000, // 6.0.0+
|
||||
};
|
||||
static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size.");
|
||||
|
||||
struct UserSelectionOutput {
|
||||
// This is nn::account::UiMode
|
||||
enum class UiMode {
|
||||
UserSelector,
|
||||
UserCreator,
|
||||
EnsureNetworkServiceAccountAvailable,
|
||||
UserIconEditor,
|
||||
UserNicknameEditor,
|
||||
UserCreatorForStarter,
|
||||
NintendoAccountAuthorizationRequestContext,
|
||||
IntroduceExternalNetworkServiceAccount,
|
||||
IntroduceExternalNetworkServiceAccountForRegistration,
|
||||
NintendoAccountNnidLinker,
|
||||
LicenseRequirementsForNetworkService,
|
||||
LicenseRequirementsForNetworkServiceWithUserContextImpl,
|
||||
UserCreatorForImmediateNaLoginTest,
|
||||
UserQualificationPromoter,
|
||||
};
|
||||
|
||||
// This is nn::account::UserSelectionPurpose
|
||||
enum class UserSelectionPurpose {
|
||||
General,
|
||||
GameCardRegistration,
|
||||
EShopLaunch,
|
||||
EShopItemShow,
|
||||
PicturePost,
|
||||
NintendoAccountLinkage,
|
||||
SettingsUpdate,
|
||||
SaveDataDeletion,
|
||||
UserMigration,
|
||||
SaveDataTransfer,
|
||||
};
|
||||
|
||||
// This is nn::account::NintendoAccountStartupDialogType
|
||||
enum class NintendoAccountStartupDialogType {
|
||||
LoginAndCreate,
|
||||
Login,
|
||||
Create,
|
||||
};
|
||||
|
||||
// This is nn::account::UserSelectionSettingsForSystemService
|
||||
struct UserSelectionSettingsForSystemService {
|
||||
UserSelectionPurpose purpose;
|
||||
bool enable_user_creation;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
};
|
||||
static_assert(sizeof(UserSelectionSettingsForSystemService) == 0x8,
|
||||
"UserSelectionSettingsForSystemService has incorrect size.");
|
||||
|
||||
struct UiSettingsDisplayOptions {
|
||||
bool is_network_service_account_required;
|
||||
bool is_skip_enabled;
|
||||
bool is_system_or_launcher;
|
||||
bool is_registration_permitted;
|
||||
bool show_skip_button;
|
||||
bool aditional_select;
|
||||
bool show_user_selector;
|
||||
bool is_unqualified_user_selectable;
|
||||
};
|
||||
static_assert(sizeof(UiSettingsDisplayOptions) == 0x8,
|
||||
"UiSettingsDisplayOptions has incorrect size.");
|
||||
|
||||
struct UiSettingsV1 {
|
||||
UiMode mode;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<Common::UUID, 8> invalid_uid_list;
|
||||
u64 application_id;
|
||||
UiSettingsDisplayOptions display_options;
|
||||
};
|
||||
static_assert(sizeof(UiSettingsV1) == 0x98, "UiSettings has incorrect size.");
|
||||
|
||||
// This is nn::account::UiSettings
|
||||
struct UiSettings {
|
||||
UiMode mode;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<Common::UUID, 8> invalid_uid_list;
|
||||
u64 application_id;
|
||||
UiSettingsDisplayOptions display_options;
|
||||
UserSelectionPurpose purpose;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
static_assert(sizeof(UiSettings) == 0xA0, "UiSettings has incorrect size.");
|
||||
|
||||
// This is nn::account::UiReturnArg
|
||||
struct UiReturnArg {
|
||||
u64 result;
|
||||
Common::UUID uuid_selected;
|
||||
};
|
||||
static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");
|
||||
static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size.");
|
||||
|
||||
class ProfileSelect final : public Applet {
|
||||
public:
|
||||
@@ -42,13 +123,17 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void SelectionComplete(std::optional<Common::UUID> uuid);
|
||||
|
||||
private:
|
||||
const Core::Frontend::ProfileSelectApplet& frontend;
|
||||
|
||||
UserSelectionConfig config;
|
||||
UiSettings config;
|
||||
UiSettingsV1 config_old;
|
||||
ProfileSelectAppletVersion profile_select_version;
|
||||
|
||||
bool complete = false;
|
||||
Result status = ResultSuccess;
|
||||
std::vector<u8> final_data;
|
||||
|
||||
@@ -770,6 +770,11 @@ void SoftwareKeyboard::ExitKeyboard() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result SoftwareKeyboard::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// Inline Software Keyboard Requests
|
||||
|
||||
void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
/**
|
||||
* Submits the input text to the application.
|
||||
|
||||
@@ -363,6 +363,11 @@ void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url)
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
Result WebBrowser::RequestExit() {
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const {
|
||||
return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
Result RequestExit() override;
|
||||
|
||||
void ExtractOfflineRomFS();
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ public:
|
||||
virtual Result GetStatus() const = 0;
|
||||
virtual void ExecuteInteractive() = 0;
|
||||
virtual void Execute() = 0;
|
||||
virtual Result RequestExit() = 0;
|
||||
|
||||
AppletDataBroker& GetBroker() {
|
||||
return broker;
|
||||
|
||||
@@ -70,7 +70,6 @@ Result Controller_NPad::VerifyValidSixAxisSensorHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
||||
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
||||
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
||||
|
||||
if (!npad_id) {
|
||||
return InvalidNpadId;
|
||||
@@ -78,10 +77,6 @@ Result Controller_NPad::VerifyValidSixAxisSensorHandle(
|
||||
if (!device_index) {
|
||||
return NpadDeviceIndexOutOfRange;
|
||||
}
|
||||
// This doesn't get validated on nnsdk
|
||||
if (!npad_type) {
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -819,12 +814,12 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
|
||||
return communication_mode;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
|
||||
NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode) {
|
||||
bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
|
||||
NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return InvalidNpadId;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
@@ -833,7 +828,7 @@ Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
|
||||
}
|
||||
|
||||
if (!controller.device->IsConnected()) {
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
|
||||
@@ -842,52 +837,52 @@ Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
|
||||
controller.is_dual_left_connected = true;
|
||||
controller.is_dual_right_connected = false;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
DisconnectNpad(npad_id);
|
||||
controller.is_dual_left_connected = false;
|
||||
controller.is_dual_right_connected = true;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is for NpadJoyAssignmentMode::Single
|
||||
|
||||
// Only JoyconDual get affected by this function
|
||||
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
|
||||
DisconnectNpad(npad_id);
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
|
||||
DisconnectNpad(npad_id);
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
|
||||
return ResultSuccess;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have two controllers connected to the same npad_id we need to split them
|
||||
const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
|
||||
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
|
||||
new_npad_id = hid_core.GetFirstDisconnectedNpadId();
|
||||
auto& controller_2 = GetControllerFromNpadIdType(new_npad_id);
|
||||
DisconnectNpad(npad_id);
|
||||
if (npad_device_type == NpadJoyDeviceType::Left) {
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
|
||||
controller_2.is_dual_left_connected = false;
|
||||
controller_2.is_dual_right_connected = true;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
|
||||
} else {
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
|
||||
controller_2.is_dual_left_connected = true;
|
||||
controller_2.is_dual_right_connected = false;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||
@@ -1131,6 +1126,7 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
WriteEmptyEntry(shared_memory);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetGyroscopeZeroDriftMode(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode drift_mode) {
|
||||
@@ -1388,7 +1384,8 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
return NpadIsDualJoycon;
|
||||
}
|
||||
|
||||
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
|
||||
// Disconnect the joycons and connect them as dual joycon at the first index.
|
||||
DisconnectNpad(npad_id_1);
|
||||
DisconnectNpad(npad_id_2);
|
||||
controller_1.is_dual_left_connected = true;
|
||||
controller_1.is_dual_right_connected = true;
|
||||
|
||||
@@ -102,8 +102,8 @@ public:
|
||||
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
|
||||
NpadCommunicationMode GetNpadCommunicationMode() const;
|
||||
|
||||
Result SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode);
|
||||
bool SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
|
||||
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);
|
||||
|
||||
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
|
||||
const Core::HID::VibrationValue& vibration_value);
|
||||
|
||||
@@ -302,7 +302,7 @@ Hid::Hid(Core::System& system_)
|
||||
{130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
|
||||
{131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
|
||||
{132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
|
||||
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
|
||||
{133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
|
||||
{134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
|
||||
{135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
|
||||
{136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
|
||||
@@ -1180,8 +1180,10 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
Core::HID::NpadIdType new_npad_id{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
|
||||
controller.SetNpadMode(new_npad_id, parameters.npad_id,
|
||||
Controller_NPad::NpadJoyDeviceType::Left,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||
@@ -1203,8 +1205,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
Core::HID::NpadIdType new_npad_id{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
|
||||
controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||
@@ -1226,8 +1229,10 @@ void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
Core::HID::NpadIdType new_npad_id{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
|
||||
controller.SetNpadMode(new_npad_id, parameters.npad_id, {},
|
||||
Controller_NPad::NpadJoyAssignmentMode::Dual);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
@@ -1369,6 +1374,34 @@ void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Core::HID::NpadIdType npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
Core::HID::NpadIdType new_npad_id{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto is_reassigned =
|
||||
controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||
parameters.npad_id, parameters.applet_resource_user_id,
|
||||
parameters.npad_joy_device_type);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(is_reassigned);
|
||||
rb.PushEnum(new_npad_id);
|
||||
}
|
||||
|
||||
void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
|
||||
@@ -151,6 +151,7 @@ private:
|
||||
void SwapNpadAssignment(HLERequestContext& ctx);
|
||||
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
|
||||
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
|
||||
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
|
||||
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
||||
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
||||
|
||||
@@ -4,11 +4,138 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
#include "core/hle/service/nfp/nfp_interface.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
|
||||
class IUser final : public Interface {
|
||||
public:
|
||||
explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::Initialize, "Initialize"},
|
||||
{1, &IUser::Finalize, "Finalize"},
|
||||
{2, &IUser::ListDevices, "ListDevices"},
|
||||
{3, &IUser::StartDetection, "StartDetection"},
|
||||
{4, &IUser::StopDetection, "StopDetection"},
|
||||
{5, &IUser::Mount, "Mount"},
|
||||
{6, &IUser::Unmount, "Unmount"},
|
||||
{7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
|
||||
{8, &IUser::GetApplicationArea, "GetApplicationArea"},
|
||||
{9, &IUser::SetApplicationArea, "SetApplicationArea"},
|
||||
{10, &IUser::Flush, "Flush"},
|
||||
{11, &IUser::Restore, "Restore"},
|
||||
{12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
|
||||
{13, &IUser::GetTagInfo, "GetTagInfo"},
|
||||
{14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
|
||||
{15, &IUser::GetCommonInfo, "GetCommonInfo"},
|
||||
{16, &IUser::GetModelInfo, "GetModelInfo"},
|
||||
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
||||
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||
{19, &IUser::GetState, "GetState"},
|
||||
{20, &IUser::GetDeviceState, "GetDeviceState"},
|
||||
{21, &IUser::GetNpadId, "GetNpadId"},
|
||||
{22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
|
||||
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||
{24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class ISystem final : public Interface {
|
||||
public:
|
||||
explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystem::InitializeSystem, "InitializeSystem"},
|
||||
{1, &ISystem::FinalizeSystem, "FinalizeSystem"},
|
||||
{2, &ISystem::ListDevices, "ListDevices"},
|
||||
{3, &ISystem::StartDetection, "StartDetection"},
|
||||
{4, &ISystem::StopDetection, "StopDetection"},
|
||||
{5, &ISystem::Mount, "Mount"},
|
||||
{6, &ISystem::Unmount, "Unmount"},
|
||||
{10, &ISystem::Flush, "Flush"},
|
||||
{11, &ISystem::Restore, "Restore"},
|
||||
{12, &ISystem::CreateApplicationArea, "CreateApplicationArea"},
|
||||
{13, &ISystem::GetTagInfo, "GetTagInfo"},
|
||||
{14, &ISystem::GetRegisterInfo, "GetRegisterInfo"},
|
||||
{15, &ISystem::GetCommonInfo, "GetCommonInfo"},
|
||||
{16, &ISystem::GetModelInfo, "GetModelInfo"},
|
||||
{17, &ISystem::AttachActivateEvent, "AttachActivateEvent"},
|
||||
{18, &ISystem::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||
{19, &ISystem::GetState, "GetState"},
|
||||
{20, &ISystem::GetDeviceState, "GetDeviceState"},
|
||||
{21, &ISystem::GetNpadId, "GetNpadId"},
|
||||
{23, &ISystem::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||
{100, &ISystem::Format, "Format"},
|
||||
{101, &ISystem::GetAdminInfo, "GetAdminInfo"},
|
||||
{102, &ISystem::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
|
||||
{103, &ISystem::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
|
||||
{104, &ISystem::DeleteRegisterInfo, "DeleteRegisterInfo"},
|
||||
{105, &ISystem::DeleteApplicationArea, "DeleteApplicationArea"},
|
||||
{106, &ISystem::ExistsApplicationArea, "ExistsApplicationArea"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IDebug final : public Interface {
|
||||
public:
|
||||
explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDebug::InitializeDebug, "InitializeDebug"},
|
||||
{1, &IDebug::FinalizeDebug, "FinalizeDebug"},
|
||||
{2, &IDebug::ListDevices, "ListDevices"},
|
||||
{3, &IDebug::StartDetection, "StartDetection"},
|
||||
{4, &IDebug::StopDetection, "StopDetection"},
|
||||
{5, &IDebug::Mount, "Mount"},
|
||||
{6, &IDebug::Unmount, "Unmount"},
|
||||
{7, &IDebug::OpenApplicationArea, "OpenApplicationArea"},
|
||||
{8, &IDebug::GetApplicationArea, "GetApplicationArea"},
|
||||
{9, &IDebug::SetApplicationArea, "SetApplicationArea"},
|
||||
{10, &IDebug::Flush, "Flush"},
|
||||
{11, &IDebug::Restore, "Restore"},
|
||||
{12, &IDebug::CreateApplicationArea, "CreateApplicationArea"},
|
||||
{13, &IDebug::GetTagInfo, "GetTagInfo"},
|
||||
{14, &IDebug::GetRegisterInfo, "GetRegisterInfo"},
|
||||
{15, &IDebug::GetCommonInfo, "GetCommonInfo"},
|
||||
{16, &IDebug::GetModelInfo, "GetModelInfo"},
|
||||
{17, &IDebug::AttachActivateEvent, "AttachActivateEvent"},
|
||||
{18, &IDebug::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||
{19, &IDebug::GetState, "GetState"},
|
||||
{20, &IDebug::GetDeviceState, "GetDeviceState"},
|
||||
{21, &IDebug::GetNpadId, "GetNpadId"},
|
||||
{22, &IDebug::GetApplicationAreaSize, "GetApplicationAreaSize"},
|
||||
{23, &IDebug::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||
{24, &IDebug::RecreateApplicationArea, "RecreateApplicationArea"},
|
||||
{100, &IDebug::Format, "Format"},
|
||||
{101, &IDebug::GetAdminInfo, "GetAdminInfo"},
|
||||
{102, &IDebug::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
|
||||
{103, &IDebug::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
|
||||
{104, &IDebug::DeleteRegisterInfo, "DeleteRegisterInfo"},
|
||||
{105, &IDebug::DeleteApplicationArea, "DeleteApplicationArea"},
|
||||
{106, &IDebug::ExistsApplicationArea, "ExistsApplicationArea"},
|
||||
{200, &IDebug::GetAll, "GetAll"},
|
||||
{201, &IDebug::SetAll, "SetAll"},
|
||||
{202, &IDebug::FlushDebug, "FlushDebug"},
|
||||
{203, &IDebug::BreakTag, "BreakTag"},
|
||||
{204, nullptr, "ReadBackupData"},
|
||||
{205, nullptr, "WriteBackupData"},
|
||||
{206, nullptr, "WriteNtf"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IUserManager final : public ServiceFramework<IUserManager> {
|
||||
public:
|
||||
explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
|
||||
@@ -37,10 +164,68 @@ private:
|
||||
std::shared_ptr<IUser> user_interface;
|
||||
};
|
||||
|
||||
class ISystemManager final : public ServiceFramework<ISystemManager> {
|
||||
public:
|
||||
explicit ISystemManager(Core::System& system_) : ServiceFramework{system_, "nfp:sys"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemManager::CreateSystemInterface, "CreateSystemInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateSystemInterface(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
if (system_interface == nullptr) {
|
||||
system_interface = std::make_shared<ISystem>(system);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system_interface);
|
||||
}
|
||||
|
||||
std::shared_ptr<ISystem> system_interface;
|
||||
};
|
||||
|
||||
class IDebugManager final : public ServiceFramework<IDebugManager> {
|
||||
public:
|
||||
explicit IDebugManager(Core::System& system_) : ServiceFramework{system_, "nfp:dbg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDebugManager::CreateDebugInterface, "CreateDebugInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateDebugInterface(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
if (system_interface == nullptr) {
|
||||
system_interface = std::make_shared<IDebug>(system);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDebug>(system_interface);
|
||||
}
|
||||
|
||||
std::shared_ptr<IDebug> system_interface;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("nfp:user", std::make_shared<IUserManager>(system));
|
||||
server_manager->RegisterNamedService("nfp:sys", std::make_shared<ISystemManager>(system));
|
||||
server_manager->RegisterNamedService("nfp:dbg", std::make_shared<IDebugManager>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "core/hle/service/nfp/amiibo_crypto.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"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
#include "core/hle/service/time/time_zone_types.h"
|
||||
@@ -241,6 +240,42 @@ Result NfpDevice::Flush() {
|
||||
|
||||
tag_data.write_counter++;
|
||||
|
||||
FlushWithBreak(BreakType::Normal);
|
||||
|
||||
is_data_moddified = false;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::FlushDebug() {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "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_NFC, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
tag_data.write_counter++;
|
||||
|
||||
FlushWithBreak(BreakType::Normal);
|
||||
|
||||
is_data_moddified = false;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::FlushWithBreak(BreakType break_type) {
|
||||
if (break_type != BreakType::Normal) {
|
||||
LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
std::vector<u8> data(sizeof(EncryptedNTAG215File));
|
||||
if (is_plain_amiibo) {
|
||||
memcpy(data.data(), &tag_data, sizeof(tag_data));
|
||||
@@ -258,8 +293,6 @@ Result NfpDevice::Flush() {
|
||||
return WriteAmiiboFailed;
|
||||
}
|
||||
|
||||
is_data_moddified = false;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -417,6 +450,38 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const {
|
||||
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.amiibo_initialized == 0) {
|
||||
return RegistrationIsNotInitialized;
|
||||
}
|
||||
|
||||
Service::Mii::MiiManager manager;
|
||||
const auto& settings = tag_data.settings;
|
||||
|
||||
// TODO: Validate and complete this data
|
||||
register_info = {
|
||||
.mii_store_data = {},
|
||||
.creation_date = settings.init_date.GetWriteDate(),
|
||||
.amiibo_name = GetAmiiboName(settings),
|
||||
.font_region = settings.settings.font_region,
|
||||
};
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||
@@ -807,6 +872,159 @@ Result NfpDevice::DeleteApplicationArea() {
|
||||
return Flush();
|
||||
}
|
||||
|
||||
Result NfpDevice::ExistApplicationArea(bool& has_application_area) {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "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_NFC, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetAll(NfpData& data) const {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "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_NFC, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
CommonInfo common_info{};
|
||||
Service::Mii::MiiManager manager;
|
||||
const u64 application_id = tag_data.application_id;
|
||||
|
||||
GetCommonInfo(common_info);
|
||||
|
||||
data = {
|
||||
.magic = tag_data.constant_value,
|
||||
.write_counter = tag_data.write_counter,
|
||||
.settings_crc = tag_data.settings.crc,
|
||||
.common_info = common_info,
|
||||
.mii_char_info = tag_data.owner_mii,
|
||||
.mii_store_data_extension = tag_data.mii_extension,
|
||||
.creation_date = tag_data.settings.init_date.GetWriteDate(),
|
||||
.amiibo_name = tag_data.settings.amiibo_name,
|
||||
.amiibo_name_null_terminated = 0,
|
||||
.settings = tag_data.settings.settings,
|
||||
.unknown1 = tag_data.unknown,
|
||||
.register_info_crc = tag_data.register_info_crc,
|
||||
.unknown2 = tag_data.unknown2,
|
||||
.application_id = application_id,
|
||||
.access_id = tag_data.application_area_id,
|
||||
.settings_crc_counter = tag_data.settings.crc_counter,
|
||||
.font_region = tag_data.settings.settings.font_region,
|
||||
.tag_type = PackedTagType::Type2,
|
||||
.console_type =
|
||||
static_cast<AppAreaVersion>(application_id >> application_id_version_offset & 0xf),
|
||||
.application_id_byte = tag_data.application_id_byte,
|
||||
.application_area = tag_data.application_area,
|
||||
};
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::SetAll(const NfpData& data) {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "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_NFC, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
tag_data.constant_value = data.magic;
|
||||
tag_data.write_counter = data.write_counter;
|
||||
tag_data.settings.crc = data.settings_crc;
|
||||
tag_data.settings.write_date.SetWriteDate(data.common_info.last_write_date);
|
||||
tag_data.write_counter = data.common_info.write_counter;
|
||||
tag_data.amiibo_version = data.common_info.version;
|
||||
tag_data.owner_mii = data.mii_char_info;
|
||||
tag_data.mii_extension = data.mii_store_data_extension;
|
||||
tag_data.settings.init_date.SetWriteDate(data.creation_date);
|
||||
tag_data.settings.amiibo_name = data.amiibo_name;
|
||||
tag_data.settings.settings = data.settings;
|
||||
tag_data.unknown = data.unknown1;
|
||||
tag_data.register_info_crc = data.register_info_crc;
|
||||
tag_data.unknown2 = data.unknown2;
|
||||
tag_data.application_id = data.application_id;
|
||||
tag_data.application_area_id = data.access_id;
|
||||
tag_data.settings.crc_counter = data.settings_crc_counter;
|
||||
tag_data.settings.settings.font_region.Assign(data.font_region);
|
||||
tag_data.application_id_byte = data.application_id_byte;
|
||||
tag_data.application_area = data.application_area;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::BreakTag(BreakType break_type) {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "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_NFC, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
// TODO: Complete this implementation
|
||||
|
||||
return FlushWithBreak(break_type);
|
||||
}
|
||||
|
||||
Result NfpDevice::ReadBackupData() {
|
||||
// Not implemented
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::WriteBackupData() {
|
||||
// Not implemented
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::WriteNtf() {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "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_NFC, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
u64 NfpDevice::GetHandle() const {
|
||||
// Generate a handle based of the npad id
|
||||
return static_cast<u64>(npad_id);
|
||||
|
||||
@@ -41,12 +41,16 @@ public:
|
||||
Result StopDetection();
|
||||
Result Mount(MountTarget mount_target);
|
||||
Result Unmount();
|
||||
|
||||
Result Flush();
|
||||
Result FlushDebug();
|
||||
Result FlushWithBreak(BreakType break_type);
|
||||
|
||||
Result GetTagInfo(TagInfo& tag_info) const;
|
||||
Result GetCommonInfo(CommonInfo& common_info) const;
|
||||
Result GetModelInfo(ModelInfo& model_info) const;
|
||||
Result GetRegisterInfo(RegisterInfo& register_info) const;
|
||||
Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
|
||||
Result GetAdminInfo(AdminInfo& admin_info) const;
|
||||
|
||||
Result DeleteRegisterInfo();
|
||||
@@ -61,6 +65,14 @@ public:
|
||||
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
|
||||
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
|
||||
Result DeleteApplicationArea();
|
||||
Result ExistApplicationArea(bool& has_application_area);
|
||||
|
||||
Result GetAll(NfpData& data) const;
|
||||
Result SetAll(const NfpData& data);
|
||||
Result BreakTag(BreakType break_type);
|
||||
Result ReadBackupData();
|
||||
Result WriteBackupData();
|
||||
Result WriteNtf();
|
||||
|
||||
u64 GetHandle() const;
|
||||
u32 GetApplicationAreaSize() const;
|
||||
|
||||
+436
-59
@@ -7,42 +7,13 @@
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp_device.h"
|
||||
#include "core/hle/service/nfp/nfp_interface.h"
|
||||
#include "core/hle/service/nfp/nfp_result.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
|
||||
IUser::IUser(Core::System& system_)
|
||||
: ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::Initialize, "Initialize"},
|
||||
{1, &IUser::Finalize, "Finalize"},
|
||||
{2, &IUser::ListDevices, "ListDevices"},
|
||||
{3, &IUser::StartDetection, "StartDetection"},
|
||||
{4, &IUser::StopDetection, "StopDetection"},
|
||||
{5, &IUser::Mount, "Mount"},
|
||||
{6, &IUser::Unmount, "Unmount"},
|
||||
{7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
|
||||
{8, &IUser::GetApplicationArea, "GetApplicationArea"},
|
||||
{9, &IUser::SetApplicationArea, "SetApplicationArea"},
|
||||
{10, &IUser::Flush, "Flush"},
|
||||
{11, &IUser::Restore, "Restore"},
|
||||
{12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
|
||||
{13, &IUser::GetTagInfo, "GetTagInfo"},
|
||||
{14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
|
||||
{15, &IUser::GetCommonInfo, "GetCommonInfo"},
|
||||
{16, &IUser::GetModelInfo, "GetModelInfo"},
|
||||
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
||||
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||
{19, &IUser::GetState, "GetState"},
|
||||
{20, &IUser::GetDeviceState, "GetDeviceState"},
|
||||
{21, &IUser::GetNpadId, "GetNpadId"},
|
||||
{22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
|
||||
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||
{24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
Interface::Interface(Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name}, service_context{system_, service_name} {
|
||||
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
|
||||
|
||||
for (u32 device_index = 0; device_index < 10; device_index++) {
|
||||
@@ -52,11 +23,11 @@ IUser::IUser(Core::System& system_)
|
||||
}
|
||||
}
|
||||
|
||||
IUser ::~IUser() {
|
||||
Interface::~Interface() {
|
||||
availability_change_event->Close();
|
||||
}
|
||||
|
||||
void IUser::Initialize(HLERequestContext& ctx) {
|
||||
void Interface::Initialize(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::Initialized;
|
||||
@@ -69,7 +40,33 @@ void IUser::Initialize(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IUser::Finalize(HLERequestContext& ctx) {
|
||||
void Interface::InitializeSystem(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::Initialized;
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->Initialize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Interface::InitializeDebug(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::Initialized;
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->Initialize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Interface::Finalize(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::NonInitialized;
|
||||
@@ -82,7 +79,33 @@ void IUser::Finalize(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IUser::ListDevices(HLERequestContext& ctx) {
|
||||
void Interface::FinalizeSystem(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::NonInitialized;
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->Finalize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Interface::FinalizeDebug(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::NonInitialized;
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->Finalize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Interface::ListDevices(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
@@ -128,7 +151,7 @@ void IUser::ListDevices(HLERequestContext& ctx) {
|
||||
rb.Push(static_cast<s32>(nfp_devices.size()));
|
||||
}
|
||||
|
||||
void IUser::StartDetection(HLERequestContext& ctx) {
|
||||
void Interface::StartDetection(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
|
||||
@@ -153,7 +176,7 @@ void IUser::StartDetection(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::StopDetection(HLERequestContext& ctx) {
|
||||
void Interface::StopDetection(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -177,7 +200,7 @@ void IUser::StopDetection(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::Mount(HLERequestContext& ctx) {
|
||||
void Interface::Mount(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto model_type{rp.PopEnum<ModelType>()};
|
||||
@@ -204,7 +227,7 @@ void IUser::Mount(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::Unmount(HLERequestContext& ctx) {
|
||||
void Interface::Unmount(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -228,7 +251,7 @@ void IUser::Unmount(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::OpenApplicationArea(HLERequestContext& ctx) {
|
||||
void Interface::OpenApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto access_id{rp.Pop<u32>()};
|
||||
@@ -253,7 +276,7 @@ void IUser::OpenApplicationArea(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::GetApplicationArea(HLERequestContext& ctx) {
|
||||
void Interface::GetApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto data_size = ctx.GetWriteBufferSize();
|
||||
@@ -287,7 +310,7 @@ void IUser::GetApplicationArea(HLERequestContext& ctx) {
|
||||
rb.Push(static_cast<u32>(data_size));
|
||||
}
|
||||
|
||||
void IUser::SetApplicationArea(HLERequestContext& ctx) {
|
||||
void Interface::SetApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto data{ctx.ReadBuffer()};
|
||||
@@ -318,7 +341,7 @@ void IUser::SetApplicationArea(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::Flush(HLERequestContext& ctx) {
|
||||
void Interface::Flush(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -342,7 +365,7 @@ void IUser::Flush(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::Restore(HLERequestContext& ctx) {
|
||||
void Interface::Restore(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
|
||||
@@ -366,7 +389,7 @@ void IUser::Restore(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::CreateApplicationArea(HLERequestContext& ctx) {
|
||||
void Interface::CreateApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto access_id{rp.Pop<u32>()};
|
||||
@@ -399,7 +422,7 @@ void IUser::CreateApplicationArea(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::GetTagInfo(HLERequestContext& ctx) {
|
||||
void Interface::GetTagInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -425,7 +448,7 @@ void IUser::GetTagInfo(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::GetRegisterInfo(HLERequestContext& ctx) {
|
||||
void Interface::GetRegisterInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -451,7 +474,7 @@ void IUser::GetRegisterInfo(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::GetCommonInfo(HLERequestContext& ctx) {
|
||||
void Interface::GetCommonInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -477,7 +500,7 @@ void IUser::GetCommonInfo(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::GetModelInfo(HLERequestContext& ctx) {
|
||||
void Interface::GetModelInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -503,7 +526,7 @@ void IUser::GetModelInfo(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
void Interface::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -527,7 +550,7 @@ void IUser::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
rb.PushCopyObjects(device.value()->GetActivateEvent());
|
||||
}
|
||||
|
||||
void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
void Interface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -551,7 +574,7 @@ void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
|
||||
}
|
||||
|
||||
void IUser::GetState(HLERequestContext& ctx) {
|
||||
void Interface::GetState(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -559,7 +582,7 @@ void IUser::GetState(HLERequestContext& ctx) {
|
||||
rb.PushEnum(state);
|
||||
}
|
||||
|
||||
void IUser::GetDeviceState(HLERequestContext& ctx) {
|
||||
void Interface::GetDeviceState(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -577,7 +600,7 @@ void IUser::GetDeviceState(HLERequestContext& ctx) {
|
||||
rb.PushEnum(device.value()->GetCurrentState());
|
||||
}
|
||||
|
||||
void IUser::GetNpadId(HLERequestContext& ctx) {
|
||||
void Interface::GetNpadId(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -601,7 +624,7 @@ void IUser::GetNpadId(HLERequestContext& ctx) {
|
||||
rb.PushEnum(device.value()->GetNpadId());
|
||||
}
|
||||
|
||||
void IUser::GetApplicationAreaSize(HLERequestContext& ctx) {
|
||||
void Interface::GetApplicationAreaSize(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
@@ -619,7 +642,7 @@ void IUser::GetApplicationAreaSize(HLERequestContext& ctx) {
|
||||
rb.Push(device.value()->GetApplicationAreaSize());
|
||||
}
|
||||
|
||||
void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
void Interface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
@@ -633,7 +656,7 @@ void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IUser::RecreateApplicationArea(HLERequestContext& ctx) {
|
||||
void Interface::RecreateApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto access_id{rp.Pop<u32>()};
|
||||
@@ -660,7 +683,361 @@ void IUser::RecreateApplicationArea(HLERequestContext& ctx) {
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) {
|
||||
void Interface::Format(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->Format();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::GetAdminInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
AdminInfo admin_info{};
|
||||
const auto result = device.value()->GetAdminInfo(admin_info);
|
||||
ctx.WriteBuffer(admin_info);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterInfoPrivate register_info{};
|
||||
const auto result = device.value()->GetRegisterInfoPrivate(register_info);
|
||||
ctx.WriteBuffer(register_info);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto buffer{ctx.ReadBuffer()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle,
|
||||
buffer.size());
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->SetRegisterInfoPrivate({});
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::DeleteRegisterInfo(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->DeleteRegisterInfo();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::DeleteApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->DeleteApplicationArea();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::ExistsApplicationArea(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
bool has_application_area = false;
|
||||
const auto result = device.value()->ExistApplicationArea(has_application_area);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(result);
|
||||
rb.Push(has_application_area);
|
||||
}
|
||||
|
||||
void Interface::GetAll(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
NfpData data{};
|
||||
const auto result = device.value()->GetAll(data);
|
||||
|
||||
ctx.WriteBuffer(data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::SetAll(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto nfp_data{ctx.ReadBuffer()};
|
||||
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
NfpData data{};
|
||||
memcpy(&data, nfp_data.data(), sizeof(NfpData));
|
||||
|
||||
const auto result = device.value()->SetAll(data);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::FlushDebug(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->FlushDebug();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::BreakTag(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto break_type{rp.PopEnum<BreakType>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}, break_type={}", device_handle, break_type);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->BreakTag(break_type);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::ReadBackupData(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->ReadBackupData();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::WriteBackupData(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->WriteBackupData();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Interface::WriteNtf(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfpDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->WriteNtf();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<NfpDevice>> Interface::GetNfpDevice(u64 handle) {
|
||||
for (auto& device : devices) {
|
||||
if (device->GetHandle() == handle) {
|
||||
return device;
|
||||
@@ -13,19 +13,17 @@
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_);
|
||||
~IUser();
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
NonInitialized,
|
||||
Initialized,
|
||||
};
|
||||
explicit Interface(Core::System& system_, const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void Initialize(HLERequestContext& ctx);
|
||||
void InitializeSystem(HLERequestContext& ctx);
|
||||
void InitializeDebug(HLERequestContext& ctx);
|
||||
void Finalize(HLERequestContext& ctx);
|
||||
void FinalizeSystem(HLERequestContext& ctx);
|
||||
void FinalizeDebug(HLERequestContext& ctx);
|
||||
void ListDevices(HLERequestContext& ctx);
|
||||
void StartDetection(HLERequestContext& ctx);
|
||||
void StopDetection(HLERequestContext& ctx);
|
||||
@@ -49,6 +47,26 @@ private:
|
||||
void GetApplicationAreaSize(HLERequestContext& ctx);
|
||||
void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
|
||||
void RecreateApplicationArea(HLERequestContext& ctx);
|
||||
void Format(HLERequestContext& ctx);
|
||||
void GetAdminInfo(HLERequestContext& ctx);
|
||||
void GetRegisterInfoPrivate(HLERequestContext& ctx);
|
||||
void SetRegisterInfoPrivate(HLERequestContext& ctx);
|
||||
void DeleteRegisterInfo(HLERequestContext& ctx);
|
||||
void DeleteApplicationArea(HLERequestContext& ctx);
|
||||
void ExistsApplicationArea(HLERequestContext& ctx);
|
||||
void GetAll(HLERequestContext& ctx);
|
||||
void SetAll(HLERequestContext& ctx);
|
||||
void FlushDebug(HLERequestContext& ctx);
|
||||
void BreakTag(HLERequestContext& ctx);
|
||||
void ReadBackupData(HLERequestContext& ctx);
|
||||
void WriteBackupData(HLERequestContext& ctx);
|
||||
void WriteNtf(HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
NonInitialized,
|
||||
Initialized,
|
||||
};
|
||||
|
||||
std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
|
||||
|
||||
@@ -109,6 +109,12 @@ enum class AppAreaVersion : u8 {
|
||||
NotSet = 0xFF,
|
||||
};
|
||||
|
||||
enum class BreakType : u32 {
|
||||
Normal,
|
||||
Unknown1,
|
||||
Unknown2,
|
||||
};
|
||||
|
||||
enum class CabinetMode : u8 {
|
||||
StartNicknameAndOwnerSettings,
|
||||
StartGameDataEraser,
|
||||
@@ -181,6 +187,12 @@ struct AmiiboDate {
|
||||
};
|
||||
}
|
||||
|
||||
void SetWriteDate(const WriteDate& write_date) {
|
||||
SetYear(write_date.year);
|
||||
SetMonth(write_date.month);
|
||||
SetDay(write_date.day);
|
||||
}
|
||||
|
||||
void SetYear(u16 year) {
|
||||
const u16 year_converted = static_cast<u16>((year - 2000) << 9);
|
||||
raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
|
||||
@@ -354,6 +366,15 @@ struct RegisterInfo {
|
||||
};
|
||||
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
|
||||
|
||||
struct RegisterInfoPrivate {
|
||||
Service::Mii::MiiStoreData mii_store_data;
|
||||
WriteDate creation_date;
|
||||
AmiiboName amiibo_name;
|
||||
u8 font_region;
|
||||
INSERT_PADDING_BYTES(0x8E);
|
||||
};
|
||||
static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
|
||||
|
||||
struct AdminInfo {
|
||||
u64 application_id;
|
||||
u32 application_area_id;
|
||||
@@ -366,6 +387,39 @@ struct AdminInfo {
|
||||
};
|
||||
static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size");
|
||||
|
||||
#pragma pack(1)
|
||||
// This is nn::nfp::NfpData
|
||||
struct NfpData {
|
||||
u8 magic;
|
||||
INSERT_PADDING_BYTES(0x1);
|
||||
u8 write_counter;
|
||||
INSERT_PADDING_BYTES(0x1);
|
||||
u32 settings_crc;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
CommonInfo common_info;
|
||||
Service::Mii::Ver3StoreData mii_char_info;
|
||||
Service::Mii::NfpStoreDataExtension mii_store_data_extension;
|
||||
WriteDate creation_date;
|
||||
std::array<u16_be, amiibo_name_length> amiibo_name;
|
||||
u16 amiibo_name_null_terminated;
|
||||
Settings settings;
|
||||
u8 unknown1;
|
||||
u32 register_info_crc;
|
||||
std::array<u32, 5> unknown2;
|
||||
INSERT_PADDING_BYTES(0x64);
|
||||
u64 application_id;
|
||||
u32 access_id;
|
||||
u16 settings_crc_counter;
|
||||
u8 font_region;
|
||||
PackedTagType tag_type;
|
||||
AppAreaVersion console_type;
|
||||
u8 application_id_byte;
|
||||
INSERT_PADDING_BYTES(0x2E);
|
||||
ApplicationArea application_area;
|
||||
};
|
||||
static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
|
||||
#pragma pack()
|
||||
|
||||
struct SectorKey {
|
||||
MifareCmd command;
|
||||
u8 unknown; // Usually 1
|
||||
|
||||
@@ -16,9 +16,6 @@ namespace Network {
|
||||
|
||||
class ProxySocket : public SocketBase {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(ProxySocket);
|
||||
YUZU_NON_MOVEABLE(ProxySocket);
|
||||
|
||||
explicit ProxySocket(RoomNetwork& room_network_) noexcept;
|
||||
~ProxySocket() override;
|
||||
|
||||
|
||||
@@ -36,13 +36,10 @@ public:
|
||||
|
||||
SocketBase() = default;
|
||||
explicit SocketBase(SOCKET fd_) : fd{fd_} {}
|
||||
|
||||
virtual ~SocketBase() = default;
|
||||
|
||||
virtual SocketBase& operator=(const SocketBase&) = delete;
|
||||
|
||||
// Avoid closing sockets implicitly
|
||||
virtual SocketBase& operator=(SocketBase&&) noexcept = delete;
|
||||
YUZU_NON_COPYABLE(SocketBase);
|
||||
YUZU_NON_MOVEABLE(SocketBase);
|
||||
|
||||
virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
|
||||
|
||||
@@ -109,14 +106,8 @@ public:
|
||||
|
||||
~Socket() override;
|
||||
|
||||
Socket(const Socket&) = delete;
|
||||
Socket& operator=(const Socket&) = delete;
|
||||
|
||||
Socket(Socket&& rhs) noexcept;
|
||||
|
||||
// Avoid closing sockets implicitly
|
||||
Socket& operator=(Socket&&) noexcept = delete;
|
||||
|
||||
Errno Initialize(Domain domain, Type type, Protocol protocol) override;
|
||||
|
||||
Errno Close() override;
|
||||
|
||||
@@ -21,9 +21,9 @@ TEST_CASE("Range Map: Setup", "[video_core]") {
|
||||
my_map.Map(4000, 4500, MappedEnum::Valid_2);
|
||||
my_map.Map(4200, 4400, MappedEnum::Valid_2);
|
||||
my_map.Map(4200, 4400, MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(4200) == 200);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(3000) == 200);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(2900) == 0);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(4200) == 200);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(3000) == 200);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(2900) == 0);
|
||||
|
||||
REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid);
|
||||
REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1);
|
||||
@@ -38,20 +38,20 @@ TEST_CASE("Range Map: Setup", "[video_core]") {
|
||||
|
||||
my_map.Unmap(0, 6000);
|
||||
for (u64 address = 0; address < 10000; address += 1000) {
|
||||
REQUIRE(my_map.GetContinousSizeFrom(address) == 0);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(address) == 0);
|
||||
}
|
||||
|
||||
my_map.Map(1000, 3000, MappedEnum::Valid_1);
|
||||
my_map.Map(4000, 5000, MappedEnum::Valid_1);
|
||||
my_map.Map(2500, 4100, MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(1000) == 4000);
|
||||
|
||||
my_map.Map(1000, 3000, MappedEnum::Valid_1);
|
||||
my_map.Map(4000, 5000, MappedEnum::Valid_2);
|
||||
my_map.Map(2500, 4100, MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(4100) == 900);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(1000) == 1500);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(2500) == 1600);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(4100) == 900);
|
||||
REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid);
|
||||
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3);
|
||||
@@ -59,8 +59,8 @@ TEST_CASE("Range Map: Setup", "[video_core]") {
|
||||
REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid);
|
||||
|
||||
my_map.Map(2000, 6000, MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(1000) == 1000);
|
||||
REQUIRE(my_map.GetContinuousSizeFrom(3000) == 3000);
|
||||
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1);
|
||||
|
||||
@@ -1442,7 +1442,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
||||
}
|
||||
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
|
||||
address_size =
|
||||
static_cast<u32>(gpu_memory->MaxContinousRange(gpu_addr_begin, address_size));
|
||||
static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size));
|
||||
}
|
||||
const u32 size = address_size; // TODO: Analyze stride and number of vertices
|
||||
vertex_buffers[index] = Binding{
|
||||
|
||||
@@ -856,8 +856,8 @@ public:
|
||||
|
||||
struct ZetaSize {
|
||||
enum class DimensionControl : u32 {
|
||||
DepthDefinesArray = 0,
|
||||
ArraySizeOne = 1,
|
||||
DefineArraySize = 0,
|
||||
ArraySizeIsOne = 1,
|
||||
};
|
||||
|
||||
u32 width;
|
||||
@@ -1104,8 +1104,8 @@ public:
|
||||
|
||||
struct TileMode {
|
||||
enum class DimensionControl : u32 {
|
||||
DepthDefinesArray = 0,
|
||||
DepthDefinesDepth = 1,
|
||||
DefineArraySize = 0,
|
||||
DefineDepthSize = 1,
|
||||
};
|
||||
union {
|
||||
BitField<0, 4, u32> block_width;
|
||||
|
||||
@@ -43,7 +43,7 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
|
||||
|
||||
big_entries.resize(big_page_table_size / 32, 0);
|
||||
big_page_table_cpu.resize(big_page_table_size);
|
||||
big_page_continous.resize(big_page_table_size / continous_bits, 0);
|
||||
big_page_continuous.resize(big_page_table_size / continuous_bits, 0);
|
||||
entries.resize(page_table_size / 32, 0);
|
||||
}
|
||||
|
||||
@@ -85,17 +85,17 @@ PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
|
||||
return kind_map.GetValueAt(gpu_addr);
|
||||
}
|
||||
|
||||
inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const {
|
||||
const u64 entry_mask = big_page_continous[big_page_index / continous_bits];
|
||||
const size_t sub_index = big_page_index % continous_bits;
|
||||
inline bool MemoryManager::IsBigPageContinuous(size_t big_page_index) const {
|
||||
const u64 entry_mask = big_page_continuous[big_page_index / continuous_bits];
|
||||
const size_t sub_index = big_page_index % continuous_bits;
|
||||
return ((entry_mask >> sub_index) & 0x1ULL) != 0;
|
||||
}
|
||||
|
||||
inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value) {
|
||||
const u64 continous_mask = big_page_continous[big_page_index / continous_bits];
|
||||
const size_t sub_index = big_page_index % continous_bits;
|
||||
big_page_continous[big_page_index / continous_bits] =
|
||||
(~(1ULL << sub_index) & continous_mask) | (value ? 1ULL << sub_index : 0);
|
||||
inline void MemoryManager::SetBigPageContinuous(size_t big_page_index, bool value) {
|
||||
const u64 continuous_mask = big_page_continuous[big_page_index / continuous_bits];
|
||||
const size_t sub_index = big_page_index % continuous_bits;
|
||||
big_page_continuous[big_page_index / continuous_bits] =
|
||||
(~(1ULL << sub_index) & continuous_mask) | (value ? 1ULL << sub_index : 0);
|
||||
}
|
||||
|
||||
template <MemoryManager::EntryType entry_type>
|
||||
@@ -140,7 +140,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
|
||||
const auto index = PageEntryIndex<true>(current_gpu_addr);
|
||||
const u32 sub_value = static_cast<u32>(current_cpu_addr >> cpu_page_bits);
|
||||
big_page_table_cpu[index] = sub_value;
|
||||
const bool is_continous = ([&] {
|
||||
const bool is_continuous = ([&] {
|
||||
uintptr_t base_ptr{
|
||||
reinterpret_cast<uintptr_t>(memory.GetPointerSilent(current_cpu_addr))};
|
||||
if (base_ptr == 0) {
|
||||
@@ -156,7 +156,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
SetBigPageContinous(index, is_continous);
|
||||
SetBigPageContinuous(index, is_continuous);
|
||||
}
|
||||
remaining_size -= big_page_size;
|
||||
}
|
||||
@@ -378,7 +378,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
|
||||
if constexpr (is_safe) {
|
||||
rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
|
||||
}
|
||||
if (!IsBigPageContinous(page_index)) [[unlikely]] {
|
||||
if (!IsBigPageContinuous(page_index)) [[unlikely]] {
|
||||
memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
|
||||
} else {
|
||||
u8* physical = memory.GetPointer(cpu_addr_base);
|
||||
@@ -427,7 +427,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
|
||||
if constexpr (is_safe) {
|
||||
rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
|
||||
}
|
||||
if (!IsBigPageContinous(page_index)) [[unlikely]] {
|
||||
if (!IsBigPageContinuous(page_index)) [[unlikely]] {
|
||||
memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount);
|
||||
} else {
|
||||
u8* physical = memory.GetPointer(cpu_addr_base);
|
||||
@@ -512,7 +512,7 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
|
||||
size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
|
||||
std::optional<VAddr> old_page_addr{};
|
||||
size_t range_so_far = 0;
|
||||
bool result{false};
|
||||
@@ -553,7 +553,7 @@ size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
|
||||
}
|
||||
|
||||
size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
|
||||
return kind_map.GetContinousSizeFrom(gpu_addr);
|
||||
return kind_map.GetContinuousSizeFrom(gpu_addr);
|
||||
}
|
||||
|
||||
void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
|
||||
@@ -594,7 +594,7 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std
|
||||
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
if (GetEntry<true>(gpu_addr) == EntryType::Mapped) [[likely]] {
|
||||
size_t page_index = gpu_addr >> big_page_bits;
|
||||
if (IsBigPageContinous(page_index)) [[likely]] {
|
||||
if (IsBigPageContinuous(page_index)) [[likely]] {
|
||||
const std::size_t page{(page_index & big_page_mask) + size};
|
||||
return page <= big_page_size;
|
||||
}
|
||||
@@ -608,7 +608,7 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
return page <= Core::Memory::YUZU_PAGESIZE;
|
||||
}
|
||||
|
||||
bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
bool MemoryManager::IsContinuousRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
std::optional<VAddr> old_page_addr{};
|
||||
bool result{true};
|
||||
auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
/**
|
||||
* Checks if a gpu region is mapped by a single range of cpu addresses.
|
||||
*/
|
||||
[[nodiscard]] bool IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const;
|
||||
[[nodiscard]] bool IsContinuousRange(GPUVAddr gpu_addr, std::size_t size) const;
|
||||
|
||||
/**
|
||||
* Checks if a gpu region is mapped entirely.
|
||||
@@ -123,7 +123,7 @@ public:
|
||||
bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
|
||||
VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
|
||||
|
||||
size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const;
|
||||
size_t MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const;
|
||||
|
||||
bool IsWithinGPUAddressRange(GPUVAddr gpu_addr) const {
|
||||
return gpu_addr < address_space_size;
|
||||
@@ -158,8 +158,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsBigPageContinous(size_t big_page_index) const;
|
||||
inline void SetBigPageContinous(size_t big_page_index, bool value);
|
||||
inline bool IsBigPageContinuous(size_t big_page_index) const;
|
||||
inline void SetBigPageContinuous(size_t big_page_index, bool value);
|
||||
|
||||
template <bool is_gpu_address>
|
||||
void GetSubmappedRangeImpl(
|
||||
@@ -213,10 +213,10 @@ private:
|
||||
Common::RangeMap<GPUVAddr, PTEKind> kind_map;
|
||||
Common::VirtualBuffer<u32> big_page_table_cpu;
|
||||
|
||||
std::vector<u64> big_page_continous;
|
||||
std::vector<u64> big_page_continuous;
|
||||
std::vector<std::pair<VAddr, std::size_t>> page_stash{};
|
||||
|
||||
static constexpr size_t continous_bits = 64;
|
||||
static constexpr size_t continuous_bits = 64;
|
||||
|
||||
const size_t unique_identifier;
|
||||
std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using Tegra::Engines::Fermi2D;
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
using Tegra::Texture::TextureType;
|
||||
using Tegra::Texture::TICEntry;
|
||||
@@ -114,86 +115,89 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::ImageInfo(const Maxwell3D::Regs& regs, size_t index) noexcept {
|
||||
const auto& rt = regs.rt[index];
|
||||
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format);
|
||||
ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
|
||||
Tegra::Texture::MsaaMode msaa_mode) noexcept {
|
||||
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(ct.format);
|
||||
rescaleable = false;
|
||||
if (rt.tile_mode.is_pitch_linear) {
|
||||
ASSERT(rt.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray);
|
||||
if (ct.tile_mode.is_pitch_linear) {
|
||||
ASSERT(ct.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DefineArraySize);
|
||||
type = ImageType::Linear;
|
||||
pitch = rt.width;
|
||||
pitch = ct.width;
|
||||
size = Extent3D{
|
||||
.width = pitch / BytesPerBlock(format),
|
||||
.height = rt.height,
|
||||
.height = ct.height,
|
||||
.depth = 1,
|
||||
};
|
||||
return;
|
||||
}
|
||||
size.width = rt.width;
|
||||
size.height = rt.height;
|
||||
layer_stride = rt.array_pitch * 4;
|
||||
size.width = ct.width;
|
||||
size.height = ct.height;
|
||||
layer_stride = ct.array_pitch * 4;
|
||||
maybe_unaligned_layer_stride = layer_stride;
|
||||
num_samples = NumSamples(regs.anti_alias_samples_mode);
|
||||
num_samples = NumSamples(msaa_mode);
|
||||
block = Extent3D{
|
||||
.width = rt.tile_mode.block_width,
|
||||
.height = rt.tile_mode.block_height,
|
||||
.depth = rt.tile_mode.block_depth,
|
||||
.width = ct.tile_mode.block_width,
|
||||
.height = ct.tile_mode.block_height,
|
||||
.depth = ct.tile_mode.block_depth,
|
||||
};
|
||||
if (rt.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) {
|
||||
if (ct.tile_mode.dim_control == Maxwell3D::Regs::TileMode::DimensionControl::DefineDepthSize) {
|
||||
type = ImageType::e3D;
|
||||
size.depth = rt.depth;
|
||||
size.depth = ct.depth;
|
||||
} else {
|
||||
rescaleable = block.depth == 0;
|
||||
rescaleable &= size.height > 256;
|
||||
downscaleable = size.height > 512;
|
||||
type = ImageType::e2D;
|
||||
resources.layers = rt.depth;
|
||||
resources.layers = ct.depth;
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept {
|
||||
format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format);
|
||||
size.width = regs.zeta_size.width;
|
||||
size.height = regs.zeta_size.height;
|
||||
ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::ZetaSize& zt_size,
|
||||
Tegra::Texture::MsaaMode msaa_mode) noexcept {
|
||||
format = VideoCore::Surface::PixelFormatFromDepthFormat(zt.format);
|
||||
size.width = zt_size.width;
|
||||
size.height = zt_size.height;
|
||||
rescaleable = false;
|
||||
resources.levels = 1;
|
||||
layer_stride = regs.zeta.array_pitch * 4;
|
||||
layer_stride = zt.array_pitch * 4;
|
||||
maybe_unaligned_layer_stride = layer_stride;
|
||||
num_samples = NumSamples(regs.anti_alias_samples_mode);
|
||||
num_samples = NumSamples(msaa_mode);
|
||||
block = Extent3D{
|
||||
.width = regs.zeta.tile_mode.block_width,
|
||||
.height = regs.zeta.tile_mode.block_height,
|
||||
.depth = regs.zeta.tile_mode.block_depth,
|
||||
.width = zt.tile_mode.block_width,
|
||||
.height = zt.tile_mode.block_height,
|
||||
.depth = zt.tile_mode.block_depth,
|
||||
};
|
||||
if (regs.zeta.tile_mode.is_pitch_linear) {
|
||||
ASSERT(regs.zeta.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray);
|
||||
if (zt.tile_mode.is_pitch_linear) {
|
||||
ASSERT(zt.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DefineArraySize);
|
||||
type = ImageType::Linear;
|
||||
pitch = size.width * BytesPerBlock(format);
|
||||
} else if (regs.zeta.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) {
|
||||
ASSERT(regs.zeta.tile_mode.is_pitch_linear == 0);
|
||||
ASSERT(regs.zeta_size.dim_control ==
|
||||
Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeOne);
|
||||
} else if (zt.tile_mode.dim_control ==
|
||||
Maxwell3D::Regs::TileMode::DimensionControl::DefineDepthSize) {
|
||||
ASSERT(zt_size.dim_control == Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeIsOne);
|
||||
type = ImageType::e3D;
|
||||
size.depth = regs.zeta_size.depth;
|
||||
size.depth = zt_size.depth;
|
||||
} else {
|
||||
ASSERT(regs.zeta_size.dim_control ==
|
||||
Maxwell3D::Regs::ZetaSize::DimensionControl::DepthDefinesArray);
|
||||
rescaleable = block.depth == 0;
|
||||
downscaleable = size.height > 512;
|
||||
type = ImageType::e2D;
|
||||
resources.layers = regs.zeta_size.depth;
|
||||
switch (zt_size.dim_control) {
|
||||
case Maxwell3D::Regs::ZetaSize::DimensionControl::DefineArraySize:
|
||||
resources.layers = zt_size.depth;
|
||||
break;
|
||||
case Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeIsOne:
|
||||
resources.layers = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept {
|
||||
ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept {
|
||||
UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero");
|
||||
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format);
|
||||
rescaleable = false;
|
||||
if (config.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch) {
|
||||
if (config.linear == Fermi2D::MemoryLayout::Pitch) {
|
||||
type = ImageType::Linear;
|
||||
size = Extent3D{
|
||||
.width = config.pitch / VideoCore::Surface::BytesPerBlock(format),
|
||||
|
||||
@@ -17,8 +17,11 @@ using VideoCore::Surface::PixelFormat;
|
||||
struct ImageInfo {
|
||||
ImageInfo() = default;
|
||||
explicit ImageInfo(const TICEntry& config) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& ct,
|
||||
Tegra::Texture::MsaaMode msaa_mode) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs::Zeta& zt,
|
||||
const Tegra::Engines::Maxwell3D::Regs::ZetaSize& zt_size,
|
||||
Tegra::Texture::MsaaMode msaa_mode) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept;
|
||||
explicit ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept;
|
||||
|
||||
|
||||
@@ -1176,13 +1176,13 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
std::vector<ImageId> overlap_ids;
|
||||
boost::container::small_vector<ImageId, 4> overlap_ids;
|
||||
std::unordered_set<ImageId> overlaps_found;
|
||||
std::vector<ImageId> left_aliased_ids;
|
||||
std::vector<ImageId> right_aliased_ids;
|
||||
boost::container::small_vector<ImageId, 4> left_aliased_ids;
|
||||
boost::container::small_vector<ImageId, 4> right_aliased_ids;
|
||||
std::unordered_set<ImageId> ignore_textures;
|
||||
std::vector<ImageId> bad_overlap_ids;
|
||||
std::vector<ImageId> all_siblings;
|
||||
boost::container::small_vector<ImageId, 4> bad_overlap_ids;
|
||||
boost::container::small_vector<ImageId, 4> all_siblings;
|
||||
const bool this_is_linear = info.type == ImageType::Linear;
|
||||
const auto region_check = [&](ImageId overlap_id, ImageBase& overlap) {
|
||||
if (True(overlap.flags & ImageFlagBits::Remapped)) {
|
||||
@@ -1269,7 +1269,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
|
||||
Image& new_image = slot_images[new_image_id];
|
||||
|
||||
if (!gpu_memory->IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) {
|
||||
if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes)) {
|
||||
new_image.flags |= ImageFlagBits::Sparse;
|
||||
}
|
||||
|
||||
@@ -1298,16 +1298,16 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
Image& overlap = slot_images[overlap_id];
|
||||
if (True(overlap.flags & ImageFlagBits::GpuModified)) {
|
||||
new_image.flags |= ImageFlagBits::GpuModified;
|
||||
}
|
||||
const auto& resolution = Settings::values.resolution_info;
|
||||
const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
|
||||
const u32 up_scale = can_rescale ? resolution.up_scale : 1;
|
||||
const u32 down_shift = can_rescale ? resolution.down_shift : 0;
|
||||
auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift);
|
||||
if (overlap.info.num_samples != new_image.info.num_samples) {
|
||||
runtime.CopyImageMSAA(new_image, overlap, std::move(copies));
|
||||
} else {
|
||||
runtime.CopyImage(new_image, overlap, std::move(copies));
|
||||
const auto& resolution = Settings::values.resolution_info;
|
||||
const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
|
||||
const u32 up_scale = can_rescale ? resolution.up_scale : 1;
|
||||
const u32 down_shift = can_rescale ? resolution.down_shift : 0;
|
||||
auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift);
|
||||
if (overlap.info.num_samples != new_image.info.num_samples) {
|
||||
runtime.CopyImageMSAA(new_image, overlap, std::move(copies));
|
||||
} else {
|
||||
runtime.CopyImage(new_image, overlap, std::move(copies));
|
||||
}
|
||||
}
|
||||
if (True(overlap.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(overlap, overlap_id);
|
||||
@@ -1503,7 +1503,7 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
|
||||
if (rt.format == Tegra::RenderTargetFormat::NONE) {
|
||||
return ImageViewId{};
|
||||
}
|
||||
const ImageInfo info(regs, index);
|
||||
const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode);
|
||||
return FindRenderTargetView(info, gpu_addr, is_clear);
|
||||
}
|
||||
|
||||
@@ -1517,7 +1517,7 @@ ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) {
|
||||
if (gpu_addr == 0) {
|
||||
return ImageViewId{};
|
||||
}
|
||||
const ImageInfo info(regs);
|
||||
const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode);
|
||||
return FindRenderTargetView(info, gpu_addr, is_clear);
|
||||
}
|
||||
|
||||
|
||||
@@ -375,6 +375,8 @@ const char* ToString(VkResult result) noexcept {
|
||||
return "VK_RESULT_MAX_ENUM";
|
||||
case VkResult::VK_ERROR_COMPRESSION_EXHAUSTED_EXT:
|
||||
return "VK_ERROR_COMPRESSION_EXHAUSTED_EXT";
|
||||
case VkResult::VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT:
|
||||
return "VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user