mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
Without a network, muOS and ArkOS launchers ask to copy the BIOS files from the card the system does not read (muOS bind.sh, dArkOS roms2), then install with --from and the pack. CI checks that shellcheck is present, and the test fails under CI without it.
568 lines
18 KiB
Plaintext
568 lines
18 KiB
Plaintext
#!/bin/bash
|
|
# shellcheck shell=bash
|
|
# RetroBIOS on a handheld: runs install.py --tui on a terminal the player can
|
|
# see, with the controller mapped to the keys the interface reads.
|
|
#
|
|
# Sourced by every launcher (the PortMaster port, the Batocera and Knulli
|
|
# port, the muOS app, the Tools entry), each of which sets the inputs below
|
|
# and calls rb_select_python, then rb_launch. Run as `bash tui.txt here`, it
|
|
# is the part that runs inside a terminal app the launcher opened.
|
|
#
|
|
# Inputs, all optional:
|
|
# RB_ROUTES where to look for a terminal, in order: here (this
|
|
# script's own stdin and stdout), app (foot or xterm in the
|
|
# graphical session), vt (a Linux console). Default "app vt".
|
|
# RB_CONSOLE the console of the vt route (default /dev/tty0)
|
|
# RB_VT_SWITCH a console number to switch to when a graphical session
|
|
# covers the current one (Batocera, Knulli)
|
|
# RB_MAPPER the command that starts gptokeyb, kill flag included
|
|
# ("$GPTOKEYB" in a PortMaster port), empty for none
|
|
# RB_GPTK its mapping (default retrobios.gptk next to this file)
|
|
# RB_PACK_PATH folders searched for a pack when there is no network,
|
|
# separated by ":", the first one named to the player
|
|
# RB_OTHER_CARDS BIOS folders the firmware keeps on each of its cards,
|
|
# separated by ":", as its own code names them
|
|
# RB_BIOS_DEST the one of them the firmware reads, never offered as the
|
|
# other card
|
|
# RB_FALLBACK 1 when the launcher has screens of its own to fall back to
|
|
# ESUDO sudo where the launcher needs it, as PortMaster sets it
|
|
|
|
RB_LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
|
RB_DIR="${RB_DIR:-$(dirname "$RB_LIB")}"
|
|
RB_INSTALL="${RB_INSTALL:-$RB_DIR/install.py}"
|
|
RB_EVENTS="${RB_EVENTS:-$RB_DIR/rbevents.py}"
|
|
RB_GPTK="${RB_GPTK:-$RB_DIR/retrobios.gptk}"
|
|
RB_STATE="${RB_STATE:-$RB_DIR/run}"
|
|
RB_LOG="${RB_LOG:-$RB_DIR/log.txt}"
|
|
RB_ROUTES="${RB_ROUTES:-app vt}"
|
|
RB_CONSOLE="${RB_CONSOLE:-/dev/tty0}"
|
|
RB_PACK_PATH="${RB_PACK_PATH:-$RB_DIR}"
|
|
RB_OTHER_CARDS="${RB_OTHER_CARDS:-}"
|
|
RB_BIOS_DEST="${RB_BIOS_DEST:-}"
|
|
RB_FONTS="${RB_FONTS:-/usr/share/consolefonts}"
|
|
RB_FB="${RB_FB:-/sys/class/graphics/fb0/virtual_size}"
|
|
RB_ROUTE_TABLE="${RB_ROUTE_TABLE:-/proc/net/route}"
|
|
RB_VT_ACTIVE="${RB_VT_ACTIVE:-/sys/class/tty/tty0/active}"
|
|
RB_PROBE_URL="${RETROBIOS_BASE_URL:-https://raw.githubusercontent.com/Abdess/retrobios/}"
|
|
RB_RUNTIME="python_3.11"
|
|
RB_RELEASES="github.com/Abdess/retrobios/releases/latest"
|
|
RB_PC_ROUTE="On a computer, with this SD card mounted, the same installer fills the card: install.py --platform NAME --dest BIOS-FOLDER-ON-THE-CARD. Instructions: abdess.github.io/retrobios/wiki/installer"
|
|
RB_PYTHON="${RB_PYTHON:-}"
|
|
RB_PYTHONHOME="${RB_PYTHONHOME:-}"
|
|
RB_PYTHON_PROBLEM="${RB_PYTHON_PROBLEM:-}"
|
|
RB_MOUNTED=""
|
|
RB_MAPPER_PID=""
|
|
RB_TTY=""
|
|
RB_STTY=""
|
|
RB_FONT_BACK=""
|
|
RB_VT_BACK=""
|
|
RB_ROUTE=""
|
|
RB_TERMAPP=()
|
|
|
|
rb_arch() {
|
|
uname -m
|
|
}
|
|
|
|
rb_py() {
|
|
if [ -n "$RB_PYTHONHOME" ]; then
|
|
PYTHONHOME="$RB_PYTHONHOME" "$RB_PYTHON" "$@"
|
|
else
|
|
"$RB_PYTHON" "$@"
|
|
fi
|
|
}
|
|
|
|
# --- Python ---------------------------------------------------------------
|
|
|
|
rb_python_ok() {
|
|
"$@" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)' >/dev/null 2>&1
|
|
}
|
|
|
|
# The OS python3 when it is 3.8 or newer, else PortMaster's python_3.11
|
|
# runtime on aarch64, mounted as PortMaster's own Python ports do. A launcher
|
|
# that can download the runtime defines rb_fetch_runtime. Sets
|
|
# RB_PYTHON_PROBLEM when neither can run install.py.
|
|
rb_select_python() {
|
|
if command -v python3 >/dev/null 2>&1 && rb_python_ok python3; then
|
|
RB_PYTHON="python3"
|
|
return 0
|
|
fi
|
|
if [ "$(rb_arch)" != "aarch64" ]; then
|
|
RB_PYTHON_PROBLEM="old"
|
|
return 1
|
|
fi
|
|
rb_runtime
|
|
}
|
|
|
|
rb_runtime() {
|
|
local python_dir="$HOME/$RB_RUNTIME"
|
|
local squashfs="${controlfolder:-/nonexistent}/libs/$RB_RUNTIME.squashfs"
|
|
if [ ! -f "$squashfs" ]; then
|
|
if ! declare -F rb_fetch_runtime >/dev/null; then
|
|
RB_PYTHON_PROBLEM="runtime_missing"
|
|
return 1
|
|
fi
|
|
rb_fetch_runtime "$squashfs" || return 1
|
|
fi
|
|
mkdir -p "$python_dir"
|
|
if [ "${PM_CAN_MOUNT:-Y}" != "N" ]; then
|
|
$ESUDO umount "$python_dir" >/dev/null 2>&1
|
|
fi
|
|
if ! $ESUDO mount "$squashfs" "$python_dir"; then
|
|
RB_PYTHON_PROBLEM="runtime_failed"
|
|
return 1
|
|
fi
|
|
RB_MOUNTED="$python_dir"
|
|
RB_PYTHON="$python_dir/bin/python3"
|
|
RB_PYTHONHOME="$python_dir"
|
|
if ! PYTHONHOME="$python_dir" rb_python_ok "$RB_PYTHON"; then
|
|
RB_PYTHON=""
|
|
RB_PYTHON_PROBLEM="runtime_failed"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
rb_release_python() {
|
|
if [ -n "$RB_MOUNTED" ] && [ "${PM_CAN_MOUNT:-Y}" != "N" ]; then
|
|
$ESUDO umount "$RB_MOUNTED" >/dev/null 2>&1
|
|
fi
|
|
RB_MOUNTED=""
|
|
}
|
|
|
|
rb_python_text() {
|
|
local found
|
|
found=$(python3 --version 2>&1 | awk '{print $2}')
|
|
case "$RB_PYTHON_PROBLEM" in
|
|
old) printf '%s\n' "Python 3.8 or newer is needed. This system has ${found:-none}, and PortMaster's Python runtime exists for aarch64 only, and this device is $(rb_arch). $RB_PC_ROUTE" ;;
|
|
runtime_offline) printf '%s\n' "Python 3.8 or newer is needed. This system has ${found:-none}, and PortMaster's $RB_RUNTIME runtime needs a connection to download. $RB_PC_ROUTE" ;;
|
|
runtime_missing) printf '%s\n' "Python 3.8 or newer is needed. This system has ${found:-none}. The RetroBIOS port from PortMaster downloads PortMaster's $RB_RUNTIME runtime, which this entry then uses too. $RB_PC_ROUTE" ;;
|
|
*) printf '%s\n' "Python 3.8 or newer is needed. This system has ${found:-none}, and PortMaster's $RB_RUNTIME runtime could not be installed or run. $RB_PC_ROUTE" ;;
|
|
esac
|
|
}
|
|
|
|
# --- Network and packs ----------------------------------------------------
|
|
|
|
# Whether the file list can be reached. Only a failure to resolve or to
|
|
# connect counts as offline: a certificate refused by an old curl still means
|
|
# a network, and install.py names that problem itself.
|
|
rb_online() {
|
|
local code
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -sSI --max-time 8 "$RB_PROBE_URL" >/dev/null 2>&1
|
|
code=$?
|
|
case "$code" in
|
|
5|6|7|28) return 1 ;;
|
|
*) return 0 ;;
|
|
esac
|
|
fi
|
|
if command -v wget >/dev/null 2>&1; then
|
|
wget -q --spider -T 8 "$RB_PROBE_URL" >/dev/null 2>&1
|
|
[ $? -ne 4 ]
|
|
return
|
|
fi
|
|
# Neither tool: a default route is the best guess there is.
|
|
awk 'NR > 1 && $2 == "00000000" { found = 1 } END { exit !found }' "$RB_ROUTE_TABLE" 2>/dev/null
|
|
}
|
|
|
|
# The system and BIOS folder install.py finds here, read without a network:
|
|
# detection is local, and the run stops where the file list would download.
|
|
rb_detect() {
|
|
RB_PLATFORM=""
|
|
RB_PLATFORM_NAME=""
|
|
RB_BIOS_DIR=""
|
|
mkdir -p "$RB_STATE"
|
|
rb_py "$RB_INSTALL" --check --events > "$RB_STATE/detect.jsonl" 2>> "$RB_LOG"
|
|
local kind a b c
|
|
while IFS=$'\t' read -r kind a b c; do
|
|
if [ "$kind" = "platform" ]; then
|
|
RB_PLATFORM="$a"
|
|
RB_PLATFORM_NAME="$b"
|
|
RB_BIOS_DIR="$c"
|
|
[ "$RB_BIOS_DIR" = "-" ] && RB_BIOS_DIR=""
|
|
break
|
|
fi
|
|
done < <(rb_py "$RB_EVENTS" status "$RB_STATE/detect.jsonl" 2>> "$RB_LOG")
|
|
}
|
|
|
|
# The first pack in RB_PACK_PATH, one whose name spells this system first:
|
|
# install.py reads the platform from the same spelling (Name_With_Underscores
|
|
# between two underscores).
|
|
rb_find_pack() {
|
|
local dir file base stem first="" want=""
|
|
if [ -n "${RB_PLATFORM_NAME:-}" ]; then
|
|
want="_${RB_PLATFORM_NAME// /_}_"
|
|
fi
|
|
local IFS=":"
|
|
for dir in $RB_PACK_PATH; do
|
|
[ -d "$dir" ] || continue
|
|
for file in "$dir"/*_BIOS_Pack.zip "$dir"/*_BIOS_Pack.part1of*.zip; do
|
|
[ -f "$file" ] || continue
|
|
base="${file##*/}"
|
|
stem="_${base%%_BIOS_Pack*}_"
|
|
if [ -n "$want" ] && [[ "$stem" == *"$want"* ]]; then
|
|
printf '%s\n' "$file"
|
|
return 0
|
|
fi
|
|
[ -z "$first" ] && first="$file"
|
|
done
|
|
done
|
|
[ -n "$first" ] && printf '%s\n' "$first"
|
|
return 0
|
|
}
|
|
|
|
rb_same_dir() {
|
|
[ -n "$1" ] && [ -n "$2" ] || return 1
|
|
[ "$(stat -L -c '%d:%i' "$1" 2>/dev/null)" = "$(stat -L -c '%d:%i' "$2" 2>/dev/null)" ]
|
|
}
|
|
|
|
# The BIOS folder on the firmware's other card, when it holds a file: a
|
|
# two-card device reads one card (muOS binds MUOS/bios from SD2 when it has
|
|
# one, else from SD1), and the files bought for it often sit on the other.
|
|
rb_other_card() {
|
|
local dir
|
|
local IFS=":"
|
|
for dir in ${RB_OTHER_CARDS:-}; do
|
|
[ -d "$dir" ] || continue
|
|
if rb_same_dir "$dir" "${RB_BIOS_DEST:-}" || rb_same_dir "$dir" "${RB_BIOS_DIR:-}"; then
|
|
continue
|
|
fi
|
|
if [ -n "$(find "$dir" -type f -print 2>/dev/null | head -n 1)" ]; then
|
|
printf '%s\n' "$dir"
|
|
return 0
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
rb_pack_text() {
|
|
local dir="${RB_PACK_PATH%%:*}" pack
|
|
if [ -n "${RB_PLATFORM_NAME:-}" ]; then
|
|
pack="the BIOS pack whose name contains ${RB_PLATFORM_NAME// /_}"
|
|
else
|
|
pack="the BIOS pack for this system (a file whose name ends in _BIOS_Pack.zip)"
|
|
fi
|
|
printf '%s\n' "No network connection. To install without one, download $pack from $RB_RELEASES on a computer, copy it into $dir on the SD card, then start RetroBIOS again. Or connect to Wi-Fi and start again."
|
|
}
|
|
|
|
# --- Terminal -------------------------------------------------------------
|
|
|
|
rb_graphical() {
|
|
[ -n "${WAYLAND_DISPLAY:-}" ] || [ -n "${DISPLAY:-}" ] \
|
|
|| pgrep -x 'sway|weston|labwc|cage|gamescope|Xorg|X' >/dev/null 2>&1
|
|
}
|
|
|
|
# A terminal app the graphical session can show, full screen. A launcher
|
|
# that knows its own (muOS's muxterm) sets RB_TERMAPP after sourcing this.
|
|
rb_terminal_app() {
|
|
if [ "${#RB_TERMAPP[@]}" -gt 0 ]; then
|
|
return 0
|
|
fi
|
|
if [ -z "${WAYLAND_DISPLAY:-}" ] && [ -z "${DISPLAY:-}" ]; then
|
|
return 1
|
|
fi
|
|
if command -v foot >/dev/null 2>&1; then
|
|
RB_TERMAPP=(foot -F --)
|
|
elif command -v xterm >/dev/null 2>&1; then
|
|
RB_TERMAPP=(xterm -fullscreen -e)
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Sets RB_ROUTE to the first of RB_ROUTES that can show text: here, app,
|
|
# vt, or none.
|
|
rb_route() {
|
|
local route
|
|
RB_ROUTE="none"
|
|
for route in $RB_ROUTES; do
|
|
case "$route" in
|
|
here)
|
|
if [ -t 0 ] && [ -t 1 ]; then
|
|
RB_ROUTE="here"
|
|
return 0
|
|
fi
|
|
;;
|
|
app)
|
|
if rb_terminal_app; then
|
|
RB_ROUTE="app"
|
|
return 0
|
|
fi
|
|
;;
|
|
vt)
|
|
# A compositor or X server holds the console it runs on: text
|
|
# written there is never seen, so the console needs a switch.
|
|
if rb_graphical; then
|
|
if [ -n "${RB_VT_SWITCH:-}" ] && command -v chvt >/dev/null 2>&1 \
|
|
&& [ -n "$(rb_active_vt)" ]; then
|
|
RB_ROUTE="vt"
|
|
return 0
|
|
fi
|
|
elif [ -c "$RB_CONSOLE" ]; then
|
|
RB_ROUTE="vt"
|
|
return 0
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# The largest Terminus font that keeps 56 columns and 20 rows, the way
|
|
# dArkOS's own tools pick one per screen (Lat7-Terminus16 on 480x320,
|
|
# TerminusBold22x11 on 640x480, 32x16 on 1024x768). Only changed when the
|
|
# font in place can be put back: saved with setfont -O, or the
|
|
# Lat7-Terminus20x10 ArkOS tools restore.
|
|
rb_font_set() {
|
|
command -v setfont >/dev/null 2>&1 || return 0
|
|
local size width height spec fh fw name file="" fallback
|
|
size=$(cat "$RB_FB" 2>/dev/null) || return 0
|
|
width="${size%%,*}"
|
|
height="${size##*,}"
|
|
case "$width$height" in
|
|
''|*[!0-9]*) return 0 ;;
|
|
esac
|
|
for spec in "32 16 Lat7-TerminusBold32x16" "28 14 Lat7-TerminusBold28x14" \
|
|
"22 11 Lat7-TerminusBold22x11" "20 10 Lat7-TerminusBold20x10" \
|
|
"16 8 Lat7-Terminus16"; do
|
|
read -r fh fw name <<< "$spec"
|
|
if [ $((width / fw)) -ge 56 ] && [ $((height / fh)) -ge 20 ] \
|
|
&& [ -f "$RB_FONTS/$name.psf.gz" ]; then
|
|
file="$RB_FONTS/$name.psf.gz"
|
|
break
|
|
fi
|
|
done
|
|
if [ -z "$file" ] && [ $((width / 8)) -ge 40 ] && [ $((height / 16)) -ge 12 ] \
|
|
&& [ -f "$RB_FONTS/Lat7-Terminus16.psf.gz" ]; then
|
|
file="$RB_FONTS/Lat7-Terminus16.psf.gz"
|
|
fi
|
|
[ -n "$file" ] || return 0
|
|
if $ESUDO setfont -C "$RB_TTY" -O "$RB_STATE/font.psf" "$file" >> "$RB_LOG" 2>&1 \
|
|
&& [ -s "$RB_STATE/font.psf" ]; then
|
|
RB_FONT_BACK="$RB_STATE/font.psf"
|
|
return 0
|
|
fi
|
|
fallback="$RB_FONTS/Lat7-Terminus20x10.psf.gz"
|
|
if [ -f "$fallback" ] && $ESUDO setfont -C "$RB_TTY" "$file" >> "$RB_LOG" 2>&1; then
|
|
RB_FONT_BACK="$fallback"
|
|
fi
|
|
}
|
|
|
|
rb_font_restore() {
|
|
if [ -n "$RB_FONT_BACK" ]; then
|
|
$ESUDO setfont -C "$RB_TTY" "$RB_FONT_BACK" >> "$RB_LOG" 2>&1
|
|
fi
|
|
RB_FONT_BACK=""
|
|
}
|
|
|
|
# The number of the console in front, which a switch has to come back to.
|
|
rb_active_vt() {
|
|
local active
|
|
active=$(cat "$RB_VT_ACTIVE" 2>/dev/null)
|
|
active="${active#tty}"
|
|
case "$active" in
|
|
''|*[!0-9]*) active=$(fgconsole 2>/dev/null) ;;
|
|
esac
|
|
case "$active" in
|
|
''|*[!0-9]*) return 1 ;;
|
|
esac
|
|
printf '%s\n' "$active"
|
|
}
|
|
|
|
rb_vt_enter() {
|
|
RB_TTY="$RB_CONSOLE"
|
|
if rb_graphical && [ -n "${RB_VT_SWITCH:-}" ]; then
|
|
RB_VT_BACK=$(rb_active_vt)
|
|
RB_TTY="${RB_VT_PREFIX:-/dev/tty}$RB_VT_SWITCH"
|
|
$ESUDO chvt "$RB_VT_SWITCH" >> "$RB_LOG" 2>&1
|
|
fi
|
|
if [ ! -r "$RB_TTY" ] || [ ! -w "$RB_TTY" ]; then
|
|
$ESUDO chmod 666 "$RB_TTY" >> "$RB_LOG" 2>&1
|
|
fi
|
|
RB_STTY=$(stty -g < "$RB_TTY" 2>/dev/null)
|
|
printf '\033c' > "$RB_TTY"
|
|
rb_font_set
|
|
}
|
|
|
|
rb_vt_leave() {
|
|
[ -n "$RB_TTY" ] || return 0
|
|
rb_font_restore
|
|
printf '\033c' > "$RB_TTY" 2>/dev/null
|
|
if [ -n "$RB_STTY" ]; then
|
|
stty "$RB_STTY" < "$RB_TTY" 2>/dev/null
|
|
fi
|
|
if [ -n "$RB_VT_BACK" ]; then
|
|
$ESUDO chvt "$RB_VT_BACK" >> "$RB_LOG" 2>&1
|
|
fi
|
|
RB_TTY=""
|
|
RB_STTY=""
|
|
RB_VT_BACK=""
|
|
}
|
|
|
|
# gptokeyb on a name only this installer's processes carry: kill mode runs
|
|
# pkill -f on it, and the path of install.py is in the command line of the
|
|
# interface and of every run it starts, never in python3's alone.
|
|
rb_mapper_start() {
|
|
[ -n "${RB_MAPPER:-}" ] || return 0
|
|
# shellcheck disable=SC2086 # RB_MAPPER is a command and its flags, as $GPTOKEYB is
|
|
$RB_MAPPER "$RB_INSTALL" -c "$RB_GPTK" >> "$RB_LOG" 2>&1 &
|
|
RB_MAPPER_PID=$!
|
|
}
|
|
|
|
rb_mapper_stop() {
|
|
[ -n "$RB_MAPPER_PID" ] || return 0
|
|
$ESUDO kill "$RB_MAPPER_PID" >/dev/null 2>&1
|
|
wait "$RB_MAPPER_PID" 2>/dev/null
|
|
RB_MAPPER_PID=""
|
|
}
|
|
|
|
# A screen of text on the current terminal, closed by any button.
|
|
rb_message() {
|
|
local cols
|
|
cols=$(stty size 2>/dev/null | awk '{print $2}')
|
|
case "$cols" in
|
|
''|*[!0-9]*) cols=40 ;;
|
|
esac
|
|
[ "$cols" -ge 20 ] || cols=40
|
|
printf '\033[2J\033[H'
|
|
printf 'RetroBIOS\n\n'
|
|
printf '%s\n' "$1" | fold -s -w $((cols - 1))
|
|
printf '\n%s\n' "Press A or Enter to close."
|
|
read -r -s -n 1 -t "${RB_MESSAGE_WAIT:-300}" _
|
|
printf '\033[2J\033[H'
|
|
}
|
|
|
|
# A question on the current terminal: true for A or Enter, false for B, Esc,
|
|
# any other button or no answer.
|
|
rb_ask() {
|
|
local cols key
|
|
cols=$(stty size 2>/dev/null | awk '{print $2}')
|
|
case "$cols" in
|
|
''|*[!0-9]*) cols=40 ;;
|
|
esac
|
|
[ "$cols" -ge 20 ] || cols=40
|
|
printf '\033[2J\033[H'
|
|
printf 'RetroBIOS\n\n'
|
|
printf '%s\n' "$1" | fold -s -w $((cols - 1))
|
|
printf '\n%s\n' "A or Enter: yes. B or Esc: no."
|
|
if ! IFS= read -r -s -n 1 -t "${RB_MESSAGE_WAIT:-300}" key; then
|
|
printf '\033[2J\033[H'
|
|
return 1
|
|
fi
|
|
# The rest of an escape sequence (an arrow) is not a second answer.
|
|
read -r -s -t 0.05 -n 8 _ 2>/dev/null
|
|
printf '\033[2J\033[H'
|
|
[ -z "$key" ] || [ "$key" = " " ]
|
|
}
|
|
|
|
# --- The run --------------------------------------------------------------
|
|
|
|
# The interface on the current terminal: install.py --tui, from a pack on the
|
|
# card when there is no network. The status also goes to RB_STATE/status,
|
|
# since a terminal app does not always pass its child's on.
|
|
rb_here() {
|
|
local status pack args=()
|
|
mkdir -p "$RB_STATE"
|
|
rm -f "$RB_STATE/status"
|
|
case "${TERM:-}" in
|
|
''|dumb|unknown) export TERM=xterm ;;
|
|
esac
|
|
if [ -z "$RB_PYTHON" ]; then
|
|
rb_message "$(rb_python_text)"
|
|
printf '1\n' > "$RB_STATE/status"
|
|
return 1
|
|
fi
|
|
local other
|
|
if ! rb_online; then
|
|
# Detection also names the BIOS folder the other card must not be.
|
|
rb_detect
|
|
other=$(rb_other_card)
|
|
if [ -n "$other" ] && rb_ask "Copy the BIOS files from your other card? $other holds files. RetroBIOS copies the ones this system needs, each checked by its hash, and leaves that card as it is."; then
|
|
args=(--from "$other")
|
|
fi
|
|
pack=$(rb_find_pack)
|
|
if [ -z "$pack" ]; then
|
|
if [ "${#args[@]}" -gt 0 ]; then
|
|
# --from needs the list of files, which comes from the network or a pack.
|
|
rb_message "$(rb_pack_text) The pack carries the list of files: with it, the files on your other card are copied too."
|
|
else
|
|
rb_message "$(rb_pack_text)"
|
|
fi
|
|
printf '1\n' > "$RB_STATE/status"
|
|
return 1
|
|
fi
|
|
args+=(--from-pack "$pack")
|
|
if [ -n "$RB_PLATFORM" ]; then
|
|
args+=(--platform "$RB_PLATFORM")
|
|
fi
|
|
fi
|
|
rb_py "$RB_INSTALL" --tui "${args[@]}" "$@" 2> "$RB_STATE/tui.err"
|
|
status=$?
|
|
cat "$RB_STATE/tui.err" >> "$RB_LOG" 2>/dev/null
|
|
if [ "$status" = "2" ] && grep -q -- "--tui cannot open" "$RB_STATE/tui.err"; then
|
|
status=125
|
|
if [ "${RB_FALLBACK:-0}" != "1" ]; then
|
|
rb_message "$(tail -n 1 "$RB_STATE/tui.err") The messages are in $RB_LOG."
|
|
fi
|
|
elif [ "$status" != "0" ] && [ -s "$RB_STATE/tui.err" ]; then
|
|
rb_message "$(tail -n 1 "$RB_STATE/tui.err") The messages are in $RB_LOG."
|
|
fi
|
|
printf '%s\n' "$status" > "$RB_STATE/status"
|
|
return "$status"
|
|
}
|
|
|
|
rb_cleanup() {
|
|
rb_vt_leave
|
|
rb_mapper_stop
|
|
}
|
|
|
|
# The whole session: the terminal, the mapper, the interface, then the
|
|
# console, its font and the mapper put back. Returns the interface's status,
|
|
# 125 when no terminal could show it.
|
|
rb_launch() {
|
|
local status
|
|
mkdir -p "$RB_STATE"
|
|
if ! rb_route; then
|
|
printf '%s\n' "RetroBIOS: no terminal to show the interface on (routes: $RB_ROUTES)" >> "$RB_LOG"
|
|
return 125
|
|
fi
|
|
export RB_DIR RB_INSTALL RB_EVENTS RB_STATE RB_LOG RB_PACK_PATH RB_FALLBACK
|
|
export RB_OTHER_CARDS RB_BIOS_DEST
|
|
export RB_PYTHON RB_PYTHONHOME RB_PYTHON_PROBLEM RB_MESSAGE_WAIT RB_ROUTE_TABLE
|
|
trap 'rb_cleanup; exit 143' TERM HUP
|
|
rb_mapper_start
|
|
case "$RB_ROUTE" in
|
|
here)
|
|
rb_here "$@"
|
|
status=$?
|
|
;;
|
|
app)
|
|
"${RB_TERMAPP[@]}" bash "$RB_LIB" here "$@" 2>> "$RB_LOG"
|
|
status=$(cat "$RB_STATE/status" 2>/dev/null)
|
|
;;
|
|
vt)
|
|
rb_vt_enter
|
|
export TERM=linux
|
|
# shellcheck disable=SC2094 # a console is read and written at once by design
|
|
rb_here "$@" < "$RB_TTY" > "$RB_TTY" 2>> "$RB_LOG"
|
|
status=$?
|
|
;;
|
|
esac
|
|
rb_cleanup
|
|
trap - TERM HUP
|
|
return "${status:-1}"
|
|
}
|
|
|
|
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
case "${1:-}" in
|
|
here)
|
|
shift
|
|
rb_here "$@"
|
|
exit $?
|
|
;;
|
|
*)
|
|
printf '%s\n' "usage: bash tui.txt here [install.py options]" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
fi
|