Add per-profile native solo routing, stick/click swapping, SL/SR output mappings, and digital left-stick directions with analog priority. Extend both codecs to schema 11 while preserving legacy profiles, expose the controls and preview in Studio, and carry rail outputs through Pro emulation. Verified with 541 tests, firmware builds, editor checks, and saved-profile preservation during native 0.89 deployment.
113 lines
4.5 KiB
Python
113 lines
4.5 KiB
Python
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
from dataclasses import replace
|
|
|
|
from switch_pico_bridge import config_manager
|
|
|
|
|
|
def test_controller_profile_native(tmp_path: Path) -> None:
|
|
root = Path(__file__).resolve().parents[1]
|
|
compiler = shutil.which("c++") or shutil.which("g++")
|
|
assert compiler is not None, "a host C++ compiler is required"
|
|
|
|
executable = tmp_path / "controller_profile_test"
|
|
subprocess.run(
|
|
[
|
|
compiler,
|
|
"-std=c++17",
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror",
|
|
"-pedantic",
|
|
f"-I{root / 'src' / 'firmware'}",
|
|
str(root / "tests" / "controller_profile_test.cpp"),
|
|
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
|
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
|
"-o",
|
|
str(executable),
|
|
],
|
|
check=True,
|
|
cwd=root,
|
|
)
|
|
subprocess.run([str(executable)], check=True, cwd=root)
|
|
|
|
legacy = subprocess.run(
|
|
[str(executable), "--legacy9"], check=True, cwd=root, capture_output=True
|
|
).stdout
|
|
migrated = config_manager.ControllerProfile.from_bytes(legacy)
|
|
assert migrated.to_bytes()[2:] == legacy[2:]
|
|
assert migrated.native_joycon_layout == 0
|
|
assert migrated.swap_sticks is False
|
|
assert migrated.swing.macro == 0
|
|
assert migrated.nunchuk_swing.button == 15
|
|
assert migrated.combined_swing.macro == 3
|
|
upgraded = subprocess.run(
|
|
[str(executable), "--codec"],
|
|
input=legacy, check=True, cwd=root, capture_output=True,
|
|
).stdout
|
|
assert upgraded == migrated.to_bytes()
|
|
|
|
legacy10 = subprocess.run(
|
|
[str(executable), "--legacy10"], check=True, cwd=root, capture_output=True
|
|
).stdout
|
|
migrated10 = config_manager.ControllerProfile.from_bytes(legacy10)
|
|
assert legacy10[:4] == bytes((10, 0, 128, 1))
|
|
assert migrated10.native_joycon_layout == 2
|
|
assert migrated10.swap_sticks is True
|
|
assert migrated10.button_map[0] == 18
|
|
assert migrated10.extra_button_map[0] == 19
|
|
assert migrated10.shift.button_map[0] == 20
|
|
assert migrated10.shift.extra_button_map[0] == 21
|
|
assert migrated10.left_trigger.output == migrated10.right_trigger.output == 21
|
|
assert migrated10.to_bytes()[2:] == legacy10[2:]
|
|
upgraded10 = subprocess.run(
|
|
[str(executable), "--codec"],
|
|
input=legacy10, check=True, cwd=root, capture_output=True,
|
|
).stdout
|
|
assert upgraded10 == migrated10.to_bytes()
|
|
|
|
directions = replace(
|
|
migrated10,
|
|
button_map=(*migrated10.button_map[:12], 22, 23, 24, 25),
|
|
extra_button_map=(24, *migrated10.extra_button_map[1:]),
|
|
shift=replace(
|
|
migrated10.shift,
|
|
button_map=(23, *migrated10.shift.button_map[1:]),
|
|
extra_button_map=(25, *migrated10.shift.extra_button_map[1:]),
|
|
),
|
|
left_trigger=replace(migrated10.left_trigger, output=22),
|
|
right_trigger=replace(migrated10.right_trigger, output=22),
|
|
)
|
|
direction_wire = directions.to_bytes()
|
|
native_directions = subprocess.run(
|
|
[str(executable), "--codec"],
|
|
input=direction_wire, check=True, cwd=root, capture_output=True,
|
|
).stdout
|
|
assert native_directions == direction_wire
|
|
assert config_manager.ControllerProfile.from_bytes(native_directions) == directions
|
|
assert config_manager.ControllerProfile.from_json(directions.to_json()) == directions
|
|
|
|
for layout in range(len(config_manager.NATIVE_JOYCON_LAYOUTS)):
|
|
for swap in (False, True):
|
|
profile = replace(
|
|
migrated,
|
|
native_joycon_layout=layout,
|
|
swap_sticks=swap,
|
|
button_map=(18, *migrated.button_map[1:]),
|
|
extra_button_map=(19, *migrated.extra_button_map[1:]),
|
|
shift=replace(
|
|
migrated.shift,
|
|
button_map=(20, *migrated.shift.button_map[1:]),
|
|
extra_button_map=(21, *migrated.shift.extra_button_map[1:]),
|
|
),
|
|
left_trigger=replace(migrated.left_trigger, output=21),
|
|
right_trigger=replace(migrated.right_trigger, output=21),
|
|
)
|
|
encoded = profile.to_bytes()
|
|
native = subprocess.run(
|
|
[str(executable), "--codec"],
|
|
input=encoded, check=True, cwd=root, capture_output=True,
|
|
).stdout
|
|
assert native == encoded
|
|
assert config_manager.ControllerProfile.from_bytes(native) == profile
|