hid_core: Add NpadCondition to shared memory to fix no-controller crash

Fix crash when launching games without a connected controller by adding
a global NpadCondition structure at offset 0x3E200 in HID shared memory.
Games read this structure on startup to verify controller state validity,
and crash with a userspace PANIC if the data is uninitialized.

The NpadCondition structure contains:
- is_initialized flag (set to 1)
- hold_type (controller orientation)
- is_valid flag (set to 1)

These fields are properly initialized when shared memory is created,
ensuring games see valid controller state data even before any
controllers are physically connected.

Fixes crash in The Legend of Zelda: Echoes of Wisdom and other titles
that validate controller state on startup.

Based on LotP's implementation of the NpadCondition structure.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-10-12 16:49:03 +10:00
parent bc81e53c53
commit a15719b21d
2 changed files with 21 additions and 2 deletions

View File

@@ -1,4 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Zephyron, LotP
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -159,6 +161,15 @@ struct NpadGcTriggerState {
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::NpadCondition (global controller condition structure)
struct NpadCondition {
u32 _00{};
u32 is_initialized{1}; // Must be 1 to prevent crashes
u32 hold_type{static_cast<u32>(NpadJoyHoldType::Horizontal)}; // Store enum as u32
u32 is_valid{1}; // Must be 1 to prevent crashes
};
static_assert(sizeof(NpadCondition) == 0x10, "NpadCondition is an invalid size");
// This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
union {

View File

@@ -1,4 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Zephyron, LotP
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -201,7 +203,10 @@ static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
// This is nn::hid::detail::SharedMemoryFormat
struct SharedMemoryFormat {
void Initialize() {}
void Initialize() {
// Initialize the global NpadCondition to prevent crashes when no controller is connected
npad_condition = NpadCondition{};
}
DebugPadSharedMemoryFormat debug_pad;
TouchScreenSharedMemoryFormat touch_screen;
@@ -218,7 +223,9 @@ struct SharedMemoryFormat {
ConsoleSixAxisSensorSharedMemoryFormat console;
INSERT_PADDING_BYTES(0x19E0);
MouseSharedMemoryFormat debug_mouse;
INSERT_PADDING_BYTES(0x2000);
INSERT_PADDING_BYTES(0x200);
NpadCondition npad_condition;
INSERT_PADDING_BYTES(0x1DF0);
};
static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
@@ -236,6 +243,7 @@ static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offs
static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
static_assert(offsetof(SharedMemoryFormat, npad_condition) == 0x3E200, "npad_condition has wrong offset");
static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");
} // namespace Service::HID