Compare commits
35 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce46aa4721 | ||
|
|
20e9501b0d | ||
|
|
56d9052b73 | ||
|
|
91d802f68d | ||
|
|
5f8d6bc504 | ||
|
|
e292b2d991 | ||
|
|
6c9b9b72da | ||
|
|
f582a4482d | ||
|
|
1079215871 | ||
|
|
6978093404 | ||
|
|
2f45e999d8 | ||
|
|
d44464829b | ||
|
|
efe50d88ec | ||
|
|
71f62a346d | ||
|
|
c0e45a3c78 | ||
|
|
70482e6b26 | ||
|
|
ca5e843bf6 | ||
|
|
a74fddc98f | ||
|
|
57ebcbf2c4 | ||
|
|
c7019db6f4 | ||
|
|
21f5912ec9 | ||
|
|
8d46c3cc66 | ||
|
|
7712e46d64 | ||
|
|
b504df9d02 | ||
|
|
2984695265 | ||
|
|
c9260a75f6 | ||
|
|
b57d61010f | ||
|
|
6a1ad03153 | ||
|
|
e35c2fd5d0 | ||
|
|
29f9a454eb | ||
|
|
41b65d38fa | ||
|
|
fc9abd3c62 | ||
|
|
c001a2af25 | ||
|
|
3d24eb54ec | ||
|
|
21742f0096 |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: a8cbfd9af4...19a423034e
@@ -167,6 +167,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
|
||||
// Core
|
||||
values.use_multi_core.SetGlobal(true);
|
||||
values.use_extended_memory_layout.SetGlobal(true);
|
||||
|
||||
// CPU
|
||||
values.cpu_accuracy.SetGlobal(true);
|
||||
|
||||
@@ -466,6 +466,7 @@ struct Values {
|
||||
|
||||
// Core
|
||||
Setting<bool> use_multi_core{true, "use_multi_core"};
|
||||
Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
|
||||
|
||||
// Cpu
|
||||
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
@@ -252,9 +254,16 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||
|
||||
// Create a resource limit for the process.
|
||||
const auto physical_memory_size =
|
||||
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
|
||||
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
|
||||
|
||||
// Create the process.
|
||||
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
||||
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
|
||||
Kernel::KProcess::ProcessType::Userland)
|
||||
Kernel::KProcess::ProcessType::Userland, resource_limit)
|
||||
.IsSuccess());
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/device_memory.h"
|
||||
#include "hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {}
|
||||
DeviceMemory::DeviceMemory()
|
||||
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
||||
1ULL << 39} {}
|
||||
DeviceMemory::~DeviceMemory() = default;
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -12,12 +12,8 @@ namespace Core {
|
||||
namespace DramMemoryMap {
|
||||
enum : u64 {
|
||||
Base = 0x80000000ULL,
|
||||
Size = 0x100000000ULL,
|
||||
End = Base + Size,
|
||||
KernelReserveBase = Base + 0x60000,
|
||||
SlabHeapBase = KernelReserveBase + 0x85000,
|
||||
SlapHeapSize = 0xa21000,
|
||||
SlabHeapEnd = SlabHeapBase + SlapHeapSize,
|
||||
};
|
||||
}; // namespace DramMemoryMap
|
||||
|
||||
|
||||
@@ -885,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
|
||||
auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
for (auto& device : output_devices) {
|
||||
if (!device) {
|
||||
|
||||
@@ -299,16 +299,23 @@ public:
|
||||
|
||||
/**
|
||||
* Sends a specific vibration to the output device
|
||||
* @return returns true if vibration had no errors
|
||||
* @return true if vibration had no errors
|
||||
*/
|
||||
bool SetVibration(std::size_t device_index, VibrationValue vibration);
|
||||
|
||||
/**
|
||||
* Sends a small vibration to the output device
|
||||
* @return returns true if SetVibration was successfull
|
||||
* @return true if SetVibration was successfull
|
||||
*/
|
||||
bool TestVibration(std::size_t device_index);
|
||||
|
||||
/**
|
||||
* Sets the desired data to be polled from a controller
|
||||
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
|
||||
* @return true if SetPollingMode was successfull
|
||||
*/
|
||||
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
||||
|
||||
/// Returns the led pattern corresponding to this emulated controller
|
||||
LedPattern GetLedPattern() const;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <random>
|
||||
|
||||
#include "common/literals.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
|
||||
@@ -28,30 +29,13 @@ namespace {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
u32 GetMemoryModeForInit() {
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
u32 GetMemorySizeForInit() {
|
||||
return 0;
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
|
||||
}
|
||||
|
||||
Smc::MemoryArrangement GetMemoryArrangeForInit() {
|
||||
switch (GetMemoryModeForInit() & 0x3F) {
|
||||
case 0x01:
|
||||
default:
|
||||
return Smc::MemoryArrangement_4GB;
|
||||
case 0x02:
|
||||
return Smc::MemoryArrangement_4GBForAppletDev;
|
||||
case 0x03:
|
||||
return Smc::MemoryArrangement_4GBForSystemDev;
|
||||
case 0x11:
|
||||
return Smc::MemoryArrangement_6GB;
|
||||
case 0x12:
|
||||
return Smc::MemoryArrangement_6GBForAppletDev;
|
||||
case 0x21:
|
||||
return Smc::MemoryArrangement_8GB;
|
||||
}
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
|
||||
: Smc::MemoryArrangement_4GB;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ KEvent::KEvent(KernelCore& kernel_)
|
||||
|
||||
KEvent::~KEvent() = default;
|
||||
|
||||
void KEvent::Initialize(std::string&& name_) {
|
||||
void KEvent::Initialize(std::string&& name_, KProcess* owner_) {
|
||||
// Increment reference count.
|
||||
// Because reference count is one on creation, this will result
|
||||
// in a reference count of two. Thus, when both readable and
|
||||
@@ -30,10 +30,8 @@ void KEvent::Initialize(std::string&& name_) {
|
||||
writable_event.Initialize(this, name_ + ":Writable");
|
||||
|
||||
// Set our owner process.
|
||||
owner = kernel.CurrentProcess();
|
||||
if (owner) {
|
||||
owner->Open();
|
||||
}
|
||||
owner = owner_;
|
||||
owner->Open();
|
||||
|
||||
// Mark initialized.
|
||||
name = std::move(name_);
|
||||
@@ -47,10 +45,8 @@ void KEvent::Finalize() {
|
||||
void KEvent::PostDestroy(uintptr_t arg) {
|
||||
// Release the event count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
if (owner) {
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
|
||||
owner->Close();
|
||||
}
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
|
||||
owner->Close();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
explicit KEvent(KernelCore& kernel_);
|
||||
~KEvent() override;
|
||||
|
||||
void Initialize(std::string&& name);
|
||||
void Initialize(std::string&& name, KProcess* owner_);
|
||||
|
||||
void Finalize() override;
|
||||
|
||||
|
||||
@@ -986,9 +986,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
|
||||
VAddr cur_addr{addr};
|
||||
|
||||
for (const auto& node : page_linked_list.Nodes()) {
|
||||
const std::size_t num_pages{(addr - cur_addr) / PageSize};
|
||||
if (const auto result{
|
||||
Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
|
||||
if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
|
||||
OperationType::Unmap)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -123,12 +123,11 @@ private:
|
||||
};
|
||||
|
||||
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
|
||||
ProcessType type) {
|
||||
ProcessType type, KResourceLimit* res_limit) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
process->name = std::move(process_name);
|
||||
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->resource_limit = res_limit;
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
|
||||
@@ -143,9 +142,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
|
||||
kernel.AppendNewProcess(process);
|
||||
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
// Clear remaining fields.
|
||||
process->num_running_threads = 0;
|
||||
process->is_signaled = false;
|
||||
@@ -153,6 +149,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
process->is_suspended = false;
|
||||
process->schedule_count = 0;
|
||||
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name,
|
||||
ProcessType type);
|
||||
ProcessType type, KResourceLimit* res_limit);
|
||||
|
||||
/// Gets a reference to the process' page table.
|
||||
KPageTable& PageTable() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
@@ -151,4 +152,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
|
||||
}
|
||||
}
|
||||
|
||||
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
|
||||
auto* resource_limit = KResourceLimit::Create(system.Kernel());
|
||||
resource_limit->Initialize(&system.CoreTiming());
|
||||
|
||||
// Initialize default resource limit values.
|
||||
// TODO(bunnei): These values are the system defaults, the limits for service processes are
|
||||
// lower. These should use the correct limit values.
|
||||
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
|
||||
.IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
|
||||
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -67,4 +67,7 @@ private:
|
||||
KLightConditionVariable cond_var;
|
||||
const Core::Timing::CoreTiming* core_timing{};
|
||||
};
|
||||
|
||||
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -240,13 +240,6 @@ struct KernelCore::Impl {
|
||||
constexpr u64 secure_applet_memory_size{4_MiB};
|
||||
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
|
||||
secure_applet_memory_size));
|
||||
|
||||
// This memory seems to be reserved on hardware, but is not reserved/used by yuzu.
|
||||
// Likely Horizon OS reserved memory
|
||||
// TODO(ameerj): Derive the memory rather than hardcode it.
|
||||
constexpr u64 unknown_reserved_memory{0x2f896000};
|
||||
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
|
||||
unknown_reserved_memory));
|
||||
}
|
||||
|
||||
void InitializePreemption(KernelCore& kernel) {
|
||||
|
||||
@@ -2332,7 +2332,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
|
||||
R_UNLESS(event != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize("CreateEvent");
|
||||
event->Initialize("CreateEvent", kernel.CurrentProcess());
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
@@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->GetWritableEvent().Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
return AppletMessage::None;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
@@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
}
|
||||
|
||||
void AppletMessageQueue::RequestExit() {
|
||||
PushMessage(AppletMessage::ExitRequested);
|
||||
PushMessage(AppletMessage::Exit);
|
||||
}
|
||||
|
||||
void AppletMessageQueue::FocusStateChanged() {
|
||||
@@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
const auto message = msg_queue->PopMessage();
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
|
||||
if (message == AppletMessageQueue::AppletMessage::None) {
|
||||
LOG_ERROR(Service_AM, "Message queue is empty");
|
||||
rb.Push(ERR_NO_MESSAGES);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
|
||||
@@ -22,6 +22,7 @@ class NVFlinger;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
// This is nn::settings::Language
|
||||
enum SystemLanguage {
|
||||
Japanese = 0,
|
||||
English = 1, // en-US
|
||||
@@ -41,16 +42,44 @@ enum SystemLanguage {
|
||||
// 4.0.0+
|
||||
SimplifiedChinese = 15,
|
||||
TraditionalChinese = 16,
|
||||
// 10.1.0+
|
||||
BrazilianPortuguese = 17,
|
||||
};
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
// This is nn::am::AppletMessage
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
ExitRequested = 4,
|
||||
None = 0,
|
||||
ChangeIntoForeground = 1,
|
||||
ChangeIntoBackground = 2,
|
||||
Exit = 4,
|
||||
ApplicationExited = 6,
|
||||
FocusStateChanged = 15,
|
||||
Resume = 16,
|
||||
DetectShortPressingHomeButton = 20,
|
||||
DetectLongPressingHomeButton = 21,
|
||||
DetectShortPressingPowerButton = 22,
|
||||
DetectMiddlePressingPowerButton = 23,
|
||||
DetectLongPressingPowerButton = 24,
|
||||
RequestToPrepareSleep = 25,
|
||||
FinishedSleepSequence = 26,
|
||||
SleepRequiredByHighTemperature = 27,
|
||||
SleepRequiredByLowBattery = 28,
|
||||
AutoPowerDown = 29,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
DetectReceivingCecSystemStandby = 32,
|
||||
SdCardRemoved = 33,
|
||||
LaunchApplicationRequested = 50,
|
||||
RequestToDisplay = 51,
|
||||
ShowApplicationLogo = 55,
|
||||
HideApplicationLogo = 56,
|
||||
ForceHideApplicationLogo = 57,
|
||||
FloatingApplicationDetected = 60,
|
||||
DetectShortPressingCaptureButton = 90,
|
||||
AlbumScreenShotTaken = 92,
|
||||
AlbumRecordingSaved = 93,
|
||||
};
|
||||
|
||||
explicit AppletMessageQueue(Core::System& system);
|
||||
@@ -179,11 +208,14 @@ public:
|
||||
~ICommonStateGetter() override;
|
||||
|
||||
private:
|
||||
// This is nn::oe::FocusState
|
||||
enum class FocusState : u8 {
|
||||
InFocus = 1,
|
||||
NotInFocus = 2,
|
||||
Background = 3,
|
||||
};
|
||||
|
||||
// This is nn::oe::OperationMode
|
||||
enum class OperationMode : u8 {
|
||||
Handheld = 0,
|
||||
Docked = 1,
|
||||
|
||||
@@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con
|
||||
|
||||
Controller::Controller(Core::Timing::CoreTiming& core_timing_)
|
||||
: core_timing{core_timing_}, configs{
|
||||
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
{PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
{PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
} {}
|
||||
|
||||
Controller::~Controller() = default;
|
||||
@@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
|
||||
PerformanceConfiguration::Config15,
|
||||
}};
|
||||
|
||||
SetPerformanceConfiguration(PerformanceMode::Docked,
|
||||
SetPerformanceConfiguration(PerformanceMode::Boost,
|
||||
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
|
||||
}
|
||||
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() const {
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
|
||||
: PerformanceMode::Handheld;
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
|
||||
: PerformanceMode::Normal;
|
||||
}
|
||||
|
||||
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
|
||||
|
||||
@@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 {
|
||||
Config16 = 0x9222000C,
|
||||
};
|
||||
|
||||
// This is nn::oe::CpuBoostMode
|
||||
enum class CpuBoostMode : u32 {
|
||||
Disabled = 0,
|
||||
Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16
|
||||
Partial = 2, // GPU Only -> Config 15 or 16
|
||||
Normal = 0, // Boost mode disabled
|
||||
FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16
|
||||
Partial = 2, // GPU Only -> Config 15 or 16
|
||||
};
|
||||
|
||||
enum class PerformanceMode : u8 {
|
||||
Handheld = 0,
|
||||
Docked = 1,
|
||||
// This is nn::oe::PerformanceMode
|
||||
enum class PerformanceMode : s32 {
|
||||
Invalid = -1,
|
||||
Normal = 0,
|
||||
Boost = 1,
|
||||
};
|
||||
|
||||
// Class to manage the state and change of the emulated system performance.
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
@@ -15,10 +17,21 @@ namespace Service::KernelHelpers {
|
||||
|
||||
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
|
||||
: kernel(system_.Kernel()) {
|
||||
|
||||
// Create a resource limit for the process.
|
||||
const auto physical_memory_size =
|
||||
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System);
|
||||
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size);
|
||||
|
||||
// Create the process.
|
||||
process = Kernel::KProcess::Create(kernel);
|
||||
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
|
||||
Kernel::KProcess::ProcessType::Userland)
|
||||
Kernel::KProcess::ProcessType::KernelInternal,
|
||||
resource_limit)
|
||||
.IsSuccess());
|
||||
|
||||
// Close reference to our resource limit, as the process opens one.
|
||||
resource_limit->Close();
|
||||
}
|
||||
|
||||
ServiceContext::~ServiceContext() {
|
||||
@@ -43,7 +56,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
|
||||
}
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize(std::move(name));
|
||||
event->Initialize(std::move(name), process);
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,15 +7,132 @@
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core::HID {
|
||||
enum class NpadIdType : u32;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::NFP {
|
||||
|
||||
enum class ServiceType : u32 {
|
||||
User,
|
||||
Debug,
|
||||
System,
|
||||
};
|
||||
|
||||
enum class State : u32 {
|
||||
NonInitialized,
|
||||
Initialized,
|
||||
};
|
||||
|
||||
enum class DeviceState : u32 {
|
||||
Initialized,
|
||||
SearchingForTag,
|
||||
TagFound,
|
||||
TagRemoved,
|
||||
TagMounted,
|
||||
Unaviable,
|
||||
Finalized,
|
||||
};
|
||||
|
||||
enum class ModelType : u32 {
|
||||
Amiibo,
|
||||
};
|
||||
|
||||
enum class MountTarget : u32 {
|
||||
Rom,
|
||||
Ram,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class AmiiboType : u8 {
|
||||
Figure,
|
||||
Card,
|
||||
Yarn,
|
||||
};
|
||||
|
||||
enum class AmiiboSeries : u8 {
|
||||
SuperSmashBros,
|
||||
SuperMario,
|
||||
ChibiRobo,
|
||||
YoshiWoollyWorld,
|
||||
Splatoon,
|
||||
AnimalCrossing,
|
||||
EightBitMario,
|
||||
Skylanders,
|
||||
Unknown8,
|
||||
TheLegendOfZelda,
|
||||
ShovelKnight,
|
||||
Unknown11,
|
||||
Kiby,
|
||||
Pokemon,
|
||||
MarioSportsSuperstars,
|
||||
MonsterHunter,
|
||||
BoxBoy,
|
||||
Pikmin,
|
||||
FireEmblem,
|
||||
Metroid,
|
||||
Others,
|
||||
MegaMan,
|
||||
Diablo
|
||||
};
|
||||
|
||||
using TagUuid = std::array<u8, 10>;
|
||||
|
||||
struct TagInfo {
|
||||
TagUuid uuid;
|
||||
u8 uuid_length;
|
||||
INSERT_PADDING_BYTES(0x15);
|
||||
s32 protocol;
|
||||
u32 tag_type;
|
||||
INSERT_PADDING_BYTES(0x30);
|
||||
};
|
||||
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
|
||||
|
||||
struct CommonInfo {
|
||||
u16 last_write_year;
|
||||
u8 last_write_month;
|
||||
u8 last_write_day;
|
||||
u16 write_counter;
|
||||
u16 version;
|
||||
u32 application_area_size;
|
||||
INSERT_PADDING_BYTES(0x34);
|
||||
};
|
||||
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
|
||||
|
||||
struct ModelInfo {
|
||||
u16 character_id;
|
||||
u8 character_variant;
|
||||
AmiiboType amiibo_type;
|
||||
u16 model_number;
|
||||
AmiiboSeries series;
|
||||
u8 fixed; // Must be 02
|
||||
INSERT_PADDING_BYTES(0x4); // Unknown
|
||||
INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
|
||||
INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
|
||||
};
|
||||
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
|
||||
|
||||
struct RegisterInfo {
|
||||
Service::Mii::MiiInfo mii_char_info;
|
||||
u16 first_write_year;
|
||||
u8 first_write_month;
|
||||
u8 first_write_day;
|
||||
std::array<u8, 11> amiibo_name;
|
||||
u8 unknown;
|
||||
INSERT_PADDING_BYTES(0x98);
|
||||
};
|
||||
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
@@ -24,34 +141,131 @@ public:
|
||||
const char* name);
|
||||
~Interface() override;
|
||||
|
||||
struct ModelInfo {
|
||||
std::array<u8, 0x8> amiibo_identification_block;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
struct EncryptedAmiiboFile {
|
||||
u16 crypto_init; // Must be A5 XX
|
||||
u16 write_count; // Number of times the amiibo has been written?
|
||||
INSERT_PADDING_BYTES(0x20); // System crypts
|
||||
INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
|
||||
ModelInfo model_info; // This struct is bigger than documentation
|
||||
INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
|
||||
INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
|
||||
INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
|
||||
};
|
||||
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
|
||||
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
|
||||
|
||||
struct AmiiboFile {
|
||||
std::array<u8, 10> uuid;
|
||||
INSERT_PADDING_BYTES(0x4a);
|
||||
ModelInfo model_info;
|
||||
struct NTAG215Password {
|
||||
u32 PWD; // Password to allow write access
|
||||
u16 PACK; // Password acknowledge reply
|
||||
u16 RFUI; // Reserved for future use
|
||||
};
|
||||
static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
|
||||
static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
|
||||
|
||||
struct NTAG215File {
|
||||
TagUuid uuid; // Unique serial number
|
||||
u16 lock_bytes; // Set defined pages as read only
|
||||
u32 compability_container; // Defines available memory
|
||||
EncryptedAmiiboFile user_memory; // Writable data
|
||||
u32 dynamic_lock; // Dynamic lock
|
||||
u32 CFG0; // Defines memory protected by password
|
||||
u32 CFG1; // Defines number of verification attempts
|
||||
NTAG215Password password; // Password data
|
||||
};
|
||||
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
|
||||
|
||||
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
||||
bool LoadAmiibo(const std::vector<u8>& buffer);
|
||||
Kernel::KReadableEvent& GetNFCEvent();
|
||||
const AmiiboFile& GetAmiiboBuffer() const;
|
||||
void CloseAmiibo();
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
ResultCode StartDetection(s32 protocol_);
|
||||
ResultCode StopDetection();
|
||||
ResultCode Mount();
|
||||
ResultCode Unmount();
|
||||
|
||||
ResultCode GetTagInfo(TagInfo& tag_info) const;
|
||||
ResultCode GetCommonInfo(CommonInfo& common_info) const;
|
||||
ResultCode GetModelInfo(ModelInfo& model_info) const;
|
||||
ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
|
||||
|
||||
ResultCode OpenApplicationArea(u32 access_id);
|
||||
ResultCode GetApplicationArea(std::vector<u8>& data) const;
|
||||
ResultCode SetApplicationArea(const std::vector<u8>& data);
|
||||
ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
|
||||
|
||||
u64 GetHandle() const;
|
||||
DeviceState GetCurrentState() const;
|
||||
Core::HID::NpadIdType GetNpadId() const;
|
||||
|
||||
Kernel::KReadableEvent& GetActivateEvent() const;
|
||||
Kernel::KReadableEvent& GetDeactivateEvent() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
private:
|
||||
/// Validates that the amiibo file is not corrupted
|
||||
bool IsAmiiboValid() const;
|
||||
|
||||
bool AmiiboApplicationDataExist(u32 access_id) const;
|
||||
std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
|
||||
void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
|
||||
|
||||
/// return password needed to allow write access to protected memory
|
||||
u32 GetTagPassword(const TagUuid& uuid) const;
|
||||
|
||||
const Core::HID::NpadIdType npad_id;
|
||||
|
||||
DeviceState device_state{DeviceState::Unaviable};
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* nfc_tag_load;
|
||||
AmiiboFile amiibo{};
|
||||
Kernel::KEvent* activate_event;
|
||||
Kernel::KEvent* deactivate_event;
|
||||
NTAG215File tag_data{};
|
||||
s32 protocol;
|
||||
bool is_application_area_initialized{};
|
||||
u32 application_area_id;
|
||||
std::vector<u8> application_area_data;
|
||||
};
|
||||
};
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void Finalize(Kernel::HLERequestContext& ctx);
|
||||
void ListDevices(Kernel::HLERequestContext& ctx);
|
||||
void StartDetection(Kernel::HLERequestContext& ctx);
|
||||
void StopDetection(Kernel::HLERequestContext& ctx);
|
||||
void Mount(Kernel::HLERequestContext& ctx);
|
||||
void Unmount(Kernel::HLERequestContext& ctx);
|
||||
void OpenApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void SetApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void CreateApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void GetTagInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetRegisterInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetCommonInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetModelInfo(Kernel::HLERequestContext& ctx);
|
||||
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetState(Kernel::HLERequestContext& ctx);
|
||||
void GetDeviceState(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadId(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
|
||||
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
// TODO(german77): We should have a vector of interfaces
|
||||
Module::Interface& nfp_interface;
|
||||
|
||||
State state{State::NonInitialized};
|
||||
Kernel::KEvent* availability_change_event;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::NFP
|
||||
|
||||
@@ -39,8 +39,7 @@ struct Memory::Impl {
|
||||
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End,
|
||||
"Out of bounds target: {:016X}", target);
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
|
||||
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
|
||||
@@ -175,22 +175,23 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
BatteryLevel GetBatteryLevel() {
|
||||
Common::Input::BatteryLevel GetBatteryLevel() {
|
||||
const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
|
||||
switch (level) {
|
||||
case SDL_JOYSTICK_POWER_EMPTY:
|
||||
return BatteryLevel::Empty;
|
||||
return Common::Input::BatteryLevel::Empty;
|
||||
case SDL_JOYSTICK_POWER_LOW:
|
||||
return BatteryLevel::Low;
|
||||
return Common::Input::BatteryLevel::Low;
|
||||
case SDL_JOYSTICK_POWER_MEDIUM:
|
||||
return BatteryLevel::Medium;
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
case SDL_JOYSTICK_POWER_FULL:
|
||||
case SDL_JOYSTICK_POWER_MAX:
|
||||
return BatteryLevel::Full;
|
||||
case SDL_JOYSTICK_POWER_UNKNOWN:
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
case SDL_JOYSTICK_POWER_WIRED:
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
case SDL_JOYSTICK_POWER_UNKNOWN:
|
||||
default:
|
||||
return BatteryLevel::Charging;
|
||||
return Common::Input::BatteryLevel::None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +352,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetButton(identifier, event.jbutton.button, true);
|
||||
// Battery doesn't trigger an event so just update every button press
|
||||
SetBattery(identifier, joystick->GetBatteryLevel());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -192,22 +192,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
|
||||
return MAX_UDP_CLIENTS;
|
||||
}
|
||||
|
||||
BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
|
||||
Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
|
||||
switch (battery) {
|
||||
case Response::Battery::Dying:
|
||||
return BatteryLevel::Empty;
|
||||
return Common::Input::BatteryLevel::Empty;
|
||||
case Response::Battery::Low:
|
||||
return BatteryLevel::Critical;
|
||||
return Common::Input::BatteryLevel::Critical;
|
||||
case Response::Battery::Medium:
|
||||
return BatteryLevel::Low;
|
||||
return Common::Input::BatteryLevel::Low;
|
||||
case Response::Battery::High:
|
||||
return BatteryLevel::Medium;
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
case Response::Battery::Full:
|
||||
case Response::Battery::Charged:
|
||||
return BatteryLevel::Full;
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
case Response::Battery::Charging:
|
||||
default:
|
||||
return BatteryLevel::Charging;
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
std::size_t GetClientNumber(std::string_view host, u16 port) const;
|
||||
|
||||
// Translates UDP battery level to input engine battery level
|
||||
BatteryLevel GetBatteryLevel(Response::Battery battery) const;
|
||||
Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const;
|
||||
|
||||
void OnVersion(Response::Version);
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
|
||||
@@ -70,7 +70,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
|
||||
TriggerOnAxisChange(identifier, axis, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) {
|
||||
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
@@ -143,13 +143,13 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||
return axis_iter->second;
|
||||
}
|
||||
|
||||
BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
std::lock_guard lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return BatteryLevel::Charging;
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.battery;
|
||||
@@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] BatteryLevel value) {
|
||||
[[maybe_unused]] Common::Input::BatteryLevel value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
|
||||
@@ -34,16 +34,6 @@ struct BasicMotion {
|
||||
u64 delta_timestamp{};
|
||||
};
|
||||
|
||||
// Stages of a battery charge
|
||||
enum class BatteryLevel {
|
||||
Empty,
|
||||
Critical,
|
||||
Low,
|
||||
Medium,
|
||||
Full,
|
||||
Charging,
|
||||
};
|
||||
|
||||
// Types of input that are stored in the engine
|
||||
enum class EngineInputType {
|
||||
None,
|
||||
@@ -178,7 +168,7 @@ public:
|
||||
bool GetButton(const PadIdentifier& identifier, int button) const;
|
||||
bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
|
||||
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
||||
BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
||||
|
||||
int SetCallback(InputIdentifier input_identifier);
|
||||
@@ -189,7 +179,7 @@ protected:
|
||||
void SetButton(const PadIdentifier& identifier, int button, bool value);
|
||||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
|
||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||
|
||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||
@@ -202,13 +192,13 @@ private:
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
BatteryLevel battery{};
|
||||
Common::Input::BatteryLevel battery{};
|
||||
};
|
||||
|
||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value);
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@ public:
|
||||
}
|
||||
|
||||
Common::Input::BatteryStatus GetStatus() const {
|
||||
return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier));
|
||||
return input_engine->GetBattery(identifier);
|
||||
}
|
||||
|
||||
void ForceUpdate() override {
|
||||
|
||||
@@ -184,6 +184,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return GL_FLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_11_11_10:
|
||||
return GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -495,6 +495,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_11_11_10:
|
||||
return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,8 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
|
||||
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM:
|
||||
return VK_FORMAT_R5G6B5_UNORM_PACK16;
|
||||
case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM:
|
||||
return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
|
||||
static_cast<u32>(framebuffer.pixel_format));
|
||||
|
||||
@@ -554,10 +554,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsFormatFlipped(PixelFormat format) {
|
||||
[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) {
|
||||
switch (format) {
|
||||
case PixelFormat::A1B5G5R5_UNORM:
|
||||
return true;
|
||||
case PixelFormat::B5G6R5_UNORM:
|
||||
return emulate_bgr565;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1488,7 +1490,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
};
|
||||
if (!info.IsRenderTarget()) {
|
||||
swizzle = info.Swizzle();
|
||||
if (IsFormatFlipped(format)) {
|
||||
if (IsFormatFlipped(format, device->MustEmulateBGR565())) {
|
||||
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
|
||||
}
|
||||
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
|
||||
|
||||
@@ -39,6 +39,11 @@ constexpr std::array DEPTH16_UNORM_STENCIL8_UINT{
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
|
||||
constexpr std::array B5G6R5_UNORM_PACK16{
|
||||
VK_FORMAT_R5G6B5_UNORM_PACK16,
|
||||
VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
} // namespace Alternatives
|
||||
|
||||
enum class NvidiaArchitecture {
|
||||
@@ -87,6 +92,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
|
||||
return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data();
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
|
||||
case VK_FORMAT_B5G6R5_UNORM_PACK16:
|
||||
return Alternatives::B5G6R5_UNORM_PACK16.data();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@@ -224,9 +231,14 @@ std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) {
|
||||
return supported_extensions;
|
||||
}
|
||||
|
||||
bool IsExtensionSupported(std::span<const std::string> supported_extensions,
|
||||
std::string_view extension) {
|
||||
return std::ranges::find(supported_extensions, extension) != supported_extensions.end();
|
||||
}
|
||||
|
||||
NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
|
||||
std::span<const std::string> exts) {
|
||||
if (std::ranges::find(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME) != exts.end()) {
|
||||
if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
|
||||
VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{};
|
||||
shading_rate_props.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
|
||||
@@ -239,7 +251,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
|
||||
return NvidiaArchitecture::AmpereOrNewer;
|
||||
}
|
||||
}
|
||||
if (std::ranges::find(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME) != exts.end()) {
|
||||
if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) {
|
||||
return NvidiaArchitecture::Turing;
|
||||
}
|
||||
return NvidiaArchitecture::VoltaOrOlder;
|
||||
@@ -604,7 +616,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_MESA_RADV) {
|
||||
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
|
||||
if (ext_extended_dynamic_state && is_radv) {
|
||||
// Mask driver version variant
|
||||
const u32 version = (properties.driverVersion << 3) >> 3;
|
||||
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
|
||||
@@ -613,6 +626,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
ext_extended_dynamic_state = false;
|
||||
}
|
||||
}
|
||||
if (ext_vertex_input_dynamic_state && is_radv) {
|
||||
// TODO(ameerj): Blacklist only offending driver versions
|
||||
// TODO(ameerj): Confirm if RDNA1 is affected
|
||||
const bool is_rdna2 =
|
||||
IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
|
||||
if (is_rdna2) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
|
||||
ext_vertex_input_dynamic_state = false;
|
||||
}
|
||||
}
|
||||
sets_per_pool = 64;
|
||||
|
||||
const bool is_amd =
|
||||
@@ -628,7 +652,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
has_broken_cube_compatibility = true;
|
||||
}
|
||||
}
|
||||
const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV;
|
||||
const bool is_amd_or_radv = is_amd || is_radv;
|
||||
if (ext_sampler_filter_minmax && is_amd_or_radv) {
|
||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||
if (!is_float16_supported) {
|
||||
@@ -639,6 +663,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
}
|
||||
|
||||
const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
|
||||
const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
|
||||
if (ext_vertex_input_dynamic_state && is_intel_windows) {
|
||||
LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
|
||||
ext_vertex_input_dynamic_state = false;
|
||||
@@ -652,6 +677,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
|
||||
cant_blit_msaa = true;
|
||||
}
|
||||
if (is_intel_anv) {
|
||||
LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
|
||||
must_emulate_bgr565 = true;
|
||||
}
|
||||
|
||||
supports_d24_depth =
|
||||
IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
|
||||
@@ -354,6 +354,10 @@ public:
|
||||
return cant_blit_msaa;
|
||||
}
|
||||
|
||||
bool MustEmulateBGR565() const {
|
||||
return must_emulate_bgr565;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Checks if the physical device is suitable.
|
||||
void CheckSuitability(bool requires_swapchain) const;
|
||||
@@ -448,6 +452,7 @@ private:
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
||||
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
||||
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
|
||||
@@ -77,13 +77,13 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
|
||||
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
@@ -445,6 +445,7 @@ void Config::ReadCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
ReadGlobalSetting(Settings::values.use_multi_core);
|
||||
ReadGlobalSetting(Settings::values.use_extended_memory_layout);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1019,6 +1020,7 @@ void Config::SaveCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
WriteGlobalSetting(Settings::values.use_multi_core);
|
||||
WriteGlobalSetting(Settings::values.use_extended_memory_layout);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
|
||||
ui->use_multi_core->setEnabled(runtime_lock);
|
||||
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
|
||||
ui->use_extended_memory_layout->setEnabled(runtime_lock);
|
||||
ui->use_extended_memory_layout->setChecked(
|
||||
Settings::values.use_extended_memory_layout.GetValue());
|
||||
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
|
||||
@@ -91,6 +94,9 @@ void ConfigureGeneral::ResetDefaults() {
|
||||
void ConfigureGeneral::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core,
|
||||
use_multi_core);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout,
|
||||
ui->use_extended_memory_layout,
|
||||
use_extended_memory_layout);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
@@ -160,6 +166,9 @@ void ConfigureGeneral::SetupPerGameUI() {
|
||||
Settings::values.use_speed_limit, use_speed_limit);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
|
||||
use_multi_core);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout,
|
||||
Settings::values.use_extended_memory_layout,
|
||||
use_extended_memory_layout);
|
||||
|
||||
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
|
||||
ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
|
||||
|
||||
@@ -48,6 +48,7 @@ private:
|
||||
|
||||
ConfigurationShared::CheckState use_speed_limit;
|
||||
ConfigurationShared::CheckState use_multi_core;
|
||||
ConfigurationShared::CheckState use_extended_memory_layout;
|
||||
|
||||
const Core::System& system;
|
||||
};
|
||||
|
||||
@@ -142,6 +142,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_extended_memory_layout">
|
||||
<property name="text">
|
||||
<string>Extended memory layout (6GB DRAM)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_check_exit">
|
||||
<property name="text">
|
||||
|
||||
@@ -934,7 +934,7 @@ void GMainWindow::InitializeHotkeys() {
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
|
||||
LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo"));
|
||||
LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));
|
||||
LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu"));
|
||||
LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));
|
||||
LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation"));
|
||||
@@ -2927,6 +2927,25 @@ void GMainWindow::OnLoadAmiibo() {
|
||||
return;
|
||||
}
|
||||
|
||||
Service::SM::ServiceManager& sm = system->ServiceManager();
|
||||
auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
|
||||
if (nfc == nullptr) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
|
||||
return;
|
||||
}
|
||||
const auto nfc_state = nfc->GetCurrentState();
|
||||
if (nfc_state == Service::NFP::DeviceState::TagFound ||
|
||||
nfc_state == Service::NFP::DeviceState::TagMounted) {
|
||||
nfc->CloseAmiibo();
|
||||
QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (nfc_state != Service::NFP::DeviceState::SearchingForTag) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
|
||||
return;
|
||||
}
|
||||
|
||||
is_amiibo_file_select_active = true;
|
||||
const QString extensions{QStringLiteral("*.bin")};
|
||||
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
|
||||
@@ -2947,6 +2966,15 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove amiibo if one is connected
|
||||
const auto nfc_state = nfc->GetCurrentState();
|
||||
if (nfc_state == Service::NFP::DeviceState::TagFound ||
|
||||
nfc_state == Service::NFP::DeviceState::TagMounted) {
|
||||
nfc->CloseAmiibo();
|
||||
QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
|
||||
return;
|
||||
}
|
||||
|
||||
QFile nfc_file{filename};
|
||||
if (!nfc_file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::warning(this, tr("Error opening Amiibo data file"),
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load &Amiibo...</string>
|
||||
<string>Load/Remove &Amiibo...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Report_Compatibility">
|
||||
|
||||
@@ -266,6 +266,7 @@ void Config::ReadValues() {
|
||||
|
||||
// Core
|
||||
ReadSetting("Core", Settings::values.use_multi_core);
|
||||
ReadSetting("Core", Settings::values.use_extended_memory_layout);
|
||||
|
||||
// Cpu
|
||||
ReadSetting("Cpu", Settings::values.cpu_accuracy);
|
||||
|
||||
Reference in New Issue
Block a user