mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-26 21:57:24 -05:00
The PortMaster port now starts install.py --tui on the console (or in foot or xterm under a compositor), with gptokeyb in kill mode on the installer's path, retrobios.gptk, TERM=linux and a Terminus font, and puts the console, font and mapper back on exit. pugwash screens stay as the fallback where no terminal can show, and start in no-check mode so PortMaster's A/B fix applies. Without a network, every launcher installs from a pack found on the card or names the pack and the folder. Adds launchers for Batocera and Knulli (Ports, evmapy .keys), muOS (.muxapp in muxterm with gptokeyb2) and the Tools menu of ArkOS, dArkOS, ROCKNIX, JELOS and AmberELEC, built by build_port.py with the pinned installer and tested on a simulated device.
419 lines
15 KiB
Plaintext
419 lines
15 KiB
Plaintext
#!/bin/bash
|
|
# shellcheck shell=bash
|
|
# RetroBIOS screens, drawn by PortMaster's own GUI (pugwash fifo_control)
|
|
# through the functions of PortMasterDialog.txt. Sourced by RetroBIOS.sh
|
|
# after PortMasterDialog.txt and tui.txt.
|
|
#
|
|
# They are the port's fallback: RetroBIOS.sh opens them only when no terminal
|
|
# can show install.py --tui, that is under a compositor that has neither foot
|
|
# nor xterm, or when the interface refuses the console it was given.
|
|
#
|
|
# Only what PortMaster 2024.02.26 and the 8.5.23 copy JELOS and KOLOS ship
|
|
# is used: PortMasterDialogInit, PortMasterDialog, PortMasterDialogResult,
|
|
# PortMasterDialogExit, PortMasterIPCheck, and the commands
|
|
# register_set_info, register_clear, selection_list, messages_begin,
|
|
# message, messages_end, progress, progress_clear and check_runtime.
|
|
# Those releases ignore --want-cancel, so every list ends on its own Back
|
|
# or Quit entry. They also read "\n" literally, so no text has a newline,
|
|
# and their wait for an answer gives up after 30 seconds: rb_answer keeps
|
|
# waiting while pugwash is alive.
|
|
#
|
|
# pugwash starts in "no-check" mode, never "no-harbour": only with
|
|
# HarbourMaster loaded does it apply the A/B fix PortMaster keeps for Miyoo,
|
|
# TrimUI, Knulli and the Xbox-layout ArkOS and AmberELEC pads.
|
|
#
|
|
# Inputs: GAMEDIR, controlfolder, ESUDO, and what tui.txt defines
|
|
# (RB_INSTALL, RB_STATE, RB_LOG, rb_py, rb_online, rb_find_pack).
|
|
|
|
RB_EVENTS="${RB_EVENTS:-$GAMEDIR/rbevents.py}"
|
|
RB_SOURCE_ARGS=()
|
|
# Read by tui.txt's rb_python_text.
|
|
# shellcheck disable=SC2034
|
|
RB_PYTHON_PROBLEM="${RB_PYTHON_PROBLEM:-}"
|
|
|
|
# --- Python runtime -------------------------------------------------------
|
|
|
|
# tui.txt's rb_runtime calls this when the runtime is not on the card yet:
|
|
# PortMaster downloads it the way its own ports get theirs.
|
|
rb_fetch_runtime() {
|
|
local squashfs="$1" answer
|
|
if [ -z "$(PortMasterIPCheck)" ]; then
|
|
RB_PYTHON_PROBLEM="runtime_offline"
|
|
return 1
|
|
fi
|
|
PortMasterDialogInit "no-check"
|
|
PortMasterDialog "messages_begin"
|
|
PortMasterDialog "message" "This system's Python is older than 3.8. Downloading PortMaster's $RB_RUNTIME runtime."
|
|
answer=$(rb_answer "check_runtime" "$RB_RUNTIME.squashfs")
|
|
PortMasterDialog "messages_end"
|
|
PortMasterDialogExit
|
|
if [ "$answer" != "OKAY" ] || [ ! -f "$squashfs" ]; then
|
|
RB_PYTHON_PROBLEM="runtime_failed"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# --- Dialog primitives ----------------------------------------------------
|
|
|
|
# A command that waits for the user, and its answer. PortMasterDialogResult
|
|
# stops waiting after 30 seconds on the older releases and then returns
|
|
# WAIT: the answer is read again until pugwash writes one or exits.
|
|
rb_answer() {
|
|
local answer tries=0
|
|
answer=$(PortMasterDialogResult "$@")
|
|
while [ "$answer" = "WAIT" ] || [ -z "$answer" ]; do
|
|
if [ -n "$PM_PID" ] && [ ! -d "/proc/$PM_PID" ]; then
|
|
break
|
|
fi
|
|
if [ "$tries" -ge 6000 ]; then
|
|
break
|
|
fi
|
|
tries=$((tries + 1))
|
|
sleep 0.3
|
|
answer=$(cat "$PM_DONE" 2>/dev/null)
|
|
done
|
|
printf '%s\n' "$answer"
|
|
}
|
|
|
|
rb_plural() {
|
|
if [ "$1" = "1" ]; then
|
|
printf '%s %s\n' "$1" "$2"
|
|
else
|
|
printf '%s %ss\n' "$1" "$2"
|
|
fi
|
|
}
|
|
|
|
rb_entry() {
|
|
PortMasterDialog "register_set_info" "$1" "$2" "title:$3" "description:$4"
|
|
}
|
|
|
|
# Show a list and print the chosen key. B, where the release reads it,
|
|
# answers like the Back or Quit entry ($2).
|
|
rb_choose() {
|
|
local answer
|
|
answer=$(rb_answer "selection_list" "--register=$1" "--want-description" "--want-cancel")
|
|
case "$answer" in
|
|
CANCEL|FAIL|WAIT|"") printf '%s\n' "$2" ;;
|
|
*) printf '%s\n' "$answer" ;;
|
|
esac
|
|
}
|
|
|
|
# A screen of text with one or two ways out: rb_notice TITLE TEXT KEY LABEL [KEY LABEL].
|
|
# B answers like the last one, which leaves.
|
|
rb_notice() {
|
|
local title="$1" text="$2"
|
|
shift 2
|
|
local last="$1" answer
|
|
PortMasterDialog "register_clear" "notice"
|
|
# The list has no heading of its own: the first entry is the heading.
|
|
rb_entry "notice" "info" "$title" "$text"
|
|
rb_entry "notice" "$1" "$2" "$text"
|
|
if [ $# -ge 4 ]; then
|
|
rb_entry "notice" "$3" "$4" "$text"
|
|
last="$3"
|
|
fi
|
|
while true; do
|
|
answer=$(rb_choose "notice" "$last")
|
|
if [ "$answer" != "info" ]; then
|
|
printf '%s\n' "$answer"
|
|
return
|
|
fi
|
|
done
|
|
}
|
|
|
|
# --- Reading install.py ---------------------------------------------------
|
|
|
|
# Run a --check and read its records into RB_* variables. Without a network
|
|
# the files come from a pack on the card, when there is one.
|
|
rb_check() {
|
|
mkdir -p "$RB_STATE"
|
|
PortMasterDialog "messages_begin"
|
|
PortMasterDialog "message" "Reading the BIOS folder and the file list."
|
|
RB_SOURCE_ARGS=()
|
|
if ! rb_online; then
|
|
local pack
|
|
pack=$(rb_find_pack)
|
|
[ -n "$pack" ] && RB_SOURCE_ARGS=(--from-pack "$pack")
|
|
fi
|
|
rb_py "$RB_INSTALL" --check --events "${RB_SOURCE_ARGS[@]}" "${RB_PLATFORM_ARGS[@]}" > "$RB_STATE/check.jsonl" 2>> "$RB_LOG"
|
|
PortMasterDialog "messages_end"
|
|
rb_py "$RB_EVENTS" status "$RB_STATE/check.jsonl" > "$RB_STATE/status.tsv" 2>> "$RB_LOG"
|
|
rb_read_status "$RB_STATE/status.tsv"
|
|
}
|
|
|
|
rb_read_status() {
|
|
RB_OS_NAME="this system"
|
|
RB_DETECTED=""
|
|
RB_PLATFORM=""
|
|
RB_PLATFORM_NAME=""
|
|
RB_BIOS_DIR=""
|
|
RB_ERROR=""
|
|
RB_ERROR_TEXT=""
|
|
RB_CHECKED=""
|
|
RB_END="none"
|
|
RB_SYSTEMS=()
|
|
RB_COMPLETE=0
|
|
local kind a b c d e f g h
|
|
while IFS=$'\t' read -r kind a b c d e f g h; do
|
|
case "$kind" in
|
|
detect) RB_OS_NAME="$a"; RB_DETECTED="$b" ;;
|
|
platform) RB_PLATFORM="$a"; RB_PLATFORM_NAME="$b"; RB_BIOS_DIR="$c" ;;
|
|
error) RB_ERROR="$a"; RB_ERROR_TEXT="$b" ;;
|
|
check)
|
|
RB_CHECKED=1
|
|
RB_FILES="$a"; RB_PRESENT="$b"; RB_MISSING="$c"; RB_WRONG="$d"
|
|
RB_NEEDED_SIZE="$e"; RB_DATA_MISSING="$f"; RB_NOT_INSTALLABLE="$g"
|
|
;;
|
|
system)
|
|
if [ "$h" = "1" ]; then
|
|
RB_SYSTEMS+=("$a"$'\t'"$b"$'\t'"$c"$'\t'"$d"$'\t'"$e"$'\t'"$f"$'\t'"$g")
|
|
else
|
|
RB_COMPLETE=$((RB_COMPLETE + 1))
|
|
fi
|
|
;;
|
|
end) RB_END="$a" ;;
|
|
esac
|
|
done < "$1"
|
|
}
|
|
|
|
rb_status_text() {
|
|
local text="$RB_OS_NAME, $RB_PLATFORM_NAME file list: $RB_PRESENT/$RB_FILES BIOS files, $RB_MISSING missing, $RB_WRONG wrong hash."
|
|
if [ "${RB_NOT_INSTALLABLE:-0}" != "0" ]; then
|
|
text="$text $RB_NOT_INSTALLABLE not in the collection."
|
|
fi
|
|
printf '%s\n' "$text"
|
|
}
|
|
|
|
rb_pending() {
|
|
printf '%s\n' $((RB_MISSING + RB_WRONG + RB_DATA_MISSING))
|
|
}
|
|
|
|
# --- Screens --------------------------------------------------------------
|
|
|
|
rb_screen_no_python() {
|
|
rb_notice "Python 3.8 or newer is needed" "$(rb_python_text)" "quit" "Quit" > /dev/null
|
|
}
|
|
|
|
# What the check found when it found nothing to show: no platform, no
|
|
# connection, an unreadable list. Prints check or quit.
|
|
rb_screen_problem() {
|
|
if [ "$RB_END" = "none" ]; then
|
|
rb_notice "The installer stopped before checking" "install.py ended without a result. Its messages are in $RB_LOG." "check" "Check again" "quit" "Quit"
|
|
elif [ "$RB_ERROR" = "conflict" ]; then
|
|
rb_notice "Two systems found" "$RB_ERROR_TEXT, so the BIOS folder is not known. $RB_PC_ROUTE" "check" "Check again" "quit" "Quit"
|
|
elif [ "$RB_DETECTED" = "0" ]; then
|
|
rb_notice "No BIOS folder found" "install.py knows no BIOS folder on $RB_OS_NAME. $RB_PC_ROUTE" "check" "Check again" "quit" "Quit"
|
|
elif [ "$RB_ERROR" = "network" ]; then
|
|
rb_notice "No connection" "$RB_OS_NAME, BIOS folder $RB_BIOS_DIR. $(rb_pack_text)" "check" "Check again" "quit" "Quit"
|
|
elif [ "$RB_ERROR" = "invalid" ]; then
|
|
rb_notice "The file list is newer than this port" "$RB_ERROR_TEXT. Update RetroBIOS from PortMaster, or use the installer on a computer. $RB_PC_ROUTE" "check" "Check again" "quit" "Quit"
|
|
else
|
|
rb_notice "The file list could not be read" "${RB_ERROR_TEXT:-install.py ended with code $RB_END}. Its messages are in $RB_LOG." "check" "Check again" "quit" "Quit"
|
|
fi
|
|
}
|
|
|
|
# The menu after a check. Prints update, choose, check or quit.
|
|
rb_screen_menu() {
|
|
local status pending
|
|
status=$(rb_status_text)
|
|
pending=$(rb_pending)
|
|
PortMasterDialog "register_clear" "menu"
|
|
# The list has no heading of its own: the first entry is the status.
|
|
if [ "$pending" -gt 0 ]; then
|
|
local data=""
|
|
if [ "${RB_DATA_MISSING:-0}" != "0" ]; then
|
|
data=" and $RB_DATA_MISSING data files"
|
|
fi
|
|
rb_entry "menu" "status" "$RB_OS_NAME: $RB_PRESENT/$RB_FILES BIOS files, $RB_MISSING missing, $RB_WRONG wrong hash" "$status BIOS folder: $RB_BIOS_DIR."
|
|
rb_entry "menu" "update" "Update everything missing" "$status Downloads $(rb_plural $((RB_MISSING + RB_WRONG)) file)$data, $RB_NEEDED_SIZE, to $RB_BIOS_DIR."
|
|
rb_entry "menu" "choose" "Choose systems" "$status Pick the systems to update."
|
|
else
|
|
rb_entry "menu" "status" "$RB_OS_NAME: all $RB_FILES BIOS files present" "$status Nothing to download. BIOS folder: $RB_BIOS_DIR."
|
|
fi
|
|
rb_entry "menu" "check" "Check again" "$status Reads the BIOS folder and the file list again."
|
|
rb_entry "menu" "quit" "Quit" "$status"
|
|
local answer
|
|
while true; do
|
|
answer=$(rb_choose "menu" "quit")
|
|
if [ "$answer" != "status" ]; then
|
|
printf '%s\n' "$answer"
|
|
return
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Systems with files to fetch, toggled with A. Prints the chosen system
|
|
# ids, one per line, or nothing for Back.
|
|
rb_screen_systems() {
|
|
local -A chosen=()
|
|
local line id name files present missing wrong size answer count
|
|
while true; do
|
|
count=0
|
|
for id in "${!chosen[@]}"; do
|
|
count=$((count + 1))
|
|
done
|
|
PortMasterDialog "register_clear" "systems"
|
|
if [ "$count" -gt 0 ]; then
|
|
rb_entry "systems" "update" "Update the $count selected" "Downloads what the selected systems lack to $RB_BIOS_DIR."
|
|
else
|
|
rb_entry "systems" "update" "Update the selected" "Nothing selected yet: pick systems below with A."
|
|
fi
|
|
rb_entry "systems" "back" "Back" "Back to the menu. $(rb_plural "$RB_COMPLETE" system) complete, not listed."
|
|
for line in "${RB_SYSTEMS[@]}"; do
|
|
IFS=$'\t' read -r id name files present missing wrong size <<< "$line"
|
|
local mark=""
|
|
if [ -n "${chosen[$id]}" ]; then
|
|
mark=" (selected)"
|
|
fi
|
|
rb_entry "systems" "sys:$id" "$name$mark" "$present/$files present, $missing missing, $wrong wrong hash, $size to download. A selects or clears it."
|
|
done
|
|
answer=$(rb_choose "systems" "back")
|
|
case "$answer" in
|
|
back) return ;;
|
|
update)
|
|
if [ "$count" -gt 0 ]; then
|
|
printf '%s\n' "${!chosen[@]}" | sort
|
|
return
|
|
fi
|
|
;;
|
|
sys:*)
|
|
id="${answer#sys:}"
|
|
# Quoted literally so unset expands the subscript once: a double-quoted
|
|
# "chosen[$id]" is expanded again inside unset.
|
|
[[ "$id" =~ ^[A-Za-z0-9._-]+$ ]] || continue
|
|
if [ -n "${chosen[$id]}" ]; then
|
|
unset 'chosen[$id]'
|
|
else
|
|
chosen[$id]=1
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Download with a progress bar fed by the event stream: rb_update [SYSTEM...]
|
|
rb_update() {
|
|
local args=() system
|
|
for system in "$@"; do
|
|
args+=(--system "$system")
|
|
done
|
|
mkdir -p "$RB_STATE"
|
|
PortMasterDialog "messages_begin"
|
|
PortMasterDialog "message" "Downloading to $RB_BIOS_DIR."
|
|
PortMasterDialog "progress" "Starting" 0 1
|
|
local kind a b c d e f
|
|
rb_py "$RB_INSTALL" --events "${RB_SOURCE_ARGS[@]}" "${RB_PLATFORM_ARGS[@]}" "${args[@]}" 2>> "$RB_LOG" \
|
|
| rb_py "$RB_EVENTS" follow "$RB_STATE/summary.tsv" 2>> "$RB_LOG" \
|
|
| while IFS=$'\t' read -r kind a b c d e f; do
|
|
case "$kind" in
|
|
download) PortMasterDialog "message" "$(rb_plural "$a" file), $b." ;;
|
|
file) PortMasterDialog "progress" "$d, $e of $f" "$a" "$b" ;;
|
|
progress) PortMasterDialog "progress" "$c of $d" "$a" "$b" ;;
|
|
error) PortMasterDialog "message" "$b" ;;
|
|
esac
|
|
done
|
|
PortMasterDialog "progress_clear"
|
|
PortMasterDialog "messages_end"
|
|
}
|
|
|
|
# What the update did. Prints back or quit.
|
|
rb_screen_summary() {
|
|
local kind a b c d shown=0 failed=0 kept=0
|
|
local end="none" installed=0 up_to_date=0 pending=0 interrupted=0 error=""
|
|
while IFS=$'\t' read -r kind a b c d; do
|
|
case "$kind" in
|
|
summary) installed="$a"; up_to_date="$b"; pending="$c"; interrupted="$d" ;;
|
|
failed) failed=$((failed + 1)) ;;
|
|
error) error="$b" ;;
|
|
end) end="$a" ;;
|
|
esac
|
|
done < "$RB_STATE/summary.tsv"
|
|
PortMasterDialog "register_clear" "summary"
|
|
# The first entry is the outcome, the heading the list does not have.
|
|
if [ "$end" = "none" ]; then
|
|
rb_entry "summary" "info:result" "The installer stopped" "install.py ended without a result. Its messages are in $RB_LOG."
|
|
elif [ -n "$error" ]; then
|
|
rb_entry "summary" "info:result" "Nothing downloaded" "$error."
|
|
else
|
|
rb_entry "summary" "info:result" "Installed: $(rb_plural "$installed" file)" "$up_to_date already up to date, $failed failed, $pending still to fetch."
|
|
fi
|
|
rb_entry "summary" "back" "Back to the menu" "Checks the BIOS folder again."
|
|
rb_entry "summary" "quit" "Quit" "Leaves RetroBIOS."
|
|
if [ "$interrupted" = "1" ]; then
|
|
rb_entry "summary" "info:stopped" "Stopped before the end" "Update again to carry on. Files already installed are kept."
|
|
fi
|
|
while IFS=$'\t' read -r kind a b c d; do
|
|
case "$kind" in
|
|
failed)
|
|
shown=$((shown + 1))
|
|
if [ "$shown" -le 50 ]; then
|
|
rb_entry "summary" "info:failed:$shown" "Failed: $a" "$b"
|
|
fi
|
|
;;
|
|
kept)
|
|
kept=$((kept + 1))
|
|
rb_entry "summary" "info:kept:$kept" "Kept $(rb_plural "$b" file) you had ($c)" "The collection's copies replaced them. Yours are in $a. Copy what that folder holds into the folder above retrobios-backup to put them back."
|
|
;;
|
|
skipped)
|
|
rb_entry "summary" "info:skipped:$a" "Not in the collection ($a): $(rb_plural "$b" file)" "Required: $c. The RetroBIOS collection does not hold these files, so they cannot be downloaded."
|
|
;;
|
|
esac
|
|
done < "$RB_STATE/summary.tsv"
|
|
if [ "$shown" -gt 50 ]; then
|
|
rb_entry "summary" "info:more" "And $((shown - 50)) more failed" "The full list is in $RB_LOG."
|
|
fi
|
|
local answer
|
|
while true; do
|
|
answer=$(rb_choose "summary" "back")
|
|
case "$answer" in
|
|
back|quit) printf '%s\n' "$answer"; return ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# --- Flow -----------------------------------------------------------------
|
|
|
|
# The screens, once rb_select_python has run.
|
|
rb_main() {
|
|
PortMasterDialogInit "no-check"
|
|
if [ -z "$RB_PYTHON" ]; then
|
|
rb_screen_no_python
|
|
PortMasterDialogExit
|
|
return 1
|
|
fi
|
|
RB_PLATFORM_ARGS=()
|
|
local action systems chosen
|
|
rb_check
|
|
while true; do
|
|
if [ -z "$RB_CHECKED" ]; then
|
|
action=$(rb_screen_problem)
|
|
else
|
|
# Later runs name the platform the first one found, so a second
|
|
# detected platform never changes what the screens describe.
|
|
RB_PLATFORM_ARGS=(--platform "$RB_PLATFORM")
|
|
action=$(rb_screen_menu)
|
|
fi
|
|
case "$action" in
|
|
update)
|
|
rb_update
|
|
[ "$(rb_screen_summary)" = "quit" ] && break
|
|
rb_check
|
|
;;
|
|
choose)
|
|
systems=$(rb_screen_systems)
|
|
if [ -n "$systems" ]; then
|
|
mapfile -t chosen <<< "$systems"
|
|
rb_update "${chosen[@]}"
|
|
[ "$(rb_screen_summary)" = "quit" ] && break
|
|
rb_check
|
|
fi
|
|
;;
|
|
check) rb_check ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
PortMasterDialogExit
|
|
return 0
|
|
}
|