[hle, kernel] Add support for FW21 and kernel changes (#3004)
- Adapts kernel changes from atmosphere for firmware 21.0.0.
- Fixes launch error of firmware 21.0.0 applets.
- Adds new commands for `prepo` (New `SaveSystemReport` & `SaveSystemReportWithUser`).
- Adds new commands for `IReadOnlyApplicationControlDataInterface` (cmd 19; incomplete!)
- Adds `{12010, nullptr, "SetButtonConfigLeft"},` undocumented IHidServer.
- Adds new commands for `ngc:u` (`Mask2` and `Check2`)
- Adds new commands for system settings server (GetHttpAuthConfig) for webapplet
- Removes incompatible firmware popup warning.
Signed-off-by: lizzie lizzie@eden-emu.dev
Co-authored by: maufeat sahyno1996@gmail.com
Co-authored-by: crueter <crueter@eden-emu.dev>
Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3004
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
parent
b690813196
commit
7d239df065
|
|
@ -95,6 +95,7 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
|||
case SaveDataSpaceId::System:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::User:
|
||||
case SaveDataSpaceId::SdUser:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::Temporary:
|
||||
return "/temp/";
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ namespace HLE::ApiVersion {
|
|||
|
||||
// Horizon OS version constants.
|
||||
|
||||
constexpr u8 HOS_VERSION_MAJOR = 20;
|
||||
constexpr u8 HOS_VERSION_MINOR = 1;
|
||||
constexpr u8 HOS_VERSION_MICRO = 1;
|
||||
constexpr u8 HOS_VERSION_MAJOR = 21;
|
||||
constexpr u8 HOS_VERSION_MINOR = 0;
|
||||
constexpr u8 HOS_VERSION_MICRO = 0;
|
||||
|
||||
// NintendoSDK version constants.
|
||||
|
||||
|
|
@ -24,9 +24,9 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
|
|||
constexpr u8 SDK_REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "9ffad64d79dd150490201461bdf66c8db963f57d";
|
||||
constexpr char DISPLAY_VERSION[] = "20.1.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 20.1.1-1.0";
|
||||
constexpr char VERSION_HASH[] = "f6b2425b6888a66590db104fc734891696e0ecb3";
|
||||
constexpr char DISPLAY_VERSION[] = "21.0.0";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 21.0.0-1.0";
|
||||
|
||||
// Atmosphere version constants.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -8,6 +11,7 @@ namespace Kernel {
|
|||
|
||||
KAutoObject* KAutoObject::Create(KAutoObject* obj) {
|
||||
obj->m_ref_count = 1;
|
||||
obj->m_class_token = obj->GetTypeObj().GetClassToken();
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ protected:
|
|||
return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
|
||||
}
|
||||
|
||||
static constexpr bool IsClassTokenDerivedFrom(ClassTokenType self, ClassTokenType base) {
|
||||
return (self | base) == self;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
ClassTokenType m_class_token;
|
||||
|
|
@ -84,6 +88,7 @@ private:
|
|||
|
||||
public:
|
||||
explicit KAutoObject(KernelCore& kernel) : m_kernel(kernel) {
|
||||
m_class_token = GetStaticTypeObj().GetClassToken();
|
||||
RegisterWithKernel();
|
||||
}
|
||||
virtual ~KAutoObject() = default;
|
||||
|
|
@ -107,11 +112,11 @@ public:
|
|||
}
|
||||
|
||||
bool IsDerivedFrom(const TypeObj& rhs) const {
|
||||
return this->GetTypeObj().IsDerivedFrom(rhs);
|
||||
return TypeObj::IsClassTokenDerivedFrom(m_class_token, rhs.GetClassToken());
|
||||
}
|
||||
|
||||
bool IsDerivedFrom(const KAutoObject& rhs) const {
|
||||
return this->IsDerivedFrom(rhs.GetTypeObj());
|
||||
return TypeObj::IsClassTokenDerivedFrom(m_class_token, rhs.m_class_token);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
|
|
@ -180,6 +185,7 @@ protected:
|
|||
|
||||
private:
|
||||
std::atomic<u32> m_ref_count{};
|
||||
ClassTokenType m_class_token{};
|
||||
};
|
||||
|
||||
class KAutoObjectWithListContainer;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -23,8 +26,8 @@ bool ReadFromUser(KernelCore& kernel, u32* out, KProcessAddress address) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
|
||||
GetCurrentMemory(kernel).Write32(GetInteger(address), *p);
|
||||
bool WriteToUser(KernelCore& kernel, KProcessAddress address, u32 val) {
|
||||
GetCurrentMemory(kernel).Write32(GetInteger(address), val);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +136,7 @@ Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress a
|
|||
|
||||
// Write the value to userspace.
|
||||
Result result{ResultSuccess};
|
||||
if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] {
|
||||
if (WriteToUser(kernel, addr, next_value)) {
|
||||
result = ResultSuccess;
|
||||
} else {
|
||||
result = ResultInvalidCurrentMemory;
|
||||
|
|
@ -207,13 +210,13 @@ void KConditionVariable::SignalImpl(KThread* thread) {
|
|||
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
can_access = true;
|
||||
if (can_access) [[likely]] {
|
||||
if (can_access) {
|
||||
UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
|
||||
Svc::HandleWaitMask);
|
||||
}
|
||||
}
|
||||
|
||||
if (can_access) [[likely]] {
|
||||
if (can_access) {
|
||||
if (prev_tag == Svc::InvalidHandle) {
|
||||
// If nobody held the lock previously, we're all good.
|
||||
thread->EndWait(ResultSuccess);
|
||||
|
|
@ -225,7 +228,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
|
|||
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
|
||||
.ReleasePointerUnsafe();
|
||||
|
||||
if (owner_thread) [[likely]] {
|
||||
if (owner_thread) {
|
||||
// Add the thread as a waiter on the owner.
|
||||
owner_thread->AddWaiter(thread);
|
||||
owner_thread->Close();
|
||||
|
|
@ -261,8 +264,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
|||
|
||||
// If we have no waiters, clear the has waiter flag.
|
||||
if (it == m_tree.end() || it->GetConditionVariableKey() != cv_key) {
|
||||
const u32 has_waiter_flag{};
|
||||
WriteToUser(m_kernel, cv_key, std::addressof(has_waiter_flag));
|
||||
constexpr u32 HasNoWaiterFlag = 0;
|
||||
WriteToUser(m_kernel, cv_key, HasNoWaiterFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -305,13 +308,13 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti
|
|||
|
||||
// Write to the cv key.
|
||||
{
|
||||
const u32 has_waiter_flag = 1;
|
||||
WriteToUser(m_kernel, key, std::addressof(has_waiter_flag));
|
||||
constexpr u32 HasWaiterFlag = 1;
|
||||
WriteToUser(m_kernel, key, HasWaiterFlag);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// Write the value to userspace.
|
||||
if (!WriteToUser(m_kernel, addr, std::addressof(next_value))) {
|
||||
if (!WriteToUser(m_kernel, addr, next_value)) {
|
||||
slp.CancelSleep();
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
Result KHandleTable::Finalize() {
|
||||
void KHandleTable::Finalize() {
|
||||
// Get the table and clear our record of it.
|
||||
u16 saved_table_size = 0;
|
||||
{
|
||||
|
|
@ -22,8 +25,6 @@ Result KHandleTable::Finalize() {
|
|||
obj->Close();
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool KHandleTable::Remove(Handle handle) {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public:
|
|||
return m_max_count;
|
||||
}
|
||||
|
||||
Result Finalize();
|
||||
void Finalize();
|
||||
bool Remove(Handle handle);
|
||||
|
||||
template <typename T = KAutoObject>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -381,6 +384,9 @@ void KScheduler::SwitchThread(KThread* next_thread) {
|
|||
|
||||
// Set the new Thread Local region.
|
||||
// cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress()));
|
||||
|
||||
// Update the thread's cpu time differential in TLS, if relevant.
|
||||
next_thread->UpdateTlsThreadCpuTime(cur_tick);
|
||||
}
|
||||
|
||||
void KScheduler::ScheduleImpl() {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -99,6 +99,12 @@ enum class DpcFlag : u32 {
|
|||
Terminated = (1 << 1),
|
||||
};
|
||||
|
||||
enum class ExceptionFlag : u8 {
|
||||
IsCallingSvc = 1 << 0,
|
||||
InExceptionHandler = 1 << 1,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ExceptionFlag);
|
||||
|
||||
enum class ThreadWaitReasonForDebugging : u32 {
|
||||
None, ///< Thread is not waiting
|
||||
Sleep, ///< Thread is waiting due to a SleepThread SVC
|
||||
|
|
@ -153,7 +159,7 @@ public:
|
|||
|
||||
/**
|
||||
* Sets the thread's current priority.
|
||||
* @param priority The new priority.
|
||||
* @param value The new priority.
|
||||
*/
|
||||
void SetPriority(s32 value) {
|
||||
m_priority = value;
|
||||
|
|
@ -340,6 +346,8 @@ public:
|
|||
void SetInterruptFlag();
|
||||
void ClearInterruptFlag();
|
||||
|
||||
void UpdateTlsThreadCpuTime(s64 switch_tick);
|
||||
|
||||
KThread* GetLockOwner() const;
|
||||
|
||||
const KAffinityMask& GetAffinityMask() const {
|
||||
|
|
@ -446,6 +454,7 @@ public:
|
|||
bool is_pinned;
|
||||
s32 disable_count;
|
||||
KThread* cur_thread;
|
||||
std::atomic<u8> exception_flags{0};
|
||||
};
|
||||
|
||||
StackParameters& GetStackParameters() {
|
||||
|
|
@ -456,6 +465,16 @@ public:
|
|||
return m_stack_parameters;
|
||||
}
|
||||
|
||||
void SetExceptionFlag(ExceptionFlag flag) {
|
||||
GetStackParameters().exception_flags.fetch_or(static_cast<u8>(flag), std::memory_order_relaxed);
|
||||
}
|
||||
void ClearExceptionFlag(ExceptionFlag flag) {
|
||||
GetStackParameters().exception_flags.fetch_and(static_cast<u8>(~static_cast<u8>(flag)), std::memory_order_relaxed);
|
||||
}
|
||||
bool IsExceptionFlagSet(ExceptionFlag flag) const {
|
||||
return (GetStackParameters().exception_flags.load(std::memory_order_relaxed) & static_cast<u8>(flag)) != 0;
|
||||
}
|
||||
|
||||
class QueueEntry {
|
||||
public:
|
||||
constexpr QueueEntry() = default;
|
||||
|
|
@ -511,10 +530,12 @@ public:
|
|||
|
||||
void SetInExceptionHandler() {
|
||||
this->GetStackParameters().is_in_exception_handler = true;
|
||||
SetExceptionFlag(ExceptionFlag::InExceptionHandler);
|
||||
}
|
||||
|
||||
void ClearInExceptionHandler() {
|
||||
this->GetStackParameters().is_in_exception_handler = false;
|
||||
ClearExceptionFlag(ExceptionFlag::InExceptionHandler);
|
||||
}
|
||||
|
||||
bool IsInExceptionHandler() const {
|
||||
|
|
@ -523,10 +544,12 @@ public:
|
|||
|
||||
void SetIsCallingSvc() {
|
||||
this->GetStackParameters().is_calling_svc = true;
|
||||
SetExceptionFlag(ExceptionFlag::IsCallingSvc);
|
||||
}
|
||||
|
||||
void ClearIsCallingSvc() {
|
||||
this->GetStackParameters().is_calling_svc = false;
|
||||
ClearExceptionFlag(ExceptionFlag::IsCallingSvc);
|
||||
}
|
||||
|
||||
bool IsCallingSvc() const {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -34,7 +37,8 @@ Result ClearEvent(Core::System& system, Handle event_handle) {
|
|||
{
|
||||
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
|
||||
if (event.IsNotNull()) {
|
||||
R_RETURN(event->Clear());
|
||||
event->Clear();
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +46,8 @@ Result ClearEvent(Core::System& system, Handle event_handle) {
|
|||
{
|
||||
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
|
||||
if (readable_event.IsNotNull()) {
|
||||
R_RETURN(readable_event->Clear());
|
||||
readable_event->Clear();
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,11 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
|
|||
{190, nullptr, "SendServerMaintenanceOverlayNotification"},
|
||||
{200, nullptr, "GetLastApplicationExitReason"},
|
||||
{210, D<&IApplicationFunctions::GetUnknownEvent210>, "Unknown210"},
|
||||
{220, nullptr, "Unknown220"}, // [20.0.0+]
|
||||
{300, nullptr, "Unknown300"}, // [20.0.0+]
|
||||
{310, nullptr, "Unknown310"}, // [20.0.0+]
|
||||
{320, nullptr, "Unknown320"}, // [20.0.0+]
|
||||
{330, nullptr, "Unknown330"}, // [20.0.0+]
|
||||
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
|
||||
{1000, nullptr, "CreateMovieMaker"},
|
||||
{1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -15,6 +18,7 @@ IAudioController::IAudioController(Core::System& system_)
|
|||
{2, D<&IAudioController::GetLibraryAppletExpectedMasterVolume>, "GetLibraryAppletExpectedMasterVolume"},
|
||||
{3, D<&IAudioController::ChangeMainAppletMasterVolume>, "ChangeMainAppletMasterVolume"},
|
||||
{4, D<&IAudioController::SetTransparentVolumeRate>, "SetTransparentVolumeRate"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -68,6 +71,7 @@ IAudioController::IAudioController(Core::System& system_)
|
|||
{10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
|
||||
{10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||
{10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
|
||||
{10200, nullptr, "Unknown10200"}, // [20.0.0+]
|
||||
{50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
|
||||
{50001, nullptr, "OverrideDefaultTargetForDebug"},
|
||||
{50003, nullptr, "SetForceOverrideExternalDeviceNameForDebug"},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -34,7 +37,8 @@ IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u
|
|||
{17, nullptr, "AcquireAudioOutputDeviceNotification"}, // 17.0.0+
|
||||
{18, nullptr, "ReleaseAudioOutputDeviceNotification"}, // 17.0.0+
|
||||
{19, nullptr, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+
|
||||
{20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"} // 18.0.0+
|
||||
{20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+
|
||||
{21, nullptr, "IsActiveOutputDeviceEstimatedLowLatency"} // 21.0.0+
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
|
|
|
|||
|
|
@ -298,8 +298,41 @@ Result FSP_SRV::OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
|
|||
Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSystem> out_interface,
|
||||
FileSys::SaveDataSpaceId space_id,
|
||||
FileSys::SaveDataAttribute attribute) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
|
||||
R_RETURN(OpenSaveDataFileSystem(out_interface, space_id, attribute));
|
||||
LOG_INFO(Service_FS, "called, space_id={}, {}",
|
||||
space_id, attribute.DebugInfo());
|
||||
|
||||
R_UNLESS(attribute.system_save_data_id != FileSys::InvalidSystemSaveDataId,
|
||||
FileSys::ResultInvalidArgument);
|
||||
|
||||
if (attribute.program_id == 0) {
|
||||
attribute.program_id = program_id;
|
||||
}
|
||||
|
||||
FileSys::VirtualDir dir{};
|
||||
R_TRY(save_data_controller->OpenSaveData(&dir, space_id, attribute));
|
||||
|
||||
FileSys::StorageId id{};
|
||||
switch (space_id) {
|
||||
case FileSys::SaveDataSpaceId::User:
|
||||
id = FileSys::StorageId::NandUser;
|
||||
break;
|
||||
case FileSys::SaveDataSpaceId::SdSystem:
|
||||
case FileSys::SaveDataSpaceId::SdUser:
|
||||
id = FileSys::StorageId::SdCard;
|
||||
break;
|
||||
case FileSys::SaveDataSpaceId::System:
|
||||
id = FileSys::StorageId::NandSystem;
|
||||
break;
|
||||
case FileSys::SaveDataSpaceId::Temporary:
|
||||
case FileSys::SaveDataSpaceId::ProperSystem:
|
||||
case FileSys::SaveDataSpaceId::SafeMode:
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
*out_interface =
|
||||
std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FSP_SRV::OpenReadOnlySaveDataFileSystem(OutInterface<IFileSystem> out_interface,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
|
|||
{20, nullptr, "DeactivateMouse"},
|
||||
{21, nullptr, "SetMouseAutoPilotState"},
|
||||
{22, nullptr, "UnsetMouseAutoPilotState"},
|
||||
{23, nullptr, "AddMouseSideWheelDelta"},
|
||||
{25, nullptr, "SetDebugMouseAutoPilotState"},
|
||||
{26, nullptr, "UnsetDebugMouseAutoPilotState"},
|
||||
{30, nullptr, "DeactivateKeyboard"},
|
||||
|
|
@ -124,6 +125,13 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
|
|||
{250, nullptr, "IsVirtual"},
|
||||
{251, nullptr, "GetAnalogStickModuleParam"},
|
||||
{253, nullptr, "ClearStorageForShipment"}, //19.0.0+
|
||||
{254, nullptr, "Unknown254"},
|
||||
{255, nullptr, "Unknown255"},
|
||||
{256, nullptr, "Unknown256"},
|
||||
{261, nullptr, "UpdateDesignInfo12"},
|
||||
{262, nullptr, "GetUniquePadButtonCount"},
|
||||
{267, nullptr, "Unknown267"},
|
||||
{268, nullptr, "Unknown268"},
|
||||
{301, nullptr, "GetAbstractedPadHandles"},
|
||||
{302, nullptr, "GetAbstractedPadState"},
|
||||
{303, nullptr, "GetAbstractedPadsState"},
|
||||
|
|
@ -154,6 +162,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
|
|||
{650, nullptr, "AddButtonPlayData"},
|
||||
{651, nullptr, "StartButtonPlayData"},
|
||||
{652, nullptr, "StopButtonPlayData"},
|
||||
{700, nullptr, "Unknown700"},
|
||||
{2000, nullptr, "DeactivateDigitizer"},
|
||||
{2001, nullptr, "SetDigitizerAutoPilotState"},
|
||||
{2002, nullptr, "UnsetDigitizerAutoPilotState"},
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
|
|||
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
|
||||
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
|
||||
{310, C<&IHidServer::ResetSevenSixAxisSensorTimestamp>, "ResetSevenSixAxisSensorTimestamp"},
|
||||
{320, nullptr, "EnableNpadImu"}, //21.0.0+
|
||||
{321, nullptr, "DisableNpadImu"}, //21.0.0+
|
||||
{400, C<&IHidServer::IsUsbFullKeyControllerEnabled>, "IsUsbFullKeyControllerEnabled"},
|
||||
{401, nullptr, "EnableUsbFullKeyController"},
|
||||
{402, nullptr, "IsUsbFullKeyControllerConnected"},
|
||||
|
|
@ -187,7 +189,56 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
|
|||
{1002, C<&IHidServer::SetTouchScreenConfiguration>, "SetTouchScreenConfiguration"},
|
||||
{1003, C<&IHidServer::IsFirmwareUpdateNeededForNotification>, "IsFirmwareUpdateNeededForNotification"},
|
||||
{1004, C<&IHidServer::SetTouchScreenResolution>, "SetTouchScreenResolution"},
|
||||
{1270, nullptr, "DeleteButtonConfigStorageRight"},
|
||||
{1271, nullptr, "IsUsingCustomButtonConfig"},
|
||||
{1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
|
||||
{1273, nullptr, "SetAllCustomButtonConfigEnabled"},
|
||||
{1274, nullptr, "SetDefaultButtonConfig"},
|
||||
{1275, nullptr, "SetAllDefaultButtonConfig"},
|
||||
{1276, nullptr, "SetHidButtonConfigEmbedded"},
|
||||
{1277, nullptr, "SetHidButtonConfigFull"},
|
||||
{1278, nullptr, "SetHidButtonConfigLeft"},
|
||||
{1279, nullptr, "SetHidButtonConfigRight"},
|
||||
{1280, nullptr, "GetHidButtonConfigEmbedded"},
|
||||
{1281, nullptr, "GetHidButtonConfigFull"},
|
||||
{1282, nullptr, "GetHidButtonConfigLeft"},
|
||||
{1283, nullptr, "GetHidButtonConfigRight"},
|
||||
{1284, nullptr, "GetButtonConfigStorageEmbedded"},
|
||||
{1285, nullptr, "GetButtonConfigStorageFull"},
|
||||
{1286, nullptr, "GetButtonConfigStorageLeft"},
|
||||
{1287, nullptr, "GetButtonConfigStorageRight"},
|
||||
{1288, nullptr, "SetButtonConfigStorageEmbedded"},
|
||||
{1289, nullptr, "SetButtonConfigStorageFull"},
|
||||
{1290, nullptr, "SetButtonConfigStorageLeft"},
|
||||
{1291, nullptr, "SetButtonConfigStorageRight"},
|
||||
{1308, nullptr, "SetButtonConfigVisible"},
|
||||
{1309, nullptr, "IsButtonConfigVisible"},
|
||||
{1320, nullptr, "WakeTouchScreenUp"},
|
||||
{1321, nullptr, "PutTouchScreenToSleep"},
|
||||
{1322, nullptr, "AcquireTouchScreenAsyncWakeCompletedEvent"},
|
||||
{1323, nullptr, "StartTouchScreenAutoTuneForSystemSettings"},
|
||||
{1324, nullptr, "AcquireTouchScreenAutoTuneCompletedEvent"},
|
||||
{1325, nullptr, "IsTouchScreenAutoTuneRequiredForRepairProviderReplacement"},
|
||||
{1420, nullptr, "GetAppletResourceProperty"},
|
||||
{2000, nullptr, "ActivateDigitizer"},
|
||||
{3000, nullptr, "GetDebugPadGenericPadMap"},
|
||||
{3001, nullptr, "SetDebugPadGenericPadMap"},
|
||||
{3002, nullptr, "ResetDebugPadGenericPadMap"},
|
||||
{3003, nullptr, "GetDebugPadKeyboardMap"},
|
||||
{3004, nullptr, "SetDebugPadKeyboardMap"},
|
||||
{3005, nullptr, "ResetDebugPadKeyboardMap"},
|
||||
{3006, nullptr, "GetFullKeyGenericPadMap"},
|
||||
{3007, nullptr, "SetFullKeyGenericPadMap"},
|
||||
{3008, nullptr, "ResetFullKeyGenericPadMap"},
|
||||
{3009, nullptr, "GetFullKeyKeyboardMap"},
|
||||
{3010, nullptr, "SetFullKeyKeyboardMap"},
|
||||
{3011, nullptr, "ResetFullKeyKeyboardMap"},
|
||||
{3012, nullptr, "GetDebugPadGenericPadMap"}, //21.0.0+
|
||||
{3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+
|
||||
{3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+
|
||||
{3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+
|
||||
{3150, nullptr, "SetMouseLibraryVersion"}, //21.0.0+
|
||||
// What? -- {12010, nullptr, "SetButtonConfigLeft"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
|
@ -96,9 +99,12 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
|||
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
||||
{549, nullptr, "GetConnectableRegisteredDevices"},
|
||||
{551, nullptr, "GetRegisteredDevicesForControllerSupport"},
|
||||
{700, nullptr, "ActivateUniquePad"},
|
||||
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
||||
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
||||
{711, nullptr, "AcquireUniquePadConnectionOnHandheldForNsEventHandle"}, //20.0.0+
|
||||
{712, nullptr, "GetUniquePadColor12"}, //20.0.0+
|
||||
{751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
|
||||
{800, nullptr, "ListSixAxisSensorHandles"},
|
||||
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
|
||||
|
|
@ -225,6 +231,11 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
|||
{1309, nullptr, "IsButtonConfigVisible"}, // 18.0.0+
|
||||
{1320, nullptr, "WakeTouchScreenUp"}, // 17.0.0+
|
||||
{1321, nullptr, "PutTouchScreenToSleep"}, // 17.0.0+
|
||||
{1322, nullptr, "AcquireTouchScreenAsyncWakeCompletedEvent"}, // 20.0.0+
|
||||
{1323, nullptr, "StartTouchScreenAutoTuneForSystemSettings"}, // 21.0.0+
|
||||
{1324, nullptr, "AcquireTouchScreenAutoTuneCompletedEvent"}, // 21.0.0+
|
||||
{1325, nullptr, "IsTouchScreenAutoTuneRequiredForRepairProviderReplacement"}, // 21.0.0+
|
||||
{1326, nullptr, "Unknown1326"}, // 21.0.0+
|
||||
{1420, nullptr, "GetAppletResourceProperty"}, // 19.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -61,6 +64,8 @@ public:
|
|||
{1, &NgcServiceImpl::Check, "Check"},
|
||||
{2, &NgcServiceImpl::Mask, "Mask"},
|
||||
{3, &NgcServiceImpl::Reload, "Reload"},
|
||||
{4, &NgcServiceImpl::Check, "Check2"},
|
||||
{5, &NgcServiceImpl::Mask, "Mask2"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ public:
|
|||
{23, nullptr, "SetKeptInSleep"},
|
||||
{24, nullptr, "RegisterSocketDescriptor"},
|
||||
{25, nullptr, "UnregisterSocketDescriptor"},
|
||||
{26, nullptr, "GetNetworkAccessStatus"}, //21.0.0+
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
|
|
|
|||
|
|
@ -548,8 +548,8 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> o
|
|||
}
|
||||
|
||||
Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground(
|
||||
u64 unk, u64 application_id) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED), app={:016X} unk={}", application_id, unk);
|
||||
u64 control_source, u64 application_id) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED), control_source={} app={:016X}", control_source, application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
|||
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIconSize>, "GetApplicationControlDataWithIconSize"},
|
||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"},
|
||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
@ -163,7 +164,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIconSize(
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u64> out_total_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
|
|
@ -173,19 +174,11 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIc
|
|||
|
||||
constexpr size_t kExpectedBufferSize = 0x14000;
|
||||
constexpr size_t kNACPSize = sizeof(FileSys::RawNACP);
|
||||
constexpr size_t kMaxIconSize = kExpectedBufferSize - kNACPSize;
|
||||
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto size = out_buffer.size();
|
||||
|
||||
if (size < kExpectedBufferSize) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, required={:016X})", size, kExpectedBufferSize);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
// Copy NACP
|
||||
if (control.first != nullptr) {
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
|
||||
|
|
@ -193,17 +186,6 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithIc
|
|||
std::memset(out_buffer.data(), 0, kNACPSize);
|
||||
}
|
||||
|
||||
// Copy icon, pad with zeros if needed
|
||||
size_t icon_size = control.second ? control.second->GetSize() : 0;
|
||||
if (icon_size > kMaxIconSize) {
|
||||
icon_size = kMaxIconSize; // Truncate if too large
|
||||
}
|
||||
if (control.second != nullptr && icon_size > 0) {
|
||||
control.second->Read(out_buffer.data() + kNACPSize, icon_size);
|
||||
} else {
|
||||
std::memset(out_buffer.data() + kNACPSize, 0, kMaxIconSize);
|
||||
}
|
||||
|
||||
*out_total_size = kExpectedBufferSize;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public:
|
|||
Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
u64 application_id);
|
||||
Result GetApplicationControlDataWithIconSize(
|
||||
Result GetApplicationControlDataWithoutIcon(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u64> out_total_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx(IoctlAllocGpfifoEx& params, DeviceFD fd) {
|
|||
params.reserved[2]);
|
||||
|
||||
if (channel_state->initialized) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Already allocated!");
|
||||
LOG_DEBUG(Service_NVDRV, "Channel already initialized; AllocGPFIFOEx returning AlreadyAllocated");
|
||||
return NvResult::AlreadyAllocated;
|
||||
}
|
||||
|
||||
|
|
@ -196,6 +196,15 @@ NvResult nvhost_gpu::AllocGPFIFOEx(IoctlAllocGpfifoEx& params, DeviceFD fd) {
|
|||
program_id = session->process->GetProgramId();
|
||||
}
|
||||
|
||||
// Store program id for later lazy initialization
|
||||
channel_state->program_id = program_id;
|
||||
|
||||
// If address space is not yet bound, defer channel initialization.
|
||||
if (!channel_state->memory_manager) {
|
||||
params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
system.GPU().InitChannel(*channel_state, program_id);
|
||||
|
||||
params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
|
||||
|
|
@ -211,7 +220,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd) {
|
|||
params.reserved[2]);
|
||||
|
||||
if (channel_state->initialized) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Already allocated!");
|
||||
LOG_DEBUG(Service_NVDRV, "Channel already initialized; AllocGPFIFOEx2 returning AlreadyAllocated");
|
||||
return NvResult::AlreadyAllocated;
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +229,15 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx& params, DeviceFD fd) {
|
|||
program_id = session->process->GetProgramId();
|
||||
}
|
||||
|
||||
// Store program id for later lazy initialization
|
||||
channel_state->program_id = program_id;
|
||||
|
||||
// If address space is not yet bound, defer channel initialization.
|
||||
if (!channel_state->memory_manager) {
|
||||
params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
system.GPU().InitChannel(*channel_state, program_id);
|
||||
|
||||
params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
|
||||
|
|
@ -244,9 +262,10 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
|
|||
LOG_DEBUG(Service_NVDRV, "called, class_num={:#X}, flags={:#X}, obj_id={:#X}", params.class_num,
|
||||
params.flags, params.obj_id);
|
||||
|
||||
if (!channel_state || !channel_state->initialized) {
|
||||
LOG_CRITICAL(Service_NVDRV, "No address space bound to allocate a object context!");
|
||||
return NvResult::NotInitialized;
|
||||
// Do not require channel initialization here: some clients allocate contexts before binding.
|
||||
if (!channel_state) {
|
||||
LOG_ERROR(Service_NVDRV, "No channel state available!");
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
|
||||
std::scoped_lock lk(channel_mutex);
|
||||
|
|
@ -268,11 +287,12 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
|
|||
}
|
||||
|
||||
if (ctxObjs[ctx_class_number_index].has_value()) {
|
||||
LOG_ERROR(Service_NVDRV, "Object context for class {:#X} already allocated on this channel",
|
||||
params.class_num);
|
||||
LOG_WARNING(Service_NVDRV, "Object context for class {:#X} already allocated on this channel",
|
||||
params.class_num);
|
||||
return NvResult::AlreadyAllocated;
|
||||
}
|
||||
|
||||
// Defer actual hardware context binding until channel is initialized.
|
||||
ctxObjs[ctx_class_number_index] = params;
|
||||
|
||||
return NvResult::Success;
|
||||
|
|
@ -326,6 +346,11 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
|
|||
|
||||
std::scoped_lock lock(channel_mutex);
|
||||
|
||||
// Lazily initialize channel when address space is available
|
||||
if (!channel_state->initialized && channel_state->memory_manager) {
|
||||
system.GPU().InitChannel(*channel_state, channel_state->program_id);
|
||||
}
|
||||
|
||||
const auto bind_id = channel_state->bind_id;
|
||||
|
||||
auto& flags = params.flags;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -26,14 +29,20 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_)
|
|||
{16, nullptr, "CreateCleanupToDeleteSaveDataArchiveInfoTask"},
|
||||
{17, nullptr, "ListDataInfo"},
|
||||
{18, nullptr, "GetDataInfo"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{19, nullptr, "GetDataInfoCacheUpdateNativeHandleHolder"},
|
||||
{20, nullptr, "CreateSaveDataArchiveInfoCacheForSaveDataBackupUpdationTask"},
|
||||
{21, nullptr, "ListSecondarySaves"},
|
||||
{22, D<&IRemoteStorageController::GetSecondarySave>, "GetSecondarySave"},
|
||||
{23, nullptr, "TouchSecondarySave"},
|
||||
{24, nullptr, "GetSecondarySaveDataInfo"},
|
||||
{25, nullptr, "RegisterDownloadSaveDataTransferTaskForAutonomyRegistration"},
|
||||
{900, nullptr, "Unknown900"},
|
||||
{26, nullptr, "Unknown26"}, //20.0.0+
|
||||
{27, nullptr, "Unknown27"}, //20.0.0+
|
||||
{28, nullptr, "Unknown28"}, //20.0.0+
|
||||
{29, nullptr, "Unknown29"}, //21.0.0+
|
||||
{800, nullptr, "Unknown800"}, //20.0.0+
|
||||
{900, nullptr, "SetLoadedDataMissing"},
|
||||
{901, nullptr, "Unknown901"}, //20.2.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uuid.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
|
@ -27,8 +33,10 @@ public:
|
|||
{10200, &PlayReport::RequestImmediateTransmission, "RequestImmediateTransmission"},
|
||||
{10300, &PlayReport::GetTransmissionStatus, "GetTransmissionStatus"},
|
||||
{10400, &PlayReport::GetSystemSessionId, "GetSystemSessionId"},
|
||||
{20100, &PlayReport::SaveSystemReport, "SaveSystemReport"},
|
||||
{20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
|
||||
{20100, &PlayReport::SaveSystemReportOld, "SaveSystemReport"},
|
||||
{20101, &PlayReport::SaveSystemReportWithUserOld, "SaveSystemReportWithUser"},
|
||||
{20102, &PlayReport::SaveSystemReport, "SaveSystemReport"},
|
||||
{20103, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
|
||||
{20200, nullptr, "SetOperationMode"},
|
||||
{30100, nullptr, "ClearStorage"},
|
||||
{30200, nullptr, "ClearStatistics"},
|
||||
|
|
@ -121,7 +129,7 @@ private:
|
|||
rb.Push(system_session_id);
|
||||
}
|
||||
|
||||
void SaveSystemReport(HLERequestContext& ctx) {
|
||||
void SaveSystemReportOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
|
|
@ -131,14 +139,11 @@ private:
|
|||
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
title_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void SaveSystemReportWithUser(HLERequestContext& ctx) {
|
||||
void SaveSystemReportWithUserOld(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
|
@ -146,10 +151,12 @@ private:
|
|||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
Common::UUID uuid{};
|
||||
std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
|
||||
"data2_size={:016X}",
|
||||
user_id[1], user_id[0], title_id, data1.size(), data2.size());
|
||||
"called, user_id={}, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
uuid.FormattedString(), title_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2},
|
||||
|
|
@ -158,6 +165,53 @@ private:
|
|||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
// (21.0.0+) buffers: [0x9 (X), 0x5 (A)], inbytes: 0x10
|
||||
void SaveSystemReport(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto field0 = rp.PopRaw<u64>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data_x = ctx.ReadBufferX(0);
|
||||
const auto data_a = ctx.ReadBufferA(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, field0={}, title_id={:016X}, data_a_size={}, data_x_size={}",
|
||||
field0, title_id, data_a.size(), data_x.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data_a, data_x});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
// (21.0.0+) buffers: [0x9 (X), 0x5 (A)], inbytes: 0x20
|
||||
void SaveSystemReportWithUser(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
// 21.0.0+: field0 (u64), user_id (u128), title_id (u64)
|
||||
const auto field0 = rp.PopRaw<u64>();
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data_x = ctx.ReadBufferX(0);
|
||||
const auto data_a = ctx.ReadBufferA(0);
|
||||
|
||||
Common::UUID uuid{};
|
||||
std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, user_id={}, field0={:016X}, title_id={:016X}, data_a_size={}, data_x_size={}",
|
||||
uuid.FormattedString(), field0, title_id, data_a.size(), data_x.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data_a, data_x},
|
||||
std::nullopt, user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -18,6 +21,8 @@ IFirmwareDebugSettingsServer::IFirmwareDebugSettingsServer(Core::System& system_
|
|||
{21, nullptr, "SetAllowedSslHosts"},
|
||||
{22, nullptr, "SetHostFsMountPoint"},
|
||||
{23, nullptr, "SetMemoryUsageRateFlag"},
|
||||
{24, nullptr, "CommitSettings"}, //20.0.0+
|
||||
{27, nullptr, "SetHttpAuthConfigs"}, //21.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -307,6 +307,19 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
|
|||
{222, nullptr, "SetForceMonauralOutputFlag"}, //17.0.0+
|
||||
{251, nullptr, "GetAccountIdentificationSettings"}, //18.0.0+
|
||||
{252, nullptr, "SetAccountIdentificationSettings"}, //18.0.0+
|
||||
{263, nullptr, "AcquireVphymDirtyFlagEventHandle"}, //20.0.0+
|
||||
{264, nullptr, "GetVphymDirtyFlags"}, //20.0.0+
|
||||
{282, nullptr, "ConvertToProductModel"}, //20.0.0+
|
||||
{283, nullptr, "ConvertToProductModelName"}, //20.0.0+
|
||||
{289, nullptr, "GetDefaultAccountIdentificationFlagSet"}, //20.0.0+
|
||||
{300, nullptr, "AcquirePushNotificationDirtyFlagEventHandle"}, //20.0.0+
|
||||
{301, nullptr, "GetPushNotificationDirtyFlags"}, //20.0.0+
|
||||
{306, nullptr, "GetPinCodeReregistrationGuideAccounts"}, //20.0.0+
|
||||
{307, nullptr, "SetPinCodeReregistrationGuideAccounts"}, //20.0.0+
|
||||
{315, C<&ISystemSettingsServer::GetHttpAuthConfigs>, "GetHttpAuthConfigs"}, //21.0.0+
|
||||
{319, nullptr, "GetAccountUserSettings"}, //21.0.0+
|
||||
{320, nullptr, "SetAccountUserSettings"}, //21.0.0+
|
||||
{321, nullptr, "GetDefaultAccountUserSettings"}, //21.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
@ -1323,6 +1336,12 @@ Result ISystemSettingsServer::SetPanelCrcMode(s32 panel_crc_mode) {
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_configs) {
|
||||
LOG_WARNING(Service_SET, "(STUBBED) called, buffer_size={}", out_configs.size());
|
||||
*out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void ISystemSettingsServer::SetupSettings() {
|
||||
auto system_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -157,6 +160,7 @@ public:
|
|||
Result GetFieldTestingFlag(Out<bool> out_field_testing_flag);
|
||||
Result GetPanelCrcMode(Out<s32> out_panel_crc_mode);
|
||||
Result SetPanelCrcMode(s32 panel_crc_mode);
|
||||
Result GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_configs);
|
||||
|
||||
private:
|
||||
bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func);
|
||||
|
|
|
|||
|
|
@ -121,18 +121,12 @@ FirmwareManager::FirmwareCheckResult FirmwareManager::VerifyFirmware(Core::Syste
|
|||
return ErrorFirmwareMissing;
|
||||
} else {
|
||||
const auto pair = GetFirmwareVersion(system);
|
||||
const auto firmware_data = pair.first;
|
||||
const auto result = pair.second;
|
||||
|
||||
if (result.IsError()) {
|
||||
LOG_INFO(Frontend, "Unable to read firmware");
|
||||
return ErrorFirmwareCorrupted;
|
||||
}
|
||||
|
||||
// TODO: update this whenever newer firmware is properly supported
|
||||
if (firmware_data.major > 20) {
|
||||
return ErrorFirmwareTooNew;
|
||||
}
|
||||
}
|
||||
|
||||
return FirmwareGood;
|
||||
|
|
|
|||
|
|
@ -53,21 +53,10 @@ inline constexpr bool GameRequiresFirmware(u64 program_id)
|
|||
!= FIRMWARE_REQUIRED_GAMES.end();
|
||||
}
|
||||
|
||||
|
||||
enum FirmwareCheckResult {
|
||||
FirmwareGood,
|
||||
ErrorFirmwareMissing,
|
||||
ErrorFirmwareCorrupted,
|
||||
ErrorFirmwareTooNew,
|
||||
};
|
||||
|
||||
static constexpr std::array<const char *, 4> FIRMWARE_CHECK_STRINGS = {
|
||||
"",
|
||||
"Firmware missing. Firmware is required to run certain games and use the Home Menu. "
|
||||
"Eden only works with firmware 19.0.1 and earlier.",
|
||||
"Firmware reported as present, but was unable to be read. Check for decryption keys and "
|
||||
"redump firmware if necessary.",
|
||||
"Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier.",
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -100,16 +89,6 @@ inline bool CheckFirmwarePresence(Core::System &system)
|
|||
*/
|
||||
FirmwareCheckResult VerifyFirmware(Core::System &system);
|
||||
|
||||
/**
|
||||
* \brief Get a string representation of a result from CheckFirmwareVersion.
|
||||
* \param result The result code.
|
||||
* \return A string representation of the passed result code.
|
||||
*/
|
||||
inline constexpr const char *GetFirmwareCheckString(FirmwareCheckResult result)
|
||||
{
|
||||
return FIRMWARE_CHECK_STRINGS.at(static_cast<std::size_t>(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the currently installed firmware version.
|
||||
* @param system The system to check firmware on.
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
/// Small helper to look up enums.
|
||||
/// res = the result code
|
||||
/// base = the base matching value in the StringKey table
|
||||
#define LOOKUP_ENUM(res, base) StringLookup::Lookup( \
|
||||
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::base))
|
||||
#define LOOKUP_ENUM(res, base) QtCommon::StringLookup::Lookup( \
|
||||
QtCommon::StringLookup::StringKey((int) res + (int) QtCommon::StringLookup::base))
|
||||
|
||||
namespace QtCommon::StringLookup {
|
||||
|
||||
|
|
@ -20,11 +20,11 @@ Q_NAMESPACE
|
|||
|
||||
// TODO(crueter): QML interface
|
||||
enum StringKey {
|
||||
SavesTooltip,
|
||||
ShadersTooltip,
|
||||
UserNandTooltip,
|
||||
SysNandTooltip,
|
||||
ModsTooltip,
|
||||
DataManagerSavesTooltip,
|
||||
DataManagerShadersTooltip,
|
||||
DataManagerUserNandTooltip,
|
||||
DataManagerSysNandTooltip,
|
||||
DataManagerModsTooltip,
|
||||
|
||||
// Key install results
|
||||
KeyInstallSuccess,
|
||||
|
|
@ -40,6 +40,10 @@ enum StringKey {
|
|||
FwInstallFailedCopy,
|
||||
FwInstallFailedCorrupted,
|
||||
|
||||
// Firmware Check results
|
||||
FwCheckErrorFirmwareMissing,
|
||||
FwCheckErrorFirmwareCorrupted,
|
||||
|
||||
// user data migrator
|
||||
MigrationPromptPrefix,
|
||||
MigrationPrompt,
|
||||
|
|
@ -55,18 +59,19 @@ enum StringKey {
|
|||
KvdbMisaligned,
|
||||
KvdbNoImens,
|
||||
RyujinxNoSaveId,
|
||||
|
||||
};
|
||||
|
||||
static const constexpr frozen::map<StringKey, frozen::string, 27> strings = {
|
||||
// NB: the constexpr check always succeeds (in clangd at least) if size arg < size
|
||||
// always triple-check the size arg
|
||||
static const constexpr frozen::map<StringKey, frozen::string, 29> strings = {
|
||||
// 0-4
|
||||
{SavesTooltip,
|
||||
{DataManagerSavesTooltip,
|
||||
QT_TR_NOOP("Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING!")},
|
||||
{ShadersTooltip,
|
||||
{DataManagerShadersTooltip,
|
||||
QT_TR_NOOP("Contains Vulkan and OpenGL pipeline caches. Generally safe to remove.")},
|
||||
{UserNandTooltip, QT_TR_NOOP("Contains updates and DLC for games.")},
|
||||
{SysNandTooltip, QT_TR_NOOP("Contains firmware and applet data.")},
|
||||
{ModsTooltip, QT_TR_NOOP("Contains game mods, patches, and cheats.")},
|
||||
{DataManagerUserNandTooltip, QT_TR_NOOP("Contains updates and DLC for games.")},
|
||||
{DataManagerSysNandTooltip, QT_TR_NOOP("Contains firmware and applet data.")},
|
||||
{DataManagerModsTooltip, QT_TR_NOOP("Contains game mods, patches, and cheats.")},
|
||||
|
||||
// Key install
|
||||
// 5-9
|
||||
|
|
@ -91,8 +96,17 @@ static const constexpr frozen::map<StringKey, frozen::string, 27> strings = {
|
|||
"Firmware installation cancelled, firmware may be in a bad state or corrupted. Restart "
|
||||
"Eden or re-install firmware.")},
|
||||
|
||||
{FwCheckErrorFirmwareMissing,
|
||||
QT_TR_NOOP(
|
||||
"Firmware missing. Firmware is required to run certain games and use the Home Menu. "
|
||||
"Versions 19.0.1 or earlier are recommended, as 20.0.0+ is currently experimental.")},
|
||||
{FwCheckErrorFirmwareCorrupted,
|
||||
QT_TR_NOOP(
|
||||
"Firmware reported as present, but was unable to be read. Check for decryption keys and "
|
||||
"redump firmware if necessary.")},
|
||||
|
||||
// migrator
|
||||
// 15-20
|
||||
// 17-22
|
||||
{MigrationPromptPrefix, QT_TR_NOOP("Eden has detected user data for the following emulators:")},
|
||||
{MigrationPrompt,
|
||||
QT_TR_NOOP("Would you like to migrate your data for use in Eden?\n"
|
||||
|
|
@ -113,7 +127,7 @@ static const constexpr frozen::map<StringKey, frozen::string, 27> strings = {
|
|||
"This is recommended if you want to share data between emulators.")},
|
||||
|
||||
// why am I writing these comments again
|
||||
// 21-26
|
||||
// 23-28
|
||||
{KvdbNonexistent, QT_TR_NOOP("Ryujinx title database does not exist.")},
|
||||
{KvdbNoHeader, QT_TR_NOOP("Invalid header on Ryujinx title database.")},
|
||||
{KvdbInvalidMagic, QT_TR_NOOP("Invalid magic header on Ryujinx title database.")},
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ DataDialog::DataDialog(QWidget *parent)
|
|||
// TODO: Should we make this a single widget that pulls data from a model?
|
||||
#define WIDGET(label, name) \
|
||||
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
|
||||
QtCommon::StringLookup::name##Tooltip, \
|
||||
QtCommon::StringLookup::DataManager##name##Tooltip, \
|
||||
QStringLiteral(#name), \
|
||||
this)); \
|
||||
ui->labels->addItem(label);
|
||||
|
|
@ -80,39 +80,27 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
|
|||
|
||||
void DataWidget::clear()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
std::string user_id = selectProfile();
|
||||
QtCommon::Content::ClearDataDir(m_dir, user_id);
|
||||
scan();
|
||||
}
|
||||
|
||||
void DataWidget::open()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
std::string user_id = selectProfile();
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
||||
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id))));
|
||||
}
|
||||
|
||||
void DataWidget::upload()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
std::string user_id = selectProfile();
|
||||
QtCommon::Content::ExportDataDir(m_dir, user_id, m_exportName);
|
||||
}
|
||||
|
||||
void DataWidget::download()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
std::string user_id = selectProfile();
|
||||
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this));
|
||||
}
|
||||
|
||||
|
|
@ -131,3 +119,13 @@ void DataWidget::scan() {
|
|||
watcher->setFuture(
|
||||
QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); }));
|
||||
}
|
||||
|
||||
std::string DataWidget::selectProfile()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
|
||||
return user_id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// Qt on macOS doesn't define VMA shit
|
||||
#include "qt_common/qt_string_lookup.h"
|
||||
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
||||
#undef VMA_IMPLEMENTATION
|
||||
#endif
|
||||
|
|
@ -4058,35 +4059,18 @@ void MainWindow::OnOpenControllerMenu() {
|
|||
void MainWindow::OnHomeMenu() {
|
||||
auto result = FirmwareManager::VerifyFirmware(*QtCommon::system.get());
|
||||
|
||||
using namespace QtCommon::StringLookup;
|
||||
|
||||
switch (result) {
|
||||
case FirmwareManager::ErrorFirmwareMissing:
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use the Home Menu."));
|
||||
Lookup(FwCheckErrorFirmwareMissing));
|
||||
return;
|
||||
case FirmwareManager::ErrorFirmwareCorrupted:
|
||||
QMessageBox::warning(this, tr("Firmware Corrupted"),
|
||||
tr(FirmwareManager::GetFirmwareCheckString(result)));
|
||||
Lookup(FwCheckErrorFirmwareCorrupted));
|
||||
return;
|
||||
case FirmwareManager::ErrorFirmwareTooNew: {
|
||||
if (!UISettings::values.show_fw_warning.GetValue()) break;
|
||||
|
||||
QMessageBox box(QMessageBox::Warning,
|
||||
tr("Firmware Too New"),
|
||||
tr(FirmwareManager::GetFirmwareCheckString(result)) + tr("\nContinue anyways?"),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
this);
|
||||
|
||||
QCheckBox *checkbox = new QCheckBox(tr("Don't show again"));
|
||||
box.setCheckBox(checkbox);
|
||||
|
||||
int button = box.exec();
|
||||
if (checkbox->isChecked()) {
|
||||
UISettings::values.show_fw_warning.SetValue(false);
|
||||
}
|
||||
|
||||
if (button == static_cast<int>(QMessageBox::No)) return;
|
||||
break;
|
||||
} default:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue