Skip to content

Instantly share code, notes, and snippets.

@Langerz82
Last active Sep 21, 2021
Embed
What would you like to do?
Retropie - Rom Emulator Batch Finder
#!/bin/bash
###############################################################################
# Retropie - Rom Emulator Batch Finder.
#
# This file loops through emulator roms and the emulators and tries to select
# the right ones for the rom. It calls a modified runcommand.sh from retropie.
# I am releasing it under Open Source so others may find it useful.
#
# TODO: Make source work with call runemulators.sh for return variable,
# instead of using a temp file.
#
# INSTRUCTIONS: Place both files in /opt/retropie/supplementary/runcommand
# Make sure the ROMDIR is correct that you want to check.
# Give both files exec permission and exec this file.
#
# Author: Langerz82
# License: GPLv3 License
# Copyright: 2020 GPLv3 Langerz82
#
###############################################################################
# Enter the path of the ROMS you wish to check.
ROMDIR="/media/pi/roms/roms/arcade"
MISSINGDIR="$ROMDIR/missing"
SYSTEM="mame-advmame"
ROOTDIR="/opt/retropie"
CONFIGDIR="$ROOTDIR/configs"
CONF_ROOT="$CONFIGDIR/$SYSTEM"
EMU_SYS_CONF="$CONF_ROOT/emulators.cfg"
SCRIPTDIR="$ROOTDIR/supplementary/runcommand"
LOG="$SCRIPTDIR/log.txt"
DONE_TEXT="$SCRIPTDIR/done.txt"
MISSING_TEXT="$SCRIPTDIR/missing.txt"
KEEP_TEXT="$SCRIPTDIR/keep.txt"
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
emulators=()
emu_count=0
function find_emulator() {
local i=1
emulators+=("")
while read line; do
local line=(${line/=/ })
local id=${line[0]}
[[ "$id" == "" ]] && continue
[[ "$id" == "default" ]] && continue
emulators+=("$id")
((i++))
done < <(sort "$EMU_SYS_CONF")
emu_count=$i
}
files_to_keep=()
function keep_files() {
while read line; do
local filename=${line[0]}
[[ "$filename" == "" ]] && continue
files_to_keep+=("$filename")
done < <(sort "keep.txt")
}
files_done=()
function done_files() {
while read line; do
local filename=${line[0]}
files_done+=("$filename")
done < <(sort "$DONE_TEXT")
}
function move_rom() {
echo "$ROMNAME" >> "$DONE_TEXT"
files_done+=("$ROMNAME")
echo "$ROMNAME" >> "$MISSING_TEXT"
mv "$ROMDIR/$ROMNAME" "$MISSINGDIR/"
return TRUE
}
if [[ ! -e "$KEEP_TEXT" ]]; then
touch "$KEEP_TEXT"
fi
if [[ ! -e "$DONE_TEXT" ]]; then
touch "$DONE_TEXT"
fi
if [[ ! -e "$MISSING_TEXT" ]]; then
touch "$MISSING_TEXT"
fi
find_emulator
keep_files
done_files
rm -f "$LOG"
mkdir -p "$MISSINGDIR"
cd $ROMDIR
for FILE_ROM in $ROMDIR/*.{zip,7z}
do
ROMNAME=$(basename -- "$FILE_ROM")
containsElement "$ROMNAME" "${files_to_keep[@]}"
if [[ "$?" == 0 ]]; then
continue
fi
containsElement "$ROMNAME" "${files_done[@]}"
if [[ "$?" == 0 ]]; then
continue
fi
i=0
emulators_ran=()
while [ $i -lt $emu_count ]
do
containsElement "${emulators[$i]}" "${emulators_ran[@]}"
if [[ "$?" == 0 ]]; then
((i++))
[[ $i == $emu_count ]] && move_rom | break
continue
fi
echo "DETECTING $ROMNAME WITH ${emulators[$i]}" &>> "$LOG"
EST_TIME=$( du --block=16384K "$FILE_ROM" | sed "s/[^0-9].*//g" )
[[ -z "${EST_TIME##*[!0-9]*}" ]] && EST_TIME=1
SH_COMMAND="$SCRIPTDIR/runemulators.sh 0 _SYS_ $SYSTEM \"$ROMNAME\" $EST_TIME ${emulators[$i]}"
eval $SH_COMMAND
source "result.tmp"
emulators_ran+=("$EMULATOR")
if [[ $EMU_FOUND > 0 ]]; then
echo "$ROMNAME" >> "$DONE_TEXT"
echo "EMU_FOUND=$EMU_FOUND" >> "$LOG"
files_done+=("$ROMNAME")
break
fi
((i++))
[[ $i == $emu_count ]] && move_rom | break
done
done
cd "$SCRIPTDIR"
rm -f "result.tmp"
#!/bin/bash
# This file is part of The RetroPie Project
#
# The RetroPie Project is the legal property of its developers, whose names are
# too numerous to list here. Please refer to the COPYRIGHT.md file distributed with this source.
#
# See the LICENSE.md file at the top-level directory of this distribution and
# at https://raw.githubusercontent.com/RetroPie/RetroPie-Setup/master/LICENSE.md
#
## @file supplementary/runcommand/runcommand.sh
## @brief runcommand launching script
## @copyright GPLv3
## @details
## @par Usage
##
## `runcommand.sh VIDEO_MODE COMMAND SAVE_NAME`
##
## or
##
## `runcommand.sh VIDEO_MODE _SYS_/_PORT_ SYSTEM ROM`
##
## Video mode switching is supported on X11, KMS and Raspberry Pi (legacy graphics) systems
##
## Automatic video mode selection (all):
##
## * VIDEO_MODE = 0: use the current video mode
##
## Automatic video mode (Raspberry Pi legacy graphics):
##
## * VIDEO_MODE = 1: set video mode to 640x480 (4:3) or 720x480 (16:9) @60hz
## * VIDEO_MODE = 4: set video mode to 1024x768 (4:3) or 1280x720 (16:9) @60hz
##
## Manual video mode selection (Raspberry Pi legacy graphics):
##
## * VIDEO_MODE = "CEA-#": set video mode to CEA mode #
## * VIDEO_MODE = "DMT-#": set video mode to DMT mode #
## * VIDEO_MODE = "PAL/NTSC-RATIO": set mode to SD output with RATIO of 4:3 / 16:10 or 16:9
##
## Manual video mode selection (KMS):
##
## * VIDEO_MODE = "CRTCID-MODEID": set video mode to CRTC connector id and mode id
##
## Manual video mode selection (X11):
##
## * VIDEO_MODE = "OUTPUT:MODEID": set video mode to connected output name and mode index
##
## @note
## Video mode switching only happens if the monitor reports the modes as available
## (via tvservice) and the requested mode differs from the currently active mode
##
## If `_SYS_` or `_PORT_` is provided for the second parameter, the commandline
## will be extracted from `/opt/retropie/configs/SYSTEM/emulators.cfg` with
## `%ROM%` `%BASENAME%` being replaced with the ROM parameter. This is the
## default mode used when launching in RetroPie so the user can switch emulator
## used as well as other options from the runcommand GUI.
##
## If SAVE_NAME is included, that is used for loading and saving of video output
## modes as well as SDL1 dispmanx settings for the current COMMAND. If omitted,
## the binary name is used as a key for the loading and saving. The savename is
## also displayed in the video output menu (detailed below), so for our purposes
## we send the emulator module id, which is somewhat descriptive yet short.
##
## On launch this script waits for 2 second for a key or joystick press. If
## pressed the GUI is shown, where a user can set video modes, default emulators
## and other options (depending what is being launched).
ROOTDIR="/opt/retropie"
CONFIGDIR="$ROOTDIR/configs"
LOG="/dev/shm/runcommand.log"
RUNCOMMAND_CONF="$CONFIGDIR/all/runcommand.cfg"
VIDEO_CONF="$CONFIGDIR/all/videomodes.cfg"
EMU_CONF="$CONFIGDIR/all/emulators.cfg"
DISPMANX_CONF="$CONFIGDIR/all/dispmanx.cfg"
RETRONETPLAY_CONF="$CONFIGDIR/all/retronetplay.cfg"
# modesetting tools
TVSERVICE="/opt/vc/bin/tvservice"
KMSTOOL="$ROOTDIR/supplementary/mesa-drm/modetest"
XRANDR="xrandr"
RM_PLAYLIST="$ROOTDIR/supplementary/runcommand/./rm_playlist_entry.py"
source "$ROOTDIR/lib/inifuncs.sh"
function get_config() {
declare -Ag MODE_MAP
MODE_MAP[1-CEA-4:3]="CEA-1"
MODE_MAP[1-DMT-4:3]="DMT-4"
MODE_MAP[1-CEA-16:9]="CEA-1"
MODE_MAP[4-CEA-4:3]="DMT-16"
MODE_MAP[4-DMT-4:3]="DMT-16"
MODE_MAP[4-CEA-16:9]="CEA-4"
if [[ -f "$RUNCOMMAND_CONF" ]]; then
iniConfig " = " '"' "$RUNCOMMAND_CONF"
iniGet "governor"
GOVERNOR="$ini_value"
iniGet "use_art"
USE_ART="$ini_value"
iniGet "disable_joystick"
DISABLE_JOYSTICK="$ini_value"
iniGet "disable_menu"
DISABLE_MENU="$ini_value"
iniGet "image_delay"
IMAGE_DELAY="$ini_value"
[[ -z "$IMAGE_DELAY" ]] && IMAGE_DELAY=2
fi
if [[ -n "$DISPLAY" ]] && $XRANDR &>/dev/null; then
HAS_MODESET="x11"
# copy kms tool output to global variable to avoid multiple invocations
elif KMS_BUFFER="$($KMSTOOL -r 2>/dev/null)"; then
HAS_MODESET="kms"
elif [[ -f "$TVSERVICE" ]]; then
HAS_MODESET="tvs"
fi
}
function start_joy2key() {
[[ "$DISABLE_JOYSTICK" -eq 1 ]] && return
# get the first joystick device (if not already set)
if [[ -c "$__joy2key_dev" ]]; then
JOY2KEY_DEV="$__joy2key_dev"
else
JOY2KEY_DEV="/dev/input/jsX"
fi
# if joy2key.py is installed run it with cursor keys for axis, and enter + tab for buttons 0 and 1
if [[ -f "$ROOTDIR/supplementary/runcommand/joy2key.py" && -n "$JOY2KEY_DEV" ]] && ! pgrep -f joy2key.py >/dev/null; then
# call joy2key.py: arguments are curses capability names or hex values starting with '0x'
# see: http://pubs.opengroup.org/onlinepubs/7908799/xcurses/terminfo.html
"$ROOTDIR/supplementary/runcommand/joy2key.py" "$JOY2KEY_DEV" kcub1 kcuf1 kcuu1 kcud1 0x0a 0x09
JOY2KEY_PID=$(pgrep -f joy2key.py)
# ensure coherency between on-screen prompts and actual button mapping functionality
sleep 0.3
fi
}
function stop_joy2key() {
if [[ -n "$JOY2KEY_PID" ]]; then
kill "$JOY2KEY_PID"
JOY2KEY_PID=""
sleep 1
fi
}
function get_params() {
MODE_REQ="$1"
COMMAND="$2"
[[ -z "$MODE_REQ" || -z "$COMMAND" ]] && return 1
CONSOLE_OUT=0
# if the COMMAND is _SYS_, or _PORT_ arg 3 should be system name, and arg 4 rom/game, and we look up the configured system for that combination
if [[ "$COMMAND" == "_SYS_" || "$COMMAND" == "_PORT_" ]]; then
# if the rom is actually a special +Start System.sh script, we should launch the script directly.
if [[ "$4" =~ \/\+Start\ (.+)\.sh$ ]]; then
# extract emulator from the name (and lowercase it)
EMULATOR=${BASH_REMATCH[1],,}
IS_SYS=0
COMMAND="bash \"$4\""
SYSTEM="$3"
[[ -z "$SYSTEM" ]] && return 1
else
IS_SYS=1
SYSTEM="$3"
ROM="$4"
EST_TIME="$5"
EMU_ROM="$6"
ROM_BN_EXT="${ROM##*/}"
ROM_BN="${ROM_BN_EXT%.*}"
if [[ "$COMMAND" == "_PORT_" ]]; then
CONF_ROOT="$CONFIGDIR/ports/$SYSTEM"
EMU_SYS_CONF="$CONF_ROOT/emulators.cfg"
IS_PORT=1
else
CONF_ROOT="$CONFIGDIR/$SYSTEM"
EMU_SYS_CONF="$CONF_ROOT/emulators.cfg"
IS_PORT=0
fi
SYS_SAVE_ROM_OLD="a$(echo "$SYSTEM$ROM" | md5sum | cut -d" " -f1)"
SYS_SAVE_ROM="$(clean_name "${SYSTEM}_${ROM_BN}")"
[[ -z "$SYSTEM" ]] && return 1
get_sys_command
fi
else
IS_SYS=0
CONSOLE_OUT=1
EMULATOR="$3"
# if we have an emulator name (such as module_id) we use that for storing/loading parameters for video output/dispmanx
# if the parameter is empty we use the name of the binary (to avoid breakage with out of date emulationstation configs)
[[ -z "$EMULATOR" ]] && EMULATOR="${COMMAND/% */}"
fi
NETPLAY=0
return 0
}
function clean_name() {
local name="$1"
name="${name//\//_}"
name="${name//[^a-zA-Z0-9_\-]/}"
echo "$name"
}
function set_save_vars() {
# convert emulator name / binary to a names usable as variables in our config files
SAVE_EMU="$(clean_name "$EMULATOR")"
SAVE_ROM_OLD=r$(echo "$COMMAND" | md5sum | cut -d" " -f1)
if [[ "$IS_SYS" -eq 1 ]]; then
SAVE_ROM="${SAVE_EMU}_$(clean_name "$ROM_BN")"
else
SAVE_ROM="$SAVE_EMU"
fi
}
function get_all_tvs_modes() {
declare -Ag MODE
local group
for group in CEA DMT; do
while read -r line; do
local id="$(echo "$line" | grep -oE "mode [0-9]*" | cut -d" " -f2)"
local info="$(echo "$line" | cut -d":" -f2-)"
info=${info/ /}
if [[ -n "$id" ]]; then
MODE_ID+=($group-$id)
MODE[$group-$id]="$info"
fi
done < <($TVSERVICE -m $group)
done
local aspect
for group in "NTSC" "PAL"; do
for aspect in "4:3" "16:10" "16:9"; do
MODE_ID+=($group-$aspect)
MODE[$group-$aspect]="SDTV - $group-$aspect"
done
done
}
function get_all_kms_modes() {
declare -Ag MODE
local default_mode="$(echo "$KMS_BUFFER" | grep -Em1 "^Mode:.*(driver|userdef).*crtc")"
local crtc="$(echo "$default_mode" | awk '{ print $(NF-1) }')"
local crtc_encoder="$(echo "$KMS_BUFFER" | grep "Encoder map:" | awk -v crtc="$crtc" '$5 == crtc { print $3 }')"
local info
local line
local mode_id
# add default mode as fallback in case real mode cannot be mapped
MODE[def-def]="$(echo "$default_mode" | awk '{--NF --NF --NF; print}' | cut -c7-)"
while read -r line; do
# encoder id
encoder_id="$(echo "$line" | awk '{ print $(NF-1) }')"
# only match encoders that are linked to the currently active crtc
if [[ "$encoder_id" == "$crtc_encoder" ]]; then
# mode id
mode_id="$(echo "$line" | awk '{ print $NF }')"
# make output more human-readable
info="$(echo "$line" | awk '{--NF --NF --NF; print}' | cut -c7-)"
# populate resolution into arrays (using mapped crtc encoder value)
MODE_ID+=($crtc-$mode_id)
MODE[$crtc-$mode_id]="$info"
# if string matches default mode, add a special mapped entry
[[ "$default_mode" =~ "$info" ]] && MODE[map-map]="$crtc $mode_id"
fi
done < <(echo "$KMS_BUFFER" | grep "Mode:" | grep "connector")
}
function get_all_x11_modes()
{
declare -Ag MODE
local id
local line
while read -r id; do
# populate CONNECTOR:0xID into an array
MODE_ID+=($id) # output:id as in (hdmi:0x44)
read -r line
# array is x/y resolution @ vertical refresh rate ( details )
MODE[$id]="$line"
done < <( $XRANDR --verbose | awk '
# defines the type of line
# true is the "header" (output and id)
# false is the "description" (Mode: and everything that begins with a space)
{ type = /^[^ \t]+/ }
# Exit after the first output
type && output {exit} # New header and output set means new output
# many outputs can be connected, but only the ones with the id are in use.
# output must be connected and have an (id)
type && / connected/ && /\(0x[0-9a-f]+\)/ {
output=$1; next
}
# parse mode and lines
# If we are in a "description", and output is set (output being what we want)
# And if $2 is an id, we are in a video mode description line
!type && output && $2 ~ /^\(0x[0-9a-f]+\)$/ {
# Print CRTC identifier (CONNECTOR:0xID)
print output ":" substr($2,2,length($2)-2) # id
# get rid of what we printed
$1="";$2=""
sub(/^[ \t]+/,"") # trim spaces
# save rest of the line
info=$0
# Save width from the 2nd line of the video mode
getline; width=$3
# Save height & vrefresh from the 3rd line of video mode
getline; height=$3; vrefresh=$NF
# Print video mode details
print width "x" height " @ " vrefresh " (" info ")"
}
')
}
function get_tvs_mode_info() {
local status="$($TVSERVICE -s)"
local temp
local mode_info=()
# get mode type / id
if [[ "$status" =~ (PAL|NTSC) ]]; then
temp=($(echo "$status" | grep -oE "(PAL|NTSC) (4:3|16:10|16:9)"))
else
temp=($(echo "$status" | grep -oE "(CEA|DMT) \([0-9]+\)"))
fi
mode_info[0]="${temp[0]}"
mode_info[1]="${temp[1]//[()]/}"
# get mode resolution
temp=$(echo "$status" | cut -d"," -f2 | grep -oE "[0-9]+x[0-9]+")
temp=(${temp/x/ })
mode_info[2]="${temp[0]}"
mode_info[3]="${temp[1]}"
# get aspect ratio
temp=$(echo "$status" | grep -oE "([0-9]+:[0-9]+)")
mode_info[4]="$temp"
# get refresh rate
temp=$(echo "$status" | grep -oE "[0-9\.]+Hz" | cut -d"." -f1)
mode_info[5]="$temp"
echo "${mode_info[@]}"
}
function get_kms_mode_info() {
local mode_id=(${1/-/ })
local mode_info=()
local status
if [[ -z "${mode_id[*]}" ]]; then
if [[ -n "${MODE[map-map]}" ]]; then
# use mapped mode directly
mode_id=(${MODE[map-map]})
else
# use fallback mode
mode_id=(def def)
fi
fi
# split resolution
status=(${MODE[${mode_id[0]}-${mode_id[1]}]/x/ })
# get crtc id
mode_info[0]="${mode_id[0]}"
# get mode id
mode_info[1]="${mode_id[1]}"
# get mode resolution
mode_info[2]="${status[0]}"
mode_info[3]="${status[1]}"
# get aspect ratio
mode_info[4]="${status[5]}"
# get refresh rate
mode_info[5]="${status[3]}"
echo "${mode_info[@]}"
}
function get_x11_mode_info() {
local mode_id=(${1/:/ })
local mode_info=()
local status
if [[ -z "$mode_id" ]]; then
# determine current output
mode_id[0]="$($XRANDR --verbose | awk '/ connected/ { print $1;exit }')"
# determine current mode id & strip brackets
mode_id[1]="$($XRANDR --verbose | awk '/ connected/ {print;exit}' | grep -o "(0x[a-f0-9]\{1,\})")"
mode_id[1]="$(echo ${mode_id[1]:1:-1})"
fi
# mode type corresponds to the currently connected output name
mode_info[0]="${mode_id[0]}"
# get mode id
mode_info[1]="${mode_id[1]}"
# get status line and split resolution
status=(${MODE[${mode_id[0]}:${mode_id[1]}]/x/ })
# get resolution
mode_info[2]="${status[0]}"
mode_info[3]="${status[1]}"
# aspect ratio cannot be determined for X11
mode_info[4]="n/a"
# get refresh rate (stripping Hz, rounded to integer)
mode_info[5]="$(LC_NUMERIC=C printf '%.0f\n' ${status[3]::-2})"
echo "${mode_info[@]}"
}
function default_process() {
local config="$1"
local mode="$2"
local key="$3"
local value="$4"
iniConfig " = " '"' "$config"
case "$mode" in
get)
iniGet "$key"
echo "$ini_value"
;;
set)
iniSet "$key" "$value"
;;
del)
iniDel "$key"
;;
esac
}
function default_mode() {
local mode="$1"
local type="$2"
local value="$3"
local key
case "$type" in
vid_emu)
key="$SAVE_EMU"
;;
vid_rom_old)
key="$SAVE_ROM_OLD"
;;
vid_rom)
key="$SAVE_ROM"
;;
fb_emu)
key="${SAVE_EMU}_fb"
;;
fb_rom_old)
key="${SAVE_ROM_OLD}_fb"
;;
fb_rom)
key="${SAVE_ROM}_fb"
;;
render)
key="${SAVE_EMU}_render"
;;
esac
default_process "$VIDEO_CONF" "$mode" "$key" "$value"
}
function default_emulator() {
local mode="$1"
local type="$2"
local value="$3"
local key
local config="$EMU_SYS_CONF"
case "$type" in
emu_sys)
key="default"
;;
emu_cmd)
key="$EMULATOR"
;;
emu_rom_old)
key="$SYS_SAVE_ROM_OLD"
config="$EMU_CONF"
;;
emu_rom)
key="$SYS_SAVE_ROM"
config="$EMU_CONF"
;;
esac
default_process "$config" "$mode" "$key" "$value"
}
function load_mode_defaults() {
local separator="-"
[[ "$HAS_MODESET" == "x11" ]] && separator=":"
local temp
MODE_ORIG=()
if [[ -n "$HAS_MODESET" ]]; then
# populate available modes
[[ -z "$MODE_ID" ]] && get_all_${HAS_MODESET}_modes
# get current mode / aspect ratio
MODE_ORIG=($(get_${HAS_MODESET}_mode_info))
MODE_CUR=("${MODE_ORIG[@]}")
MODE_ORIG_ID="${MODE_ORIG[0]}${separator}${MODE_ORIG[1]}"
if [[ "$MODE_REQ" == "0" ]]; then
MODE_REQ_ID="$MODE_ORIG_ID"
elif [[ "$HAS_MODESET" == "tvs" ]]; then
# get default mode for requested mode of 1 or 4
if [[ "$MODE_REQ" =~ (1|4) ]]; then
# if current aspect is anything else like 5:4 / 10:9 just choose a 4:3 mode
local aspect="${MODE_ORIG[4]}"
[[ "$aspect" =~ (4:3|16:9) ]] || aspect="4:3"
temp="${MODE_REQ}-${MODE_ORIG[0]}-$aspect"
MODE_REQ_ID="${MODE_MAP[$temp]}"
else
MODE_REQ_ID="$MODE_REQ"
fi
else
MODE_REQ_ID="$MODE_REQ"
fi
fi
# get default fb_res (if not running on X)
FB_ORIG=()
if [[ -z "$DISPLAY" ]]; then
local status=($(fbset | tr -s '\n'))
FB_ORIG[0]="${status[3]}"
FB_ORIG[1]="${status[4]}"
FB_ORIG[2]="${status[7]}"
fi
# default retroarch render res to config file
RENDER_RES="config"
local mode
if [[ -f "$VIDEO_CONF" ]]; then
# load default video mode for emulator / rom
mode="$(default_mode get vid_emu)"
[[ -n "$mode" ]] && MODE_REQ_ID="$mode"
# get default mode for system + rom combination
# try the old key first and convert to the new key if found
mode="$(default_mode get vid_rom_old)"
if [[ -n "$mode" ]]; then
default_mode del vid_rom_old
default_mode set vid_rom "$mode"
MODE_REQ_ID="$mode"
else
mode="$(default_mode get vid_rom)"
[[ -n "$mode" ]] && MODE_REQ_ID="$mode"
fi
if [[ "$HAS_MODESET" == "tvs" ]]; then
# load default framebuffer res for emulator / rom
mode="$(default_mode get fb_emu)"
[[ -n "$mode" ]] && FB_NEW="$mode"
# get default fb mode for system + rom combination
# try the old key first and convert to the new key if found
mode="$(default_mode get fb_rom_old)"
if [[ -n "$mode" ]]; then
default_mode del fb_rom_old
default_mode set fb_rom "$mode"
FB_NEW="$mode"
else
mode="$(default_mode get fb_rom)"
[[ -n "$mode" ]] && FB_NEW="$mode"
fi
fi
# get default retroarch render resolution for emulator
mode="$(default_mode get render)"
[[ -n "$mode" ]] && RENDER_RES="$mode"
fi
}
function try_emulator() {
[[ -z "$EMU_ROM" ]] && return
EMULATOR="$EMU_ROM"
}
function save_emulator() {
[[ -z "$EMU_ROM" ]] && EMU_ROM="$EMULATOR"
default_emulator set "emu_rom" "$EMU_ROM"
get_sys_command
set_save_vars
load_mode_defaults
}
function choose_mode() {
local mode="$1"
local default="$2"
local options=()
local key
for key in "${MODE_ID[@]}"; do
options+=("$key" "${MODE[$key]}")
done
local cmd=(dialog --default-item "$default" --menu "Choose video output mode" 22 76 16 )
local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
[[ -z "$choice" ]] && return
default_mode set "$mode" "$choice"
load_mode_defaults
}
function choose_emulator() {
local mode="$1"
local default="$2"
local cancel="$3"
local default
local default_id
local options=()
local i=1
while read line; do
# convert key=value to array
local line=(${line/=/ })
local id=${line[0]}
[[ "$id" == "default" ]] && continue
local apps[$i]="$id"
if [[ "$id" == "$default" ]]; then
default_id="$i"
fi
options+=($i "$id")
((i++))
done < <(sort "$EMU_SYS_CONF")
if [[ -z "${options[*]}" ]]; then
dialog --msgbox "No emulator options found for $SYSTEM - Do you have a valid $EMU_SYS_CONF ?" 20 60 >/dev/tty
stop_joy2key
exit 1
fi
local cmd=(dialog $cancel --default-item "$default_id" --menu "Choose default emulator" 22 76 16 )
local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
[[ -z "$choice" ]] && return
default_emulator set "$mode" "${apps[$choice]}"
get_sys_command
set_save_vars
load_mode_defaults
}
function get_resolutions() {
local res=(
"320x224"
"320x240"
"400x240"
"480x320"
"640x480"
"720x480"
"720x576"
"800x480"
"800x600"
"960x720"
"1024x600"
"1024x768"
"1024x800"
"1280x720"
"1280x800"
"1280x960"
"1280x1024"
"1360x768"
"1366x768"
"1920x1080"
)
echo "${res[@]}"
}
function choose_render_res() {
local mode="$1"
local default="$2"
local res=($(get_resolutions))
local i=1
local item
local options=()
for item in "${res[@]}"; do
[[ "$item" == "$default" ]] && default="$i"
options+=($i "$item")
((i++))
done
options+=(
O "Use video output resolution"
C "Use config file resolution"
)
[[ "$default" == "output" ]] && default="O"
[[ "$default" == "config" ]] && default="C"
local cmd=(dialog --default-item "$default" --menu "Choose RetroArch render resolution" 22 76 16 )
local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
[[ -z "$choice" ]] && return
case "$choice" in
O)
choice="output"
;;
C)
choice="config"
;;
*)
choice="${res[$choice-1]}"
;;
esac
default_mode set "$mode" "$choice"
load_mode_defaults
}
function choose_fb_res() {
local mode="$1"
local default="$2"
local res=($(get_resolutions))
local i=1
local item
local options=()
for item in "${res[@]}"; do
options+=($i "$item")
((i++))
done
local cmd=(dialog --default-item "$default" --menu "Choose framebuffer resolution (Useful for X and console apps)" 22 76 16 )
local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
[[ -z "$choice" ]] && return
choice="${res[$choice-1]}"
default_mode set "$mode" "$choice"
load_mode_defaults
}
function user_menu() {
local default
local options=()
local script
local i=1
while read -r script; do
script="${script##*/}"
script="${script%.*}"
options+=($i "$script")
((i++))
done < <(find "$CONFIGDIR/all/runcommand-menu" -type f -name "*.sh" | sort)
local default
local cmd
local choice
local ret
while true; do
cmd=(dialog --default-item "$default" --cancel-label "Back" --menu "Choose option" 22 76 16)
choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
[[ -z "$choice" ]] && return 0
default="$choice"
script="runcommand-menu/${options[choice*2-1]}.sh"
user_script "$script"
ret="$?"
[[ "$ret" -eq 1 || "$ret" -eq 2 ]] && return "$ret"
done
}
function switch_fb_res() {
local res=(${1/x/ })
local res_x="${res[0]}"
local res_y="${res[1]}"
local depth="$2"
[[ -z "$depth" ]] && depth="${FB_ORIG[2]}"
if [[ -z "$res_x" || -z "$res_y" ]]; then
fbset --all -depth 8
fbset --all -depth $depth
else
fbset --all -depth 8
fbset --all --geometry $res_x $res_y $res_x $res_y $depth
fi
}
function build_xinitrc() {
local mode="$1"
local xinitrc="/dev/shm/retropie_xinitrc"
case "$mode" in
clear)
rm -rf "$xinitrc"
;;
build)
echo "#!/bin/bash" >"$xinitrc"
# do modesetting (if supported)
if [[ -n "$HAS_MODESET" ]]; then
cat >>"$xinitrc" <<_EOF_
XRANDR_OUTPUT="\$($XRANDR --verbose | grep " connected" | awk '{ print \$1 }')"
$XRANDR --output \$XRANDR_OUTPUT --mode ${MODE_CUR[2]}x${MODE_CUR[3]} --refresh ${MODE_CUR[5]}
echo "Set mode ${MODE_CUR[2]}x${MODE_CUR[3]}@${MODE_CUR[5]}Hz on \$XRANDR_OUTPUT"
_EOF_
fi
# echo command line for runcommand log
cat >>"$xinitrc" <<_EOF_
echo -e "\nExecuting (via xinit): "${COMMAND//\$/\\\$}"\n"
${COMMAND//\$/\\\$}
_EOF_
chmod +x "$xinitrc"
# rewrite command to launch our xinit script (if not startx)
if ! [[ "$COMMAND" =~ ^startx ]]; then
COMMAND="xinit $xinitrc"
fi
# workaround for launching xserver on correct/user owned tty
# see https://github.com/RetroPie/RetroPie-Setup/issues/1805
# if no TTY env var is set, try and get it - eg if launching a ports script or runcommand manually
if [[ -z "$TTY" ]]; then
TTY=$(tty)
TTY=${TTY:8:1}
fi
# if we managed to get the current tty then try and use it
if [[ -n "$TTY" ]]; then
COMMAND="$COMMAND -- vt$TTY -keeptty"
fi
;;
esac
}
function mode_switch() {
local command_prefix
local separator="-"
# X11 uses hypens in connector names
[[ $HAS_MODESET == "x11" ]] && separator=":"
local mode_id=(${1/${separator}/ })
# if the requested mode is the same as the current mode, don't switch
[[ "${mode_id[*]}" == "${MODE_CUR[0]} ${MODE_CUR[1]}" ]] && return 1
if [[ "$HAS_MODESET" == "kms" ]]; then
# update the target resolution even though the underlying fb hasn't changed
MODE_CUR=($(get_${HAS_MODESET}_mode_info "${mode_id[*]}"))
# check the mode tuple against the list of current available CRCTID/MODEID values
if [[ -n ${MODE["${MODE_CUR[0]}-${MODE_CUR[1]}"]} ]]; then
# inject the environment variables to do modesetting for SDL2 applications
command_prefix="SDL_VIDEO_KMSDRM_CRTCID=${MODE_CUR[0]} SDL_VIDEO_KMSDRM_MODEID=${MODE_CUR[1]}"
fi
COMMAND="$(echo "$command_prefix $COMMAND" | sed -e "s/;/; $command_prefix /g")"
return 0
elif [[ "$HAS_MODESET" == "x11" ]]; then
# query the target resolution
MODE_CUR=($(get_${HAS_MODESET}_mode_info "${mode_id[*]}"))
# set target resolution
$XRANDR --output "${MODE_CUR[0]}" --mode "${MODE_CUR[1]}"
[[ "$?" -eq 0 ]] && return 0
elif [[ "$HAS_MODESET" == "tvs" ]]; then
if [[ "${mode_id[0]}" == "PAL" ]] || [[ "${mode_id[0]}" == "NTSC" ]]; then
$TVSERVICE -c "${mode_id[*]}" >/dev/null
else
$TVSERVICE -e "${mode_id[*]}" >/dev/null
fi
# if we have switched mode, switch the framebuffer resolution also
if [[ "$?" -eq 0 ]]; then
sleep 1
MODE_CUR=($(get_${HAS_MODESET}_mode_info))
[[ -z "$FB_NEW" ]] && FB_NEW="${MODE_CUR[2]}x${MODE_CUR[3]}"
return 0
fi
fi
return 1
}
function restore_fb() {
sleep 1
switch_fb_res "${FB_ORIG[0]}x${FB_ORIG[1]}" "${FB_ORIG[2]}"
}
function config_dispmanx() {
# if we are running under X then don't try and use dispmanx
[[ -n "$DISPLAY" || "$XINIT" -eq 1 ]] && return
local name="$1"
# if we have a dispmanx conf file and $name is in it (as a variable) and set to 1,
# change the library path to load dispmanx sdl first
if [[ -f "$DISPMANX_CONF" ]]; then
iniConfig " = " '"' "$DISPMANX_CONF"
iniGet "$name"
if [[ "$ini_value" == "1" ]]; then
if [[ "$HAS_MODESET" == "kms" ]]; then
COMMAND="SDL_DISPMANX_WIDTH=${MODE_CUR[2]} SDL_DISPMANX_HEIGHT=${MODE_CUR[3]} $COMMAND"
fi
COMMAND="SDL1_VIDEODRIVER=dispmanx $COMMAND"
fi
fi
}
function retroarch_append_config() {
local conf="/dev/shm/retroarch.cfg"
local dim
# only for retroarch emulators
[[ "$EMULATOR" != lr-* ]] && return
# make sure tmp folder exists for unpacking archives
mkdir -p "/tmp/retroarch"
rm -f "$conf"
touch "$conf"
iniConfig " = " '"' "$conf"
if [[ -n "$HAS_MODESET" && "${MODE_CUR[5]}" -gt 0 ]]; then
# set video_refresh_rate in our config to the same as the screen refresh
iniSet "video_refresh_rate" "${MODE_CUR[5]}"
fi
# populate with target resolution & fullscreen flag if KMS is active
if [[ "$HAS_MODESET" != "tvs" ]]; then
iniSet "video_fullscreen" "true"
iniSet "video_fullscreen_x" "${MODE_CUR[2]}"
iniSet "video_fullscreen_y" "${MODE_CUR[3]}"
# if our render resolution is "config", then we don't set anything (use the value in the retroarch.cfg)
elif [[ "$RENDER_RES" != "config" ]]; then
if [[ "$RENDER_RES" == "output" ]]; then
dim=(0 0)
else
dim=(${RENDER_RES/x/ })
fi
iniSet "video_fullscreen_x" "${dim[0]}"
iniSet "video_fullscreen_y" "${dim[1]}"
fi
# if the ROM has a custom configuration then append that too
if [[ -f "$ROM.cfg" ]]; then
conf+="'|'\"$ROM.cfg\""
fi
# if we already have an existing appendconfig parameter, we need to add our configs to that
if [[ "$COMMAND" =~ "--appendconfig" ]]; then
COMMAND=$(echo "$COMMAND" | sed "s#\(--appendconfig *[^ $]*\)#\1'|'$conf#")
else
COMMAND+=" --appendconfig $conf"
fi
# append any NETPLAY configuration
if [[ "$NETPLAY" -eq 1 ]] && [[ -f "$RETRONETPLAY_CONF" ]]; then
source "$RETRONETPLAY_CONF"
COMMAND+=" -$__netplaymode $__netplayhostip_cfile --port $__netplayport --nick $__netplaynickname"
fi
}
function set_governor() {
governor_old=()
# we save the previous states first, as setting any cpuX on the RPI will also set the value for the other cores
# which would cause us to save the wrong state for cpu1/2/3 after setting cpu0. On the RPI we could just process
# cpu0, but this code needs to work on other platforms that do support a "per core" CPU governor.
for cpu in /sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_governor; do
governor_old+=($(<$cpu))
done
for cpu in /sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_governor; do
echo "$1" | sudo tee "$cpu" >/dev/null
done
}
function restore_governor() {
local i=0
for cpu in /sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_governor; do
echo "${governor_old[$i]}" | sudo tee "$cpu" >/dev/null
((i++))
done
}
function get_sys_command() {
if [[ ! -f "$EMU_SYS_CONF" ]]; then
echo "No config found for system $SYSTEM"
stop_joy2key
exit 1
fi
# get system & rom specific emulator if set
local emulator="$(default_emulator get emu_sys)"
if [[ -z "$emulator" ]]; then
echo "No default emulator found for system $SYSTEM"
start_joy2key
choose_emulator "emu_sys" "" "--nocancel"
stop_joy2key
get_sys_command "$SYSTEM" "$ROM"
return
fi
EMULATOR="$emulator"
# get default emulator for system + rom combination
# try the old key first and convert to the new key if found
emulator="$(default_emulator get emu_rom_old)"
if [[ -n "$emulator" ]]; then
default_emulator del emu_rom_old
default_emulator set emu_rom "$emulator"
EMULATOR="$emulator"
else
emulator="$(default_emulator get emu_rom)"
[[ -n "$emulator" ]] && EMULATOR="$emulator"
fi
COMMAND="$(default_emulator get emu_cmd)"
# replace tokens
COMMAND="${COMMAND//\%ROM\%/\"$ROM\"}"
COMMAND="${COMMAND//\%BASENAME\%/\"$ROM_BN\"}"
# special case to get the last 2 folders for quake games for the -game parameter
# remove everything up to /quake/
local quake_dir="${ROM##*/quake/}"
# remove filename
quake_dir="${quake_dir%/*}"
COMMAND="${COMMAND//\%QUAKEDIR\%/\"$quake_dir\"}"
# if it starts with CON: it is a console application (so we don't redirect stdout later)
if [[ "$COMMAND" == CON:* ]]; then
# remove CON:
COMMAND="${COMMAND:4}"
CONSOLE_OUT=1
fi
# if it starts with XINIT: it is an X11 application (so we need to launch via xinit)
if [[ "$COMMAND" == XINIT:* ]]; then
# remove XINIT:
COMMAND="${COMMAND:6}"
XINIT=1
fi
}
function show_launch() {
local images=()
if [[ "$IS_SYS" -eq 1 && "$USE_ART" -eq 1 ]]; then
# if using art look for images in paths for es art.
images+=(
"$HOME/RetroPie/roms/$SYSTEM/images/${ROM_BN}-image"
"$HOME/.emulationstation/downloaded_images/$SYSTEM/${ROM_BN}-image"
"$HOME/.emulationstation/downloaded_media/$SYSTEM/screenshots/${ROM_BN}"
"$HOME/RetroPie/roms/$SYSTEM/media/screenshots/${ROM_BN}"
)
fi
# look for custom launching images
if [[ "$IS_SYS" -eq 1 ]]; then
images+=(
"$HOME/RetroPie/roms/$SYSTEM/images/${ROM_BN}-launching"
"$CONF_ROOT/launching"
)
fi
[[ "$IS_PORT" -eq 1 ]] && images+=("$CONFIGDIR/ports/launching")
images+=("$CONFIGDIR/all/launching")
local image
local path
local ext
for path in "${images[@]}"; do
for ext in jpg png; do
if [[ -f "$path.$ext" ]]; then
image="$path.$ext"
break 2
fi
done
done
if [[ -n "$image" ]]; then
# if we are running under X use feh otherwise try and use fbi
if [[ -n "$DISPLAY" ]]; then
feh -F -N -Z -Y -q "$image" & &>/dev/null
IMG_PID=$!
sleep "$IMAGE_DELAY"
else
fbi -1 -t "$IMAGE_DELAY" -noverbose -a "$image" </dev/tty &>/dev/null
fi
elif [[ "$DISABLE_MENU" -ne 1 && "$USE_ART" -ne 1 ]]; then
local launch_name
if [[ -n "$ROM_BN" ]]; then
launch_name="$ROM_BN ($EMULATOR)"
else
launch_name="$EMULATOR"
fi
DIALOGRC="$CONFIGDIR/all/runcommand-launch-dialog.cfg" dialog --infobox "\nLaunching $launch_name ...\n\nPress a button to configure\n\nErrors are logged to $LOG" 9 60
fi
}
function check_menu() {
local dont_launch=0
# check for key pressed to enter configuration
IFS= read -s -t 2 -N 1 key </dev/tty
if [[ -n "$key" ]]; then
[[ -n "$IMG_PID" ]] && kill -SIGINT "$IMG_PID"
tput cnorm
quick_menu
dont_launch=$?
tput civis
clear
fi
return $dont_launch
}
# calls script with parameters SYSTEM, EMULATOR, ROM, and commandline
function user_script() {
local script="$CONFIGDIR/all/$1"
if [[ -f "$script" ]]; then
bash "$script" "$SYSTEM" "$EMULATOR" "$ROM" "$COMMAND" </dev/tty 2>>"$LOG"
fi
}
function restore_cursor_and_exit() {
# if we are not being run from emulationstation (get parent of parent), turn the cursor back on.
if [[ "$(ps -o comm= -p $(ps -o ppid= -p $PPID))" != "emulationstatio" ]]; then
tput cnorm
fi
exit 0
}
function launch_command() {
local ret
# escape $ to avoid variable expansion (eg roms containing $!)
COMMAND="${COMMAND//\$/\\\$}"
# launch the command
echo -e "Parameters: $@\nExecuting: $COMMAND" >>"$LOG"
if [[ "$CONSOLE_OUT" -eq 1 ]]; then
# turn cursor on
tput cnorm
eval $COMMAND </dev/tty &>> "$LOG" &
ret=$!
tput civis
else
eval $COMMAND </dev/tty &>> "$LOG" &
ret=$!
fi
return $ret
}
function runcommand() {
get_config
if ! get_params "$@"; then
echo "$0 MODE COMMAND [SAVENAME]"
echo "$0 MODE _SYS_/_PORT_ SYSTEM ROM"
exit 1
fi
# turn off cursor and clear screen
tput civis
clear
rm -f "$LOG"
echo -e "$SYSTEM\n$EMULATOR\n$ROM\n$COMMAND" >/dev/shm/runcommand.info
user_script "runcommand-onstart.sh"
set_save_vars
load_mode_defaults
#start_joy2key
#show_launch
try_emulator
#if [[ "$DISABLE_MENU" -ne 1 ]]; then
# if ! check_menu; then
# stop_joy2key
# user_script "runcommand-onend.sh"
# clear
# restore_cursor_and_exit 0
# fi
#fi
#stop_joy2key
mode_switch "$MODE_REQ_ID"
# replace X/Y resolution and refresh (useful for KMS/modesetting)
COMMAND="${COMMAND//\%XRES\%/${MODE_CUR[2]}}"
COMMAND="${COMMAND//\%YRES\%/${MODE_CUR[3]}}"
COMMAND="${COMMAND//\%REFRESH\%/${MODE_CUR[5]}}"
[[ -n "$FB_NEW" ]] && switch_fb_res $FB_NEW
config_dispmanx "$SAVE_EMU"
# switch to configured cpu scaling governor
[[ -n "$GOVERNOR" ]] && set_governor "$GOVERNOR"
retroarch_append_config
# build xinitrc and rewrite command if not already in X11 context
if [[ "$XINIT" -eq 1 && "$HAS_MODESET" != "x11" ]]; then
build_xinitrc build
fi
launch_command
local PID=$?
sleep $EST_TIME
EMU_FOUND=0
for INC in $(seq 8)
do
sleep 0.5
[ ! -n $PID -a -e /proc/$PID ] && break
done
sleep 2
#if [ -n "$PID" -a -e /proc/$PID ] ; then
# emu_found $PID
# EMU_FOUND=1
#fi
local RA_PID=$( pgrep retroarch )
if [ -n "$RA_PID" -a -e /proc/$RA_PID ] ; then
emu_found $RA_PID
EMU_FOUND=2
fi
local AM_PID=$( pgrep advmame )
if [ -n "$AM_PID" -a -e /proc/$AM_PID ] ; then
emu_found $AM_PID
EMU_FOUND=3
fi
echo "EMU_FOUND=$EMU_FOUND" > "result.tmp"
echo "EMULATOR=$EMULATOR" >> "result.tmp"
sleep 2
[[ $EMU_FOUND > 0 ]] && save_emulator | sleep 2
[[ -n "$IMG_PID" ]] && kill -SIGINT "$IMG_PID"
# remove tmp folder for unpacked archives if it exists
rm -rf "/tmp/retroarch"
# restore default cpu scaling governor
[[ -n "$GOVERNOR" ]] && restore_governor
# if we switched mode - restore preferred mode
mode_switch "$MODE_ORIG_ID"
# delete temporary xinitrc launch script
if [[ "$XINIT" -eq 1 && "$HAS_MODESET" != "x11" ]]; then
build_xinitrc clear
fi
# reset/restore framebuffer res (if it was changed)
[[ -n "$FB_NEW" ]] && restore_fb
[[ "$EMULATOR" == lr-* ]] && retroarchIncludeToEnd "$CONF_ROOT/retroarch.cfg"
user_script "runcommand-onend.sh"
restore_cursor_and_exit "$ret"
}
function emu_found()
{
local pid=$1
sudo kill $pid
EMU_FOUND=1
}
runcommand "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment