mirror of
https://github.com/niakw/Prospero.Eden-Encore.git
synced 2026-10-27 09:01:48 +01:00
120 lines
5.8 KiB
Python
120 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Host-compile the actual controller mapping contract, with no PS5 app build.
|
|
|
|
Protects immutable global/game-specific emulator-input semantics independently of in-game glyph artwork.
|
|
This cannot fix a game's own Nintendo prompt textures or applet UI.
|
|
"""
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
# Source contract: default PlayStation mapping must keep the previously
|
|
# qualified in-match auto selection. The 2026-10-08 regression was a single
|
|
# launch-time SetAdaptivePlayStation(false) despite a PlayStation profile.
|
|
launch = (ROOT / "headless/main.cpp").read_text()
|
|
pad_runtime = (ROOT / "headless/pad.cpp").read_text()
|
|
assert "pad->SetAdaptivePlayStation" not in launch
|
|
assert "mapping_context" not in pad_runtime
|
|
assert "adaptive_playstation" not in pad_runtime
|
|
assert '(effective_layout == 1 ? "Switch" : "PlayStation")' in launch
|
|
assert "Eden::ResolveSessionButtonMapping(" in launch
|
|
assert "Eden::ResolveGameTitleId(rom_metadata_id, selected_game)" in launch
|
|
assert "const int pad = mapping[game];" in pad_runtime
|
|
# A session's guest face buttons are no longer reinterpreted after menu
|
|
# navigation, stick motion, cutscenes or a match starts.
|
|
assert "kButtonTouchPad | kButtonSquare" not in pad_runtime
|
|
assert "kSwitchMapping" not in pad_runtime
|
|
CXX = next((x for x in ("clang++-18", "clang++", "g++") if shutil.which(x)), None)
|
|
if not CXX:
|
|
raise SystemExit("C++20 compiler is required for button mapping regression")
|
|
|
|
code = r"""
|
|
#include "button_mapping.h"
|
|
#include <cassert>
|
|
#include <array>
|
|
#include <string_view>
|
|
using namespace Eden;
|
|
|
|
void check_bijection(const ButtonMapping& m) {
|
|
assert(ValidMapping(m));
|
|
for (int game = 0; game < kGameButtons; ++game)
|
|
assert(MappedTo(m, m[game]) == game);
|
|
}
|
|
|
|
int main() {
|
|
static_assert(kGameButtons == 12);
|
|
static_assert(kPadButtons == 13);
|
|
static_assert(kPlayStationMapping[game_a] == pad_cross);
|
|
static_assert(kPlayStationMapping[game_b] == pad_circle);
|
|
static_assert(kPlayStationMapping[game_x] == pad_square);
|
|
static_assert(kPlayStationMapping[game_y] == pad_triangle);
|
|
static_assert(kSwitchMapping[game_a] == pad_circle);
|
|
static_assert(kSwitchMapping[game_b] == pad_cross);
|
|
static_assert(kSwitchMapping[game_x] == pad_triangle);
|
|
static_assert(kSwitchMapping[game_y] == pad_square);
|
|
// In-game visual packs carry guest-action art, not hardware pin positions.
|
|
// Only a fixed Cross-confirm session admits PS action glyphs.
|
|
const auto ps_session = ResolveSessionButtonMapping(0, kPlayStationMapping, -1, false, kPlayStationMapping);
|
|
const auto switch_session = ResolveSessionButtonMapping(1, kSwitchMapping, -1, false, kSwitchMapping);
|
|
const auto switch_title = ResolveSessionButtonMapping(0, kPlayStationMapping, 1, false, kSwitchMapping);
|
|
auto custom_buttons = kPlayStationMapping;
|
|
custom_buttons = Assign(custom_buttons, game_a, pad_circle);
|
|
const auto custom_session = ResolveSessionButtonMapping(0, kPlayStationMapping, 0, true, custom_buttons);
|
|
assert(CompatibleWithPlayStationActionGlyphs(ps_session));
|
|
assert(!CompatibleWithPlayStationActionGlyphs(switch_session));
|
|
assert(!CompatibleWithPlayStationActionGlyphs(switch_title));
|
|
assert(!CompatibleWithPlayStationActionGlyphs(custom_session));
|
|
assert(BaseMappingForLayout(0) == kPlayStationMapping);
|
|
assert(BaseMappingForLayout(1) == kSwitchMapping);
|
|
assert(!MappingIsCustom(kPlayStationMapping, 0));
|
|
assert(!MappingIsCustom(kSwitchMapping, 1));
|
|
assert(MappingIsCustom(kPlayStationMapping, 1));
|
|
|
|
for (const auto& base : {kPlayStationMapping, kSwitchMapping}) {
|
|
check_bijection(base);
|
|
// Every valid reassignment swaps the two guest functions, without
|
|
// accidentally aliasing another action to the same physical key.
|
|
for (int guest = 0; guest < kGameButtons; ++guest) {
|
|
for (int target = 0; target < kPadButtons; ++target) {
|
|
const auto original = base;
|
|
auto candidate = Assign(base, guest, target);
|
|
check_bijection(candidate);
|
|
assert(candidate[guest] == target);
|
|
const int original_guest = MappedTo(original, target);
|
|
if (original_guest >= 0 && original_guest != guest)
|
|
assert(candidate[original_guest] == original[guest]);
|
|
for (int other = 0; other < kGameButtons; ++other)
|
|
if (other != guest && other != original_guest)
|
|
assert(candidate[other] == original[other]);
|
|
}
|
|
}
|
|
}
|
|
const auto unchanged = kDefaultMapping;
|
|
assert(Assign(unchanged, -1, pad_cross) == unchanged);
|
|
assert(Assign(unchanged, kGameButtons, pad_cross) == unchanged);
|
|
assert(Assign(unchanged, game_a, -1) == unchanged);
|
|
assert(Assign(unchanged, game_a, kPadButtons) == unchanged);
|
|
auto duplicate = unchanged;
|
|
duplicate[game_b] = duplicate[game_a];
|
|
assert(!ValidMapping(duplicate));
|
|
auto out_of_range = unchanged;
|
|
out_of_range[game_a] = kPadButtons;
|
|
assert(!ValidMapping(out_of_range));
|
|
|
|
// Input mapping changes guest *actions*, never the game's artwork.
|
|
// The semantic test does not inspect or replace texture assets.
|
|
}
|
|
"""
|
|
with tempfile.TemporaryDirectory(prefix="eden-controller-semantics-") as folder:
|
|
tmp = Path(folder)
|
|
source, exe = tmp / "test.cpp", tmp / "test"
|
|
source.write_text(code)
|
|
subprocess.run([CXX, "-std=c++20", "-O2", "-Wall", "-Wextra", "-Werror",
|
|
"-I", str(ROOT / "headless"), str(source), "-o", str(exe)],
|
|
check=True)
|
|
subprocess.run([str(exe)], check=True)
|
|
print("PASS controller PS5/Switch mapping semantics, bijection, custom swaps, invalid inputs")
|
|
print("In-game glyph replacement and FC27 scene-specific prompts: NOT TESTED")
|