#!/bin/bash
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# Copyright 2019-2020 Alessandro "Locutus73" Miele
# You can download the latest version of this script from:
# https://github.com/MiSTer-devel/Scripts_MiSTer
# Version 0.9.8 - 2020-03-03 - Update core manager to latest Wiki core structure; code clean up by frederic-mahe (thank you very much).
# Version 0.9.7 - 2020-01-07 - Support for MRA files (MAME Arcade ROMs).
# Version 0.9.6 - 2019-12-04 - Update core manager to latest Wiki core structure by rarcos, thank you very much.
# Version 0.9.5 - 2019-07-26 - The script is compatible with a possible renaming of "Cores" to "Computer Cores" in MiSTer Wiki Sidebar.
# Version 0.9.4 - 2019-06-10 - Testing Internet connectivity with github.com instead of google.com.
# Version 0.9.3 - 2019-05-31 - Added ALLOW_INSECURE_SSL="true" in order to not enforce the use of security_fixes.sh
# Version 0.9.2 - 2019-05-31 - Added DIALOG_HEIGHT parameter.
# Version 0.9.1 - 2019-05-30 - Creating directories when needed.
# Version 0.9 - 2019-05-30 - First commit, lacking any MiSTer Updater configuration capability.
# ========= OPTIONS ==================
# ========= ADVANCED OPTIONS =========
UPDATER_URL="https://github.com/MiSTer-devel/Updater_script_MiSTer/blob/master/mister_updater.sh"
ALLOW_INSECURE_SSL="true"
DIALOG_HEIGHT="31"
# ========= CODE STARTS HERE =========
function checkTERMINAL {
# if [ "$(uname -n)" != "MiSTer" ]
# then
# echo "This script must be run"
# echo "on a MiSTer system."
# exit 1
# fi
if [[ ! (-t 0 && -t 1 && -t 2) ]]
then
echo "This script must be run"
echo "from an interactive terminal."
echo "Please press F9 (F12 to exit)"
echo "or use SSH."
exit 2
fi
}
function setupScriptINI {
# get the name of the script, or of the parent script if called through a 'curl ... | bash -'
ORIGINAL_SCRIPT_PATH="${0}"
[[ "${ORIGINAL_SCRIPT_PATH}" == "bash" ]] && \
ORIGINAL_SCRIPT_PATH="$(ps -o comm,pid | awk -v PPID=${PPID} '$2 == PPID {print $1}')"
setupCURL
TMP_INCLUDE_FILE=$(mktemp)
echo "Downloading ${UPDATER_URL/*\//}"
MISTER_UPDATER_CODE=$(${CURL} "${UPDATER_URL}?raw=true" | dos2unix)
[ "${MISTER_UPDATER_CODE}" == "" ] && echo "Error downloading ${UPDATER_URL/*\//}" && exit 1
echo "${MISTER_UPDATER_CODE}" | \
sed -n '/#========= USER OPTIONS =========/,/#========= CODE STARTS HERE =========/p' | \
sed 's/#========= CODE STARTS HERE =========//' | \
sed 's/declare -A/declare -g -A/g' | \
sed 's/DOWNLOAD_NEW_CORES="true"/DOWNLOAD_NEW_CORES="false"/g' \
> "${TMP_INCLUDE_FILE}"
source ${TMP_INCLUDE_FILE}
rm -f ${TMP_INCLUDE_FILE}
# ini file can contain user defined variables (as bash commands)
# Load and execute the content of the ini file, if there is one
INI_PATH="$(dirname ${ORIGINAL_SCRIPT_PATH})/update.ini"
if [ ! -f "${INI_PATH}" ]
then
echo "${MISTER_UPDATER_CODE}" | \
sed -n '/#========= USER OPTIONS =========/,/#========= CODE STARTS HERE =========/p' | \
sed 's/#========= CODE STARTS HERE =========//' | \
sed 's/DOWNLOAD_NEW_CORES="true"/DOWNLOAD_NEW_CORES="false"/g' \
> "${INI_PATH}"
fi
echo "${MISTER_UPDATER_CODE}" | \
sed -n '/^function checkCoreURL {/,/^}/p' \
> "${TMP_INCLUDE_FILE}"
source ${TMP_INCLUDE_FILE}
rm -f ${TMP_INCLUDE_FILE}
echo "${MISTER_UPDATER_CODE}" | \
sed -n '/^function checkAdditionalRepository {/,/^}/p' \
> "${TMP_INCLUDE_FILE}"
source ${TMP_INCLUDE_FILE}
rm -f ${TMP_INCLUDE_FILE}
unset MISTER_UPDATER_CODE
if [[ -f "${INI_PATH}" ]] ; then
# TMP_INCLUDE_FILE=$(mktemp)
# preventively eliminate DOS-specific format and exit command
dos2unix < "${INI_PATH}" 2> /dev/null | \
grep -v "^exit" | \
sed 's/declare -A/declare -g -A/g' \
> ${TMP_INCLUDE_FILE}
source ${TMP_INCLUDE_FILE}
rm -f ${TMP_INCLUDE_FILE}
fi
}
function setupCURL
{
[ ! -z "${CURL}" ] && return
CURL_RETRY="--connect-timeout 15 --max-time 120 --retry 3 --retry-delay 5"
# test network and https by pinging the most available website
SSL_SECURITY_OPTION=""
curl ${CURL_RETRY} --silent https://github.com > /dev/null 2>&1
case $? in
0)
;;
60)
if [[ "${ALLOW_INSECURE_SSL}" == "true" ]]
then
SSL_SECURITY_OPTION="--insecure"
else
echo "CA certificates need"
echo "to be fixed for"
echo "using SSL certificate"
echo "verification."
echo "Please fix them i.e."
echo "using security_fixes.sh"
exit 2
fi
;;
*)
echo "No Internet connection"
exit 1
;;
esac
CURL="curl ${CURL_RETRY} ${SSL_SECURITY_OPTION} --location"
CURL_SILENT="${CURL} --silent --fail"
}
function installDEBS () {
DEB_REPOSITORIES=( "${@}" )
TEMP_PATH="/tmp"
for DEB_REPOSITORY in "${DEB_REPOSITORIES[@]}"; do
OLD_IFS="${IFS}"
IFS="|"
PARAMS=(${DEB_REPOSITORY})
DEBS_URL="${PARAMS[0]}"
DEB_PREFIX="${PARAMS[1]}"
ARCHIVE_FILES="${PARAMS[2]}"
STRIP_COMPONENTS="${PARAMS[3]}"
DEST_DIR="${PARAMS[4]}"
IFS="${OLD_IFS}"
if [ ! -f "${DEST_DIR}/$(echo $ARCHIVE_FILES | sed 's/*//g')" ]
then
DEB_NAMES=$(${CURL_SILENT} "${DEBS_URL}" | grep -oE "\"${DEB_PREFIX}[a-zA-Z0-9%./_+-]*_(armhf|all)\.deb\"" | sed 's/\"//g')
MAX_VERSION=""
MAX_DEB_NAME=""
for DEB_NAME in $DEB_NAMES; do
CURRENT_VERSION=$(echo "${DEB_NAME}" | grep -o '_[a-zA-Z0-9%.+-]*_' | sed 's/_//g')
if [[ "${CURRENT_VERSION}" > "${MAX_VERSION}" ]]
then
MAX_VERSION="${CURRENT_VERSION}"
MAX_DEB_NAME="${DEB_NAME}"
fi
done
[ "${MAX_DEB_NAME}" == "" ] && echo "Error searching for ${DEB_PREFIX} in ${DEBS_URL}" && exit 1
echo "Downloading ${MAX_DEB_NAME}"
${CURL} "${DEBS_URL}/${MAX_DEB_NAME}" -o "${TEMP_PATH}/${MAX_DEB_NAME}"
[ ! -f "${TEMP_PATH}/${MAX_DEB_NAME}" ] && echo "Error: no ${TEMP_PATH}/${MAX_DEB_NAME} found." && exit 1
echo "Extracting ${ARCHIVE_FILES}"
ORIGINAL_DIR="$(pwd)"
cd "${TEMP_PATH}"
rm data.tar.xz > /dev/null 2>&1
ar -x "${TEMP_PATH}/${MAX_DEB_NAME}" data.tar.xz
cd "${ORIGINAL_DIR}"
rm "${TEMP_PATH}/${MAX_DEB_NAME}"
mkdir -p "${DEST_DIR}"
[ ! -f "${TEMP_PATH}/data.tar.xz" ] && echo "Error: no ${TEMP_PATH}/data.tar.xz found." && exit 1
tar -xJf "${TEMP_PATH}/data.tar.xz" --wildcards --no-anchored --strip-components="${STRIP_COMPONENTS}" -C "${DEST_DIR}" "${ARCHIVE_FILES}"
rm "${TEMP_PATH}/data.tar.xz" > /dev/null 2>&1
fi
done
}
function setupDIALOG {
if which dialog > /dev/null 2>&1
then
DIALOG="dialog"
else
if [ ! -f /media/fat/linux/dialog/dialog ]
then
setupCURL
installDEBS "http://http.us.debian.org/debian/pool/main/d/dialog|dialog_1.3-2016|dialog|3|/media/fat/linux/dialog" "http://http.us.debian.org/debian/pool/main/n/ncurses|libncursesw5_6.0|libncursesw.so.5*|3|/media/fat/linux/dialog" "http://http.us.debian.org/debian/pool/main/n/ncurses|libtinfo5_6.0|libtinfo.so.5*|3|/media/fat/linux/dialog"
fi
DIALOG="/media/fat/linux/dialog/dialog"
export LD_LIBRARY_PATH="/media/fat/linux/dialog"
fi
rm -f "/media/fat/config/dialogrc"
if [ ! -f "~/.dialogrc" ]
then
export DIALOGRC="$(dirname ${ORIGINAL_SCRIPT_PATH})/.dialogrc"
if [ ! -f "${DIALOGRC}" ]
then
${DIALOG} --create-rc "${DIALOGRC}"
sed -i "s/use_colors = OFF/use_colors = ON/g" "${DIALOGRC}"
sed -i "s/screen_color = (CYAN,BLUE,ON)/screen_color = (CYAN,BLACK,ON)/g" "${DIALOGRC}"
sync
fi
fi
export NCURSES_NO_UTF8_ACS=1
: ${DIALOG_OK=0}
: ${DIALOG_CANCEL=1}
: ${DIALOG_HELP=2}
: ${DIALOG_EXTRA=3}
: ${DIALOG_ITEM_HELP=4}
: ${DIALOG_ESC=255}
: ${SIG_NONE=0}
: ${SIG_HUP=1}
: ${SIG_INT=2}
: ${SIG_QUIT=3}
: ${SIG_KILL=9}
: ${SIG_TERM=15}
}
function setupDIALOGtempfile {
DIALOG_TEMPFILE=`(DIALOG_TEMPFILE) 2>/dev/null` || DIALOG_TEMPFILE=/tmp/dialog_tempfile$$
trap "rm -f $DIALOG_TEMPFILE" 0 $SIG_NONE $SIG_HUP $SIG_INT $SIG_QUIT $SIG_TERM
}
function readDIALOGtempfile {
DIALOG_RETVAL=$?
DIALOG_OUTPUT="$(cat ${DIALOG_TEMPFILE})"
#rm -f ${DIALOG_TEMPFILE}
#unset DIALOG_TEMPFILE
}
function setupUPDATER {
LOCAL_UPDATER="$(dirname $(readlink -f ${ORIGINAL_SCRIPT_PATH}))/update.sh"
LOCAL_UPDATER_INI="${LOCAL_UPDATER%.*}.ini"
if [ ! -f "${LOCAL_UPDATER}" ]
then
echo "Downloading update.sh"
${CURL} "$(echo ${UPDATER_URL}?raw=true | sed 's/mister.updater.sh/update.sh/')" -o "${LOCAL_UPDATER}"
[ ! -f "${LOCAL_UPDATER}" ] && echo "Error downloading update.sh" && exit 1
fi
}
function setupCoreURLs {
declare -g -A CORE_CATEGORY_NAMES
declare -g -A CORE_CATEGORY_REVERSE_NAMES
declare -g -A COMPUTER_CORE_URLS
declare -g -A CONSOLE_CORE_URLS
declare -g -A ARCADE_CORE_URLS
declare -g -A OTHER_CORE_URLS
declare -g -A UTILITY_CORE_URLS
CORE_CATEGORY_NAMES["computer-cores"]="Computer"
CORE_CATEGORY_NAMES["console-cores"]="Console"
CORE_CATEGORY_NAMES["arcade-cores"]="Arcade"
CORE_CATEGORY_NAMES["other-cores"]="Other"
CORE_CATEGORY_NAMES["service-cores"]="Utility"
for CORE_CATEGORY in ${!CORE_CATEGORY_NAMES[@]}; do
CORE_CATEGORY_REVERSE_NAMES["${CORE_CATEGORY_NAMES[${CORE_CATEGORY}]^^}"]="${CORE_CATEGORY}"
done
echo "Downloading MiSTer Wiki"
CORE_URLS=$(${CURL} "$MISTER_URL/wiki" | awk '/user-content-fpga-cores/,/user-content-development/' | grep -io '\(https://github.com/[a-zA-Z0-9./_-]*_MiSTer">[^<]*\)\|\(user-content-[a-zA-Z0-9-]*\)')
ARCADE_URLS=$(${CURL} "$MISTER_URL/wiki/Arcade-Cores-List" | awk '/wiki-content/,/wiki-rightbar/' | grep -io '\(https://github.com/[a-zA-Z0-9./_-]*_MiSTer">[^<]*\)')
OLD_IFS="$IFS"
IFS=$'\n'
for CORE_URL in $CORE_URLS; do
if [[ $CORE_URL == https://* ]]
then
CORE_NAME="${CORE_URL#*\">}" && CORE_NAME="${CORE_NAME//\'/\\\'}"
CORE_URL="${CORE_URL%\">*}"
[ "${CORE_URL##*\/}" == "Menu_MiSTer" ] && continue
eval ${CORE_CATEGORY_NAMES[$CORE_CATEGORY]^^}_CORE_URLS[${CORE_NAME}]=${CORE_URL}
else
CORE_CATEGORY=$(echo "$CORE_URL" | sed 's/user-content-//g')
#if [ "$CORE_CATEGORY" == "computer-cores" ] || [[ "$CORE_CATEGORY" =~ [a-z]+-comput[a-z]+ ]]
#then
# CORE_CATEGORY="cores"
#fi
#if [[ "$CORE_CATEGORY" =~ console.* ]]
#then
# CORE_CATEGORY="console-cores"
#fi
case "${CORE_URL}" in
*comput*) CORE_CATEGORY="computer-cores" ;;
*console*) CORE_CATEGORY="console-cores" ;;
*other-systems*) CORE_CATEGORY="other-cores" ;;
"") CORE_CATEGORY="-" ;;
*) ;;
esac
fi
done
CORE_CATEGORY="arcade-cores"
for CORE_URL in $ARCADE_URLS; do
CORE_NAME="${CORE_URL#*\">}" && CORE_NAME="${CORE_NAME//\'/\\\'}"
CORE_URL="${CORE_URL%\">*}"
eval ${CORE_CATEGORY_NAMES[$CORE_CATEGORY]^^}_CORE_URLS[${CORE_NAME}]=${CORE_URL}
done
IFS="$OLD_IFS"
}
DIALOG_TITLE="MiSTer Core Manager"
function showPleaseWAIT {
${DIALOG} --title "${DIALOG_TITLE}" \
--infobox "Please wait..." 0 0
}
function showMainMENU {
setupDIALOGtempfile
${DIALOG} --clear --no-tags --item-help --ok-label "Select" \
--title "${DIALOG_TITLE}" \
--menu "Please choose an option.\nUse arrow keys, tab, space, enter and esc." ${DIALOG_HEIGHT} 0 999 \
"installCOMPUTER" "Install/Update Computer core" "" \
"deleteCOMPUTER" "Delete Computer core" "" \
"installCONSOLE" "Install/Update Console core" "" \
"deleteCONSOLE" "Delete Console core" "" \
"installARCADE" "Install/Update Arcade core" "" \
"deleteARCADE" "Delete Arcade core" "" \
"installOTHER" "Install/Update Other core" "" \
"deleteOTHER" "Delete Other core" "" \
"installUTILITY" "Install/Update Utility core" "" \
"deleteUTILITY" "Delete Utility core" "" \
"configureUPDATER" "Configure MiSTer Updater" "Configures ${LOCAL_UPDATER_INI} used both by the updater and by this script" \
"updateMISTER" "Update MiSTer" "Launches ${LOCAL_UPDATER}" \
"watchMOVIE" "Watch Star Wars" "Enjoy the show... Press and then in order to exit the movie" \
2> ${DIALOG_TEMPFILE}
readDIALOGtempfile
}
function showInstallMENU {
CORE_CATEGORY_NAME="${1}"
MENU_ITEMS=""
ADDITIONAL_OPTIONS="--no-tags"
OLD_IFS="$IFS"
IFS=$'\n'
for CORE_NAME in $(eval echo \"\${!${CORE_CATEGORY_NAME}_CORE_URLS[*]}\" | sort); do
CORE_URL="$(eval echo \${${CORE_CATEGORY_NAME}_CORE_URLS[${CORE_NAME//\'/\\\'}]})"
MENU_ITEMS="${MENU_ITEMS} \"${CORE_URL}\" \"${CORE_NAME}\""
done
IFS="$OLD_IFS"
setupDIALOGtempfile
eval ${DIALOG} --clear --colors --ok-label \"Install/Update\" \
--extra-button --extra-label \"README\" \
--title \"${DIALOG_TITLE}: Install/Update ${CORE_CATEGORY_NAME,,} core\" \
${ADDITIONAL_OPTIONS} \
--menu \"Please select the ${CORE_CATEGORY_NAME,,} core you wish to install or update.\" ${DIALOG_HEIGHT} 0 999 \
${MENU_ITEMS} \
2> ${DIALOG_TEMPFILE}
readDIALOGtempfile
}
function showDeleteMENU {
CORE_CATEGORY_NAME="${1}"
MENU_ITEMS=""
ADDITIONAL_OPTIONS="--no-tags"
for CORE_FILE in "${CORE_CATEGORY_PATHS[${CORE_CATEGORY}]}"/*.rbf; do
MENU_ITEMS="${MENU_ITEMS} \"${CORE_FILE}\" \"${CORE_FILE}\""
done
setupDIALOGtempfile
eval ${DIALOG} --clear --colors --ok-label \"Delete\" \
--title \"${DIALOG_TITLE}: Delete ${CORE_CATEGORY_NAME,,} core\" \
${ADDITIONAL_OPTIONS} \
--menu \"Please select the ${CORE_CATEGORY_NAME,,} core you wish to delete.\" ${DIALOG_HEIGHT} 0 999 \
${MENU_ITEMS} \
2> ${DIALOG_TEMPFILE}
readDIALOGtempfile
}
clear
checkTERMINAL
setupScriptINI
setupUPDATER
setupDIALOG
setupCoreURLs
while true; do
showMainMENU
case ${DIALOG_RETVAL} in
${DIALOG_OK})
case "${DIALOG_OUTPUT}" in
install*)
CORE_CATEGORY_NAME="${DIALOG_OUTPUT/install/}"
CORE_CATEGORY="${CORE_CATEGORY_REVERSE_NAMES[${CORE_CATEGORY_NAME}]}"
echo "CORE_CATEGORY_NAME=${CORE_CATEGORY_NAME}"
echo "CORE_CATEGORY=${CORE_CATEGORY}"
sleep 5
while true; do
showInstallMENU "${CORE_CATEGORY_NAME}"
case ${DIALOG_RETVAL} in
${DIALOG_OK})
mkdir -p "${CORE_CATEGORY_PATHS[${CORE_CATEGORY}]}"
CORE_URL="${DIALOG_OUTPUT}"
ORIGINAL_DOWNLOAD_NEW_CORES="${DOWNLOAD_NEW_CORES}"
DOWNLOAD_NEW_CORES="true"
ORIGINAL_SSH_CLIENT="${SSH_CLIENT}"
SSH_CLIENT="---"
clear
checkCoreURL
DOWNLOAD_NEW_CORES="${ORIGINAL_DOWNLOAD_NEW_CORES}"
SSH_CLIENT="${ORIGINAL_SSH_CLIENT}"
;;
${DIALOG_EXTRA})
showPleaseWAIT
README_URL="https://github.com$($CURL_SILENT $DIALOG_OUTPUT | grep -oi '/MiSTer-devel/[a-zA-Z0-9./_-]*/blob/[a-zA-Z0-9./_-]*/readme[^"]*')?raw=true"
TMP_README=/tmp/dialog_README_tempfile$$
$CURL_SILENT "${README_URL}" | dos2unix > "${TMP_README}"
if [ -f "${TMP_README}" ]
then
${DIALOG} --clear --title "${DIALOG_TITLE}" \
--textbox "${TMP_README}" 0 0
rm -f "${TMP_README}"
else
${DIALOG} --clear --title "${DIALOG_TITLE}" \
--msgbox "Something went wrong downloading\n${README_URL}" 0 0
fi
;;
*)
break
;;
esac
done
;;
delete*)
CORE_CATEGORY_NAME="${DIALOG_OUTPUT/delete/}"
CORE_CATEGORY="${CORE_CATEGORY_REVERSE_NAMES[${CORE_CATEGORY_NAME}]}"
while true; do
showDeleteMENU "${CORE_CATEGORY_NAME}"
case ${DIALOG_RETVAL} in
${DIALOG_OK})
${DIALOG} --clear --title "${DIALOG_TITLE}" --defaultno --yesno "Are you sure you want to delete\n${DIALOG_OUTPUT}\n???" 0 0 && rm -f "${DIALOG_OUTPUT}" && sync
;;
*)
break
;;
esac
done
;;
configureUPDATER)
${DIALOG} --title "${DIALOG_TITLE}" \
--msgbox "Coming soon...\n\n...in the meantime you can watch Star Wars" 0 0
;;
updateMISTER)
clear
${LOCAL_UPDATER}
;;
watchMOVIE)
clear
telnet towel.blinkenlights.nl
;;
esac
;;
*)
break;;
esac
done
clear
exit 0