Merge pull request #439 from outis1one/claude/wolf-pair-port-conflict-7nz8qg

Claude/wolf pair port conflict 7nz8qg
This commit is contained in:
Outis
2026-09-03 20:52:47 -04:00
committed by GitHub
+159 -1
View File
@@ -3554,7 +3554,7 @@ _manage_wolf_complete() {
local commands="start stop restart logs status pin controllers update apps cores reorder
add-web ge-proton games setup-swbf2 fix-ea-game wait-ea-app
install-ea-app diagnose-ea fix-perms install-completion backup
steam-add-nonsteam-game"
steam-add-nonsteam-game cemu-clone-controller"
COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
}
# Register for both 'manage.sh' and './manage.sh' invocation styles
@@ -4014,6 +4014,115 @@ VDFPY
echo "'Force the use of a specific Steam Play compatibility tool' is OFF — Cemu and"
echo "other native Linux AppImages don't run through Proton."
;;
cemu-clone-controller)
# Clones a WORKING Cemu controller mapping onto a new device slot,
# skipping Cemu's own Input Settings dialog entirely for that slot.
#
# Why this is safe, not just convenient: confirmed against two real,
# separately-hand-mapped profiles captured live (Nintendo Switch Pro
# on controller0.xml, Xbox One S on controller1.xml) — both contain
# the exact same 24 <mapping>/<button> pairs, just listed in a
# different order. That's real proof (not the README's word for
# it) that Cemu's <mappings> block uses universal
# SDL_CONTROLLER_BUTTON_* semantics, not per-device raw indices, and
# that Cemu doesn't care about entry order — so a proven-working
# block can be dropped into a new device's file wholesale.
#
# This still needs ONE real, Cemu-written <uuid> per new device
# type first — that value isn't something this script can compute
# (it depends on the CRC16-of-device-name half of the SDL GUID,
# which SDL derives from the exact joystick name string the device
# reports at runtime — guessing it risks writing a uuid Cemu will
# never actually match against the live device). Get it the
# cheapest possible way: in Cemu's Input Settings for the new
# controller slot, click + and select the device — even mapping
# ONE button and hitting Save is enough, since only the <uuid> and
# <display_name> this writes are kept; the whole <mappings> block
# gets replaced by this command anyway.
if [ -z "${3:-}" ]; then
echo "Usage: ./manage.sh cemu-clone-controller <target-slot 0-3> <uuid> [display name]"
echo ""
echo " <target-slot> Which controllerN.xml to write (0-indexed, same as"
echo " Cemu's own file naming and Wolf's own controller"
echo " numbering — controller0.xml is Cemu's Controller 1)."
echo " <uuid> The REAL uuid Cemu wrote for the new device — get this"
echo " by adding it in Cemu's Input Settings once (map just one"
echo " button, Save), then:"
echo " cat controllerProfiles/controllerN.xml | grep uuid"
echo " [display name] Cosmetic only. Defaults to the source profile's own name."
exit 1
fi
_CCC_SLOT="${2:-}"
_CCC_UUID="${3:-}"
_CCC_NAME="${4:-}"
case "$_CCC_SLOT" in
0|1|2|3) ;;
*) echo "Target slot must be 0, 1, 2, or 3 (Cemu's own Controller 1-4, 0-indexed)."; exit 1 ;;
esac
case "$_CCC_UUID" in
0_????????????????????????????????) ;;
*) echo "That doesn't look like a real Cemu uuid (expected '0_' + 32 hex chars)."
echo "Got: $_CCC_UUID"
exit 1 ;;
esac
GAME_DIR=$(grep '^GAME_STORAGE_DIR=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2-)
if [ -z "$GAME_DIR" ]; then read -r -p " Game storage path: " GAME_DIR; fi
if [ -z "$GAME_DIR" ]; then echo "No game storage path."; exit 1; fi
_CCC_DIR="$GAME_DIR/retro-home/Cemu/controllerProfiles"
if [ ! -d "$_CCC_DIR" ]; then
echo "No controllerProfiles/ yet at $_CCC_DIR — set up at least one controller"
echo "in Cemu's own Input Settings first (Controller 1), so there's a real,"
echo "working profile to clone the mapping from."
exit 1
fi
# Pick any existing, non-empty controllerN.xml as the mapping source —
# prefer controller0.xml (Cemu's own Controller 1), since that's the
# slot people set up first, but fall back to whichever one actually
# exists so this isn't hard-wired to slot 0 specifically.
_CCC_SRC=""
for _n in 0 1 2 3; do
_cand="$_CCC_DIR/controller${_n}.xml"
if [ -s "$_cand" ] && [ "$_n" != "$_CCC_SLOT" ]; then
_CCC_SRC="$_cand"
break
fi
done
if [ -z "$_CCC_SRC" ]; then
echo "No other working controllerN.xml found in $_CCC_DIR to clone from."
echo "Set up at least one controller fully in Cemu's own Input Settings first."
exit 1
fi
_CCC_TARGET="$_CCC_DIR/controller${_CCC_SLOT}.xml"
[ -f "$_CCC_TARGET" ] && cp "$_CCC_TARGET" "$_CCC_TARGET.bak.$(date +%s)"
if [ -z "$_CCC_NAME" ]; then
_CCC_NAME=$(grep -o '<display_name>[^<]*</display_name>' "$_CCC_SRC" | head -1 | sed -E 's#</?display_name>##g')
fi
python3 - "$_CCC_SRC" "$_CCC_TARGET" "$_CCC_UUID" "$_CCC_NAME" << 'CLONEPY'
import re, sys
src_path, out_path, uuid, display_name = sys.argv[1:5]
with open(src_path) as f:
template = f.read()
new = re.sub(r'<uuid>[^<]*</uuid>', f'<uuid>{uuid}</uuid>', template, count=1)
new = re.sub(r'<display_name>[^<]*</display_name>', f'<display_name>{display_name}</display_name>', new, count=1)
with open(out_path, 'w') as f:
f.write(new)
print(f"Wrote {out_path}")
print(f" uuid: {uuid}")
print(f" display_name: {display_name}")
CLONEPY
chown 1000:1000 "$_CCC_TARGET"
echo ""
echo "Cloned the mapping from $(basename "$_CCC_SRC") onto controller${_CCC_SLOT}.xml."
echo "Close Cemu COMPLETELY and relaunch for this to take effect — a hand-edited"
echo "profile file isn't picked up by an already-running Cemu."
;;
pin)
# Wolf logs: "Insert pin at http://SOMEIP:47989/pin/#HEXHASH"
# Extract just the hash fragment and build URLs for every interface
@@ -4068,6 +4177,8 @@ VDFPY
echo " ./manage.sh install-completion - Enable tab-completion for this script"
echo " ./manage.sh steam-add-nonsteam-game [name] [display name]"
echo " - Add an emulator (emulators/) as a non-Steam game, no GUI needed"
echo " ./manage.sh cemu-clone-controller [slot 0-3] [uuid] [display name]"
echo " - Clone a working Cemu controller mapping onto a new device slot"
echo " ./manage.sh backup - How to set up backups"
;;
esac
@@ -4628,6 +4739,7 @@ cd $WOLF_DIR
./manage.sh diagnose-ea # check link2ea:// state when game returns to Play
./manage.sh install-completion # enable tab-completion for manage.sh
./manage.sh steam-add-nonsteam-game # add an emulator as a non-Steam game, no GUI needed
./manage.sh cemu-clone-controller # clone a working Cemu controller mapping onto a new device slot
\`\`\`
### Tab completion
@@ -5065,6 +5177,33 @@ time Cemu launches from ES-DE too.
repo, not just reconnecting) reliably cleared it in testing, giving a
fresh PulseAudio session to work with. Reconnect to a fresh Desktop
session afterward and try again.
**If the TV device stays stuck on "Disabled" and won't take a real
device at all** (not just a one-time hang), this narrows down whether
it's PulseAudio itself with nothing to offer inside the container, or
Cemu choking on a device it can actually see:
\`\`\`bash
# Has this been a full Wolf restart, not just a reconnect, since it broke?
sudo ./setup.sh wolf
# Does PulseAudio inside the Desktop container see any output devices at all?
CONTAINER=\$(docker ps --format '{{.Names}}' | grep -i WolfDesktop | head -1)
docker exec "\$CONTAINER" pactl list short sinks
docker exec "\$CONTAINER" pactl info | grep -i "default sink"
# Cemu's own log — it logs audio device init/errors directly
GAME_DIR=\$(grep '^GAME_STORAGE_DIR=' $WOLF_DIR/.env | cut -d= -f2-)
find "\$GAME_DIR/retro-home/Cemu" -iname "log.txt" -exec cat {} \\;
# What settings.xml currently has for Audio (even while stuck "disabled")
cat "\$GAME_DIR/retro-home/Cemu/settings.xml" 2>/dev/null | grep -A5 -i "<Audio"
\`\`\`
An empty \`pactl list short sinks\` means there's no real fix on Cemu's
side to chase — the container itself has no audio sink for anything to
attach to. A populated sink list with Cemu still stuck points at Cemu's
own device-switch path instead, and \`log.txt\` / the \`<Audio>\` block
above are what to paste back for that to get root-caused rather than
guessed at.
6. **Adding a second controller and the first one ends up driving both
characters?** That's not a Cemu-specific bug — see
"Multiple controllers" above (\`./manage.sh controllers\`).
@@ -5107,6 +5246,25 @@ ambiguity about which one Cemu picks. Back up the file first
\`sudo\` needed, these files are written world-writable by Cemu itself.
Close Cemu completely and relaunch for a hand-edited profile to take effect.
**\`./manage.sh cemu-clone-controller\` does this copy for you.** Once
you've got 4 distinct controller *types* set up via \`./manage.sh controllers\`
(so Cemu can tell the devices apart at all) and one slot fully mapped by
hand, adding the rest doesn't need full manual button-mapping — Cemu's own
\`<mappings>\` format is universal SDL_CONTROLLER_BUTTON_* semantics, not
per-device raw indices, so the same mapping works for every device. All
each new slot needs is its own real \`<uuid>\` — get that the cheap way (in
Cemu's Input Settings, click **+** on the new controller, select the
device, map ONE button, Save; the mapping itself is thrown away), then:
\`\`\`bash
cd $WOLF_DIR
grep uuid "\$(grep '^GAME_STORAGE_DIR=' .env | cut -d= -f2-)/retro-home/Cemu/controllerProfiles/controller2.xml"
./manage.sh cemu-clone-controller 2 0_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\`\`\`
Clones a fully-configured slot's mapping onto slot 2 with that device's own
uuid — no per-button rebinding, no fighting the cut-off Save button for
every single controller. Backs up any existing \`controllerN.xml\` first,
and needs Cemu closed and relaunched afterward like any other hand-edit.
ROMs are mounted at \`/ROMs\` inside Desktop too, so Cemu's own File → Load
can browse straight to \`/ROMs/wiiu/\` without needing ES-DE at all.