Checkpoint with dolphin configs

master
Kuba Orlik 2 weeks ago
parent 56a8661268
commit fe4cd8835b

@ -1 +1,8 @@
test # Kazzite
set of scripts for neat bazzite tricks
# Setup
./deps.sh
./setup.sh

@ -0,0 +1,2 @@
#!/bin/bash
sudo rpm-ostree install python3-pygame

@ -0,0 +1,54 @@
#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const iniFile = process.argv[2];
if (!iniFile) {
console.error("Usage: ini-to-json.js file.ini");
process.exit(1);
}
const system = path.basename(iniFile, path.extname(iniFile));
const lines = fs.readFileSync(iniFile, "utf8").split(/\r?\n/);
const result = {
[system]: {}
};
let section = null;
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) {
continue;
}
const sectionMatch = trimmed.match(/^\[(.+)\]$/);
if (sectionMatch) {
section = sectionMatch[1];
result[system][section] ??= {};
continue;
}
if (!section) {
continue;
}
const eq = trimmed.indexOf("=");
if (eq === -1) {
continue;
}
const key = trimmed.slice(0, eq).trim();
const value = trimmed.slice(eq + 1).trim();
result[system][section][key] = value;
}
console.log(JSON.stringify(result, null, 2));

@ -0,0 +1,22 @@
#!/bin/bash
export PYGAME_HIDE_SUPPORT_PROMPT=1
export DOLPHIN_CONFIG_DIR=/home/kuba/.var/app/org.DolphinEmu.dolphin-emu/config/dolphin-emu
KAZZITE_DIR=/home/kuba/kazzite
SCRIPTS_DIR=$KAZZITE_DIR/dolphin-presets/presets
PICKED_SCRIPT=$($KAZZITE_DIR/gamepad-utils/script-selector.py --scripts "$SCRIPTS_DIR")
echo PICKED!!!!!!!!!!! $PICKED_SCRIPT
mapfile -t EXTRA_ARGS < <("$PICKED_SCRIPT")
/usr/bin/flatpak run \
--branch=stable \
--arch=x86_64 \
--command=/app/bin/dolphin-emu-wrapper \
org.DolphinEmu.dolphin-emu \
"${EXTRA_ARGS[@]}" \
"$@"

@ -0,0 +1,41 @@
#!/usr/bin/env node
const path = require("path");
const fs = require("fs");
const config = {
"Dolphin": {
"BluetoothPassthrough": {
"Enabled": true
}
}
}
function format_value(value){
if(typeof value== "boolean"){
return value? "True" : "False";
}else{return value}
}
for (const [system, sections] of Object.entries(config)) {
if(system =="WiimoteNew"){
let content = "";
for (const [section, keys] of Object.entries(sections)) {
content += `[${section}]\n`;
for (const [key, value] of Object.entries(keys)) {
content += `${key} = ${format_value(value)}\n`;
}
content += "\n";
}
fs.writeFileSync(path.resolve(process.env.DOLPHIN_CONFIG_DIR, "WiimoteNew.ini"), content);
continue;
}
for (const [section, keys] of Object.entries(sections)) {
for (const [key, value] of Object.entries(keys)) {
console.log(`--config=${system}.${section}.${key}=${format_value(value)}`);
}
}
}

@ -0,0 +1,119 @@
#!/usr/bin/env node
const path = require("path");
const fs = require("fs");
const config = {
"Dolphin": {
"BluetoothPassthrough": {
"Enabled": false
}
},
"WiimoteNew": {
"Wiimote1": {
"Device": "SDL/0/Steam Controller",
"Buttons/A": "`Button S`",
"Buttons/B": "`Trigger R` | `Button E`",
"Buttons/1": "`Button W`",
"Buttons/2": "`Button N`",
"Buttons/-": "Back",
"Buttons/+": "Start",
"Buttons/Home": "Return",
"D-Pad/Up": "`Pad N`",
"D-Pad/Down": "`Pad S`",
"D-Pad/Left": "`Pad W`",
"D-Pad/Right": "`Pad E`",
"IR/Up": "`XInput2/0/Virtual core pointer:Cursor Y-`",
"IR/Down": "`XInput2/0/Virtual core pointer:Cursor Y+`",
"IR/Left": "`XInput2/0/Virtual core pointer:Cursor X-`",
"IR/Right": "`XInput2/0/Virtual core pointer:Cursor X+`",
"Shake/X": "`Button W`",
"Shake/Y": "`Click 2`",
"Shake/Z": "`Click 2`",
"IRPassthrough/Object 1 X": "`IR Object 1 X`",
"IRPassthrough/Object 1 Y": "`IR Object 1 Y`",
"IRPassthrough/Object 1 Size": "`IR Object 1 Size`",
"IRPassthrough/Object 2 X": "`IR Object 2 X`",
"IRPassthrough/Object 2 Y": "`IR Object 2 Y`",
"IRPassthrough/Object 2 Size": "`IR Object 2 Size`",
"IRPassthrough/Object 3 X": "`IR Object 3 X`",
"IRPassthrough/Object 3 Y": "`IR Object 3 Y`",
"IRPassthrough/Object 3 Size": "`IR Object 3 Size`",
"IRPassthrough/Object 4 X": "`IR Object 4 X`",
"IRPassthrough/Object 4 Y": "`IR Object 4 Y`",
"IRPassthrough/Object 4 Size": "`IR Object 4 Size`",
"IMUAccelerometer/Up": "`Shoulder R`",
"IMUAccelerometer/Down": "`Accel Down`",
"IMUAccelerometer/Left": "`Accel Left`",
"IMUAccelerometer/Right": "`Accel Right`",
"IMUAccelerometer/Forward": "`Accel Forward`",
"IMUAccelerometer/Backward": "`Accel Backward`",
"IMUGyroscope/Pitch Up": "`Gyro Pitch Up`",
"IMUGyroscope/Pitch Down": "`Gyro Pitch Down`",
"IMUGyroscope/Roll Left": "`Gyro Roll Left`",
"IMUGyroscope/Roll Right": "`Gyro Roll Right`",
"IMUGyroscope/Yaw Left": "`Gyro Yaw Left`",
"IMUGyroscope/Yaw Right": "`Gyro Yaw Right`",
"Extension": "Nunchuk",
"Nunchuk/Buttons/C": "`Shoulder L`",
"Nunchuk/Buttons/Z": "`Trigger L`",
"Nunchuk/Stick/Up": "`Left Y+`",
"Nunchuk/Stick/Down": "`Left Y-`",
"Nunchuk/Stick/Left": "`Left X-`",
"Nunchuk/Stick/Right": "`Left X+`",
"Nunchuk/Stick/Calibration": "100.00 101.96 108.24 115.81 116.93 116.65 108.24 101.96 100.00 101.96 108.24 109.55 109.52 109.58 108.24 101.96 100.00 101.96 108.24 112.38 113.77 113.06 108.24 101.96 100.00 101.96 108.24 112.86 114.09 114.41 108.24 101.96",
"Nunchuk/Shake/X": "`Click 2`",
"Nunchuk/Shake/Y": "`Click 2`",
"Nunchuk/Shake/Z": "`Click 2`",
"Source": "1",
"IMUIR/Enabled": "False",
"IR/Auto-Hide": "True",
"Extension/Attach MotionPlus": "False"
},
"Wiimote2": {
"Device": "XInput2/0/Virtual core pointer",
"Source": "0"
},
"Wiimote3": {
"Device": "XInput2/0/Virtual core pointer",
"Source": "0"
},
"Wiimote4": {
"Device": "XInput2/0/Virtual core pointer",
"Source": "0"
},
"BalanceBoard": {
"Device": "XInput2/0/Virtual core pointer",
"Source": "0"
}
}
}
function format_value(value){
if(typeof value== "boolean"){
return value? "True" : "False";
}else{return value}
}
for (const [system, sections] of Object.entries(config)) {
if(system =="WiimoteNew"){
let content = "";
for (const [section, keys] of Object.entries(sections)) {
content += `[${section}]\n`;
for (const [key, value] of Object.entries(keys)) {
content += `${key} = ${format_value(value)}\n`;
}
content += "\n";
}
fs.writeFileSync(path.resolve(process.env.DOLPHIN_CONFIG_DIR, "WiimoteNew.ini"), content);
continue;
}
for (const [section, keys] of Object.entries(sections)) {
for (const [key, value] of Object.entries(keys)) {
console.log(`--config=${system}.${section}.${key}=${format_value(value)}`);
}
}
}

@ -0,0 +1,18 @@
import sys
import sdl2
import sdl2.ext
import time
def main():
if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_EVENTS) != 0:
print(f"SDL_Init error: {sdl2.SDL_GetError().decode()}")
return 1
num_joysticks = sdl2.SDL_NumJoysticks()
print("Joystick count: ", num_joysticks)
for i in range(num_joysticks):
print(i)
name = sdl2.SDL_GameControllerNameForIndex(i)
print(name)
if __name__ == "__main__":
sys.exit(main())

@ -0,0 +1,407 @@
#!/usr/bin/env python3
import argparse
import sys
import sdl2
import sdl2.ext
import sdl2.sdlttf as ttf
WIDTH = 1280
HEIGHT = 720
FONT = "/usr/share/fonts/open-sans/OpenSans-Semibold.ttf"
def debug(msg, enabled):
if enabled:
print(f"[DEBUG] {msg}", file=sys.stderr)
class ControllerManager:
def __init__(self, ignore, debug_enabled):
self.ignore = ignore
self.debug_enabled = debug_enabled
self.controllers = {}
self.unsupported = {}
def scan(self):
self.controllers.clear()
self.unsupported.clear()
count = sdl2.SDL_NumJoysticks()
for i in range(count):
name_ptr = sdl2.SDL_JoystickNameForIndex(i)
name = (
name_ptr.decode()
if name_ptr else
"Unknown Device"
)
if sdl2.SDL_IsGameController(i):
if any(
x.lower() in name.lower()
for x in self.ignore
):
debug(
f"Ignoring {name}",
self.debug_enabled
)
continue
c = sdl2.SDL_GameControllerOpen(i)
if c:
joy = sdl2.SDL_GameControllerGetJoystick(c)
instance = sdl2.SDL_JoystickInstanceID(
joy
)
self.controllers[instance] = {
"name": name,
"controller": c
}
debug(
f"Added controller {name}",
self.debug_enabled
)
else:
self.unsupported[i] = name
debug(
f"Unsupported joystick {name}",
self.debug_enabled
)
def add(self, index):
name_ptr = sdl2.SDL_JoystickNameForIndex(index)
name = (
name_ptr.decode()
if name_ptr else
"Unknown Device"
)
if sdl2.SDL_IsGameController(index):
c = sdl2.SDL_GameControllerOpen(index)
if c:
joy = sdl2.SDL_GameControllerGetJoystick(c)
instance = sdl2.SDL_JoystickInstanceID(
joy
)
self.controllers[instance] = {
"name": name,
"controller": c
}
debug(
f"Hotplugged {name}",
self.debug_enabled
)
else:
self.unsupported[index] = name
debug(
f"Hotplugged unsupported {name}",
self.debug_enabled
)
def remove(self, instance):
if instance in self.controllers:
name = self.controllers[instance]["name"]
sdl2.SDL_GameControllerClose(
self.controllers[instance]["controller"]
)
del self.controllers[instance]
debug(
f"Removed {name}",
self.debug_enabled
)
def draw_text(renderer, font, text, x, y, color):
surface = ttf.TTF_RenderUTF8_Blended(
font,
text.encode("utf-8"),
color
)
if not surface:
print(
"TTF render failed: " +
ttf.TTF_GetError().decode(),
file=sys.stderr
)
return
texture = sdl2.SDL_CreateTextureFromSurface(
renderer,
surface
)
if not texture:
print(
"Texture creation failed: " +
sdl2.SDL_GetError().decode(),
file=sys.stderr
)
sdl2.SDL_FreeSurface(surface)
return
rect = sdl2.SDL_Rect()
rect.x = x
rect.y = y
rect.w = surface.contents.w
rect.h = surface.contents.h
result = sdl2.SDL_RenderCopy(
renderer,
texture,
None,
rect
)
if result != 0:
print(
"RenderCopy failed: " +
sdl2.SDL_GetError().decode(),
file=sys.stderr
)
sdl2.SDL_DestroyTexture(texture)
sdl2.SDL_FreeSurface(surface)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("question")
parser.add_argument(
"--ignore",
nargs="*",
default=[]
)
parser.add_argument(
"--debug",
action="store_true"
)
args = parser.parse_args()
sdl2.SDL_Init(
sdl2.SDL_INIT_VIDEO |
sdl2.SDL_INIT_GAMECONTROLLER |
sdl2.SDL_INIT_JOYSTICK
)
ttf.TTF_Init()
window = sdl2.SDL_CreateWindow(
args.question.encode(),
sdl2.SDL_WINDOWPOS_CENTERED,
sdl2.SDL_WINDOWPOS_CENTERED,
WIDTH,
HEIGHT,
sdl2.SDL_WINDOW_FULLSCREEN
)
renderer = sdl2.SDL_CreateRenderer(
window,
-1,
sdl2.SDL_RENDERER_ACCELERATED
)
info = sdl2.SDL_RendererInfo()
sdl2.SDL_GetRendererInfo(renderer, info)
print(
"Renderer:",
info.name.decode(),
file=sys.stderr
)
font = ttf.TTF_OpenFont(
FONT.encode(),
42
)
manager = ControllerManager(
args.ignore,
args.debug
)
manager.scan()
event = sdl2.SDL_Event()
running = True
selected_controller = None
selected_time = 0
while running:
while sdl2.SDL_PollEvent(event):
if event.type == sdl2.SDL_CONTROLLERDEVICEADDED:
manager.add(
event.cdevice.which
)
elif event.type == sdl2.SDL_JOYDEVICEADDED:
manager.add(
event.jdevice.which
)
elif event.type == sdl2.SDL_JOYDEVICEREMOVED:
manager.remove(
event.jdevice.which
)
elif event.type == sdl2.SDL_CONTROLLERBUTTONDOWN:
instance = event.cbutton.which
if instance in manager.controllers:
selected_controller = manager.controllers[instance]["name"]
debug(
f"Selected {selected_controller}",
args.debug
)
selected_time = sdl2.SDL_GetTicks()
# draw
sdl2.SDL_SetRenderDrawColor(
renderer,
0,
0,
0,
255
)
sdl2.SDL_RenderClear(renderer)
draw_text(
renderer,
font,
args.question,
50,
50,
sdl2.SDL_Color(255,255,255,255)
)
y = 150
for c in manager.controllers.values():
draw_text(
renderer,
font,
"" + c["name"],
100,
y,
sdl2.SDL_Color(255,255,255,255)
)
y += 60
for name in manager.unsupported.values():
draw_text(
renderer,
font,
"" + name,
100,
y,
sdl2.SDL_Color(100,100,100,255)
)
y += 60
if selected_controller:
elapsed = sdl2.SDL_GetTicks() - selected_time
draw_text(
renderer,
font,
"Selected:",
100,
550,
sdl2.SDL_Color(255,255,255,255)
)
draw_text(
renderer,
font,
selected_controller,
100,
610,
sdl2.SDL_Color(0,255,0,255)
)
sdl2.SDL_RenderPresent(renderer)
if elapsed > 2000:
print(selected_controller)
running = False
continue
else:
sdl2.SDL_RenderPresent(renderer)
# cleanup
for c in manager.controllers.values():
sdl2.SDL_GameControllerClose(
c["controller"]
)
ttf.TTF_CloseFont(font)
sdl2.SDL_DestroyRenderer(renderer)
sdl2.SDL_DestroyWindow(window)
ttf.TTF_Quit()
sdl2.SDL_Quit()
if __name__ == "__main__":
sys.exit(main())

@ -0,0 +1,156 @@
#!/usr/bin/env python3
import argparse
import base64
import io
import json
import subprocess
import sys
import time
from pathlib import Path
import pygame
from PIL import Image
import sdl2
import sdl2.ext
# ---------- Configuration ----------
DEFAULT_SCRIPTS_PATH = "/home/kuba/kazzite"
TITLE = "Select script"
BIG_FONT_SIZE = 100
SMALL_FONT_SIZE = 40
AXIS_DEADZONE = 0.6
HAT_REPEAT_DELAY = 0.18
FPS = 60
# ---------- Helpers ----------
def load_scripts(directory):
return [
{
"name": p.stem,
"path": str(p.resolve())
}
for p in Path(directory).iterdir()
]
# ---------- UI with SDL2 input ----------
def run_ui(scripts):
pygame.init()
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
screen_w, screen_h = screen.get_size()
pygame.display.set_caption(TITLE)
scale = min(screen_w / 3840.0, screen_h / 2160.0)
big_font = pygame.font.SysFont(None, int(BIG_FONT_SIZE * scale))
small_font = pygame.font.SysFont(None, int(SMALL_FONT_SIZE * scale))
clock = pygame.time.Clock()
selected = 0
last_nav = 0
# ---------- SDL2 joystick init ----------
if sdl2.SDL_Init(sdl2.SDL_INIT_JOYSTICK) != 0:
print("SDL_Init Error:", sdl2.SDL_GetError().decode())
sys.exit(1)
joystick_count = sdl2.SDL_NumJoysticks()
sdl_joysticks = []
for i in range(joystick_count):
js = sdl2.SDL_JoystickOpen(i)
if js:
sdl_joysticks.append(js)
running = True
event = sdl2.SDL_Event()
while running:
now = time.time()
pygame.event.pump() # Update pygame keyboard events
# Handle pygame keyboard events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
selected = max(0, selected - 1)
elif event.key == pygame.K_DOWN:
selected = min(len(scripts) - 1, selected + 1)
elif event.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
path = scripts[selected]['path']
print(path)
running = False
elif event.key == pygame.K_ESCAPE:
print("ESCAPE")
running = False
sdl2.SDL_JoystickUpdate()
for i, js in enumerate(sdl_joysticks):
# Buttons
for b in range(sdl2.SDL_JoystickNumButtons(js)):
if sdl2.SDL_JoystickGetButton(js, b):
if b == 0: # confirm button
path = scripts[selected]['path']
print(path)
running = False
elif b == 1: # cancel button
print("CANCEL")
running = False
# Hats (D-pad)
for h in range(sdl2.SDL_JoystickNumHats(js)):
hat = sdl2.SDL_JoystickGetHat(js, h)
if hat & sdl2.SDL_HAT_UP and now - last_nav > HAT_REPEAT_DELAY:
selected = max(0, selected - 1)
last_nav = now
elif hat & sdl2.SDL_HAT_DOWN and now - last_nav > HAT_REPEAT_DELAY:
selected = min(len(scripts) - 1, selected + 1)
last_nav = now
# ---------- Render pygame UI ----------
screen.fill((10, 10, 10))
title_surf = small_font.render(TITLE, True, (230, 230, 230))
screen.blit(title_surf, ((screen_w - title_surf.get_width()) // 2, int(screen_h * 0.08)))
total = len(scripts)
item_height = int(300 * scale)
start_y = (screen_h - total * item_height) // 2
for i, script in enumerate(scripts):
y = start_y + i * item_height
color = (255, 255, 255) if i == selected else (180, 180, 180)
if i == selected:
pygame.draw.rect(screen, (50, 50, 50), (screen_w * 0.1, y - 20, screen_w * 0.8, item_height - 40), border_radius=20)
name_surf = big_font.render(script['name'], True, color)
screen.blit(name_surf, (int(screen_w * 0.4), y + item_height // 3))
pygame.display.flip()
clock.tick(FPS)
# Cleanup SDL2 joysticks
for js in sdl_joysticks:
sdl2.SDL_JoystickClose(js)
sdl2.SDL_QuitSubSystem(sdl2.SDL_INIT_JOYSTICK)
pygame.quit()
# ---------- Main ----------
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--scripts", default=str(DEFAULT_SCRIPTS_PATH))
args = parser.parse_args()
scripts = load_scripts(Path(args.scripts))
run_ui(scripts)
if __name__ == "__main__":
main()
Loading…
Cancel
Save