#!/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