Created
March 24, 2026 00:09
-
-
Save coatezy/0d57ce7153199655f2d8074bb7d6a59d to your computer and use it in GitHub Desktop.
Setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -Eeuo pipefail | |
| # ============================================================ | |
| # Mac Couch Emulation Bootstrap v3.1 | |
| # - Latest upstream builds where practical | |
| # - ES-DE, Dolphin, PCSX2, RPCS3, Cemu | |
| # - Optional RetroArch config patching if already installed | |
| # - ES-DE defaults + custom systems for ps3/wiiu | |
| # ============================================================ | |
| EMULATION_ROOT="${HOME}/Emulation" | |
| ROMS_DIR="${EMULATION_ROOT}/roms" | |
| BIOS_DIR="${EMULATION_ROOT}/bios" | |
| SAVES_DIR="${EMULATION_ROOT}/saves" | |
| STATES_DIR="${EMULATION_ROOT}/states" | |
| SCREENSHOTS_DIR="${EMULATION_ROOT}/screenshots" | |
| TOOLS_DIR="${EMULATION_ROOT}/tools" | |
| DOWNLOADS_DIR="${EMULATION_ROOT}/downloads" | |
| TMP_DIR="${EMULATION_ROOT}/tmp" | |
| LOG_FILE="${EMULATION_ROOT}/setup.log" | |
| ESDE_CONFIG_DIR="${HOME}/Library/Application Support/ES-DE" | |
| ESDE_CUSTOM_DIR="${HOME}/.emulationstation/custom_systems" | |
| ESDE_CUSTOM_SYSTEMS_XML="${ESDE_CUSTOM_DIR}/es_systems.xml" | |
| ESDE_SETTINGS_FILE="${ESDE_CONFIG_DIR}/es_settings.xml" | |
| RETROARCH_CFG="${HOME}/Library/Application Support/RetroArch/retroarch.cfg" | |
| DOLPHIN_USER_DIR="${HOME}/Library/Application Support/Dolphin" | |
| PCSX2_INI_DIR="${HOME}/Library/Application Support/PCSX2/inis" | |
| RPCS3_CONFIG_DIR="${HOME}/Library/Application Support/rpcs3" | |
| ADD_ESDE_TO_LOGIN_ITEMS="${ADD_ESDE_TO_LOGIN_ITEMS:-false}" | |
| OPEN_APPS_ON_FINISH="${OPEN_APPS_ON_FINISH:-true}" | |
| PATCH_RETROARCH_IF_PRESENT="${PATCH_RETROARCH_IF_PRESENT:-true}" | |
| ESDE_THEME="${ESDE_THEME:-modern}" | |
| SYSTEM_DIRS=( | |
| "3do" "amiga" "arcade" "atari2600" "atari5200" "atari7800" "atarilynx" | |
| "dreamcast" "fbneo" "gameandwatch" "gamegear" "gamecube" "gb" "gba" "gbc" | |
| "genesis" "mastersystem" "megadrive" "n64" "nds" "nes" "ngp" "ngpc" | |
| "pcengine" "ports" "ps2" "ps3" "psp" "psx" "saturn" "scummvm" "sega32x" | |
| "segacd" "snes" "tg16" "virtualboy" "wii" "wiiu" | |
| ) | |
| timestamp() { date +"%Y-%m-%d %H:%M:%S"; } | |
| log() { | |
| mkdir -p "${EMULATION_ROOT}" | |
| echo "[$(timestamp)] $*" | tee -a "${LOG_FILE}" | |
| } | |
| fail() { | |
| log "ERROR: $*" | |
| exit 1 | |
| } | |
| command_exists() { | |
| command -v "$1" >/dev/null 2>&1 | |
| } | |
| require_macos() { | |
| [[ "$(uname -s)" == "Darwin" ]] || fail "This script is for macOS only." | |
| } | |
| detect_arch() { | |
| uname -m | |
| } | |
| ensure_xcode_cli_tools() { | |
| if xcode-select -p >/dev/null 2>&1; then | |
| log "Xcode Command Line Tools already installed." | |
| return | |
| fi | |
| log "Xcode Command Line Tools not found. Requesting install..." | |
| xcode-select --install || true | |
| log "Complete the Command Line Tools install, then re-run this script." | |
| exit 0 | |
| } | |
| ensure_homebrew() { | |
| if command_exists brew; then | |
| log "Homebrew already installed." | |
| return | |
| fi | |
| log "Installing Homebrew..." | |
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| if [[ -x "/opt/homebrew/bin/brew" ]]; then | |
| eval "$(/opt/homebrew/bin/brew shellenv)" | |
| elif [[ -x "/usr/local/bin/brew" ]]; then | |
| eval "$(/usr/local/bin/brew shellenv)" | |
| fi | |
| command_exists brew || fail "Homebrew installation failed." | |
| } | |
| ensure_brew_shellenv_loaded() { | |
| if command_exists brew; then | |
| return | |
| fi | |
| if [[ -x "/opt/homebrew/bin/brew" ]]; then | |
| eval "$(/opt/homebrew/bin/brew shellenv)" | |
| elif [[ -x "/usr/local/bin/brew" ]]; then | |
| eval "$(/usr/local/bin/brew shellenv)" | |
| fi | |
| } | |
| ensure_rosetta_if_needed() { | |
| if [[ "$(detect_arch)" != "arm64" ]]; then | |
| log "Intel Mac detected; Rosetta not required." | |
| return | |
| fi | |
| if /usr/bin/pgrep oahd >/dev/null 2>&1; then | |
| log "Rosetta 2 already appears to be installed." | |
| return | |
| fi | |
| log "Installing Rosetta 2..." | |
| softwareupdate --install-rosetta --agree-to-license || log "Rosetta may already be installed." | |
| } | |
| brew_install_formula_if_missing() { | |
| local formula="$1" | |
| if brew list "${formula}" >/dev/null 2>&1; then | |
| log "Formula already installed: ${formula}" | |
| else | |
| log "Installing formula: ${formula}" | |
| brew install "${formula}" | |
| fi | |
| } | |
| install_prereqs() { | |
| log "Updating Homebrew..." | |
| brew update | |
| brew_install_formula_if_missing "jq" | |
| brew_install_formula_if_missing "unar" | |
| brew_install_formula_if_missing "wget" | |
| } | |
| create_directories() { | |
| log "Creating directory structure..." | |
| mkdir -p \ | |
| "${ROMS_DIR}" "${BIOS_DIR}" "${SAVES_DIR}" "${STATES_DIR}" \ | |
| "${SCREENSHOTS_DIR}" "${TOOLS_DIR}" "${DOWNLOADS_DIR}" "${TMP_DIR}" \ | |
| "${ESDE_CONFIG_DIR}" "${ESDE_CUSTOM_DIR}" | |
| for dir in "${SYSTEM_DIRS[@]}"; do | |
| mkdir -p "${ROMS_DIR}/${dir}" | |
| done | |
| mkdir -p "${ROMS_DIR}/ps3/firmware" | |
| mkdir -p "${ROMS_DIR}/wiiu/install" | |
| mkdir -p "${ROMS_DIR}/wiiu/mlc01" | |
| mkdir -p "${ROMS_DIR}/wiiu/keys" | |
| } | |
| download_to() { | |
| local url="$1" | |
| local output="$2" | |
| log "Downloading ${url}" | |
| curl -L --fail --output "${output}" "${url}" | |
| } | |
| github_latest_asset_url() { | |
| local repo="$1" | |
| local regex="$2" | |
| curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" \ | |
| | jq -r --arg regex "${regex}" ' | |
| .assets[] | |
| | select(.name | test($regex)) | |
| | .browser_download_url | |
| ' \ | |
| | head -n 1 | |
| } | |
| install_app_from_dmg() { | |
| local dmg_path="$1" | |
| local app_name="$2" | |
| local mount_point | |
| mount_point="$(hdiutil attach -nobrowse -plist "${dmg_path}" \ | |
| | plutil -extract system-entities json -o - - \ | |
| | jq -r '.[]."mount-point" // empty' \ | |
| | head -n 1)" | |
| [[ -n "${mount_point}" ]] || fail "Could not mount DMG: ${dmg_path}" | |
| local source_app | |
| source_app="$(find "${mount_point}" -maxdepth 2 -type d -name "${app_name}.app" | head -n 1)" | |
| [[ -n "${source_app}" ]] || fail "Could not find ${app_name}.app in mounted DMG" | |
| log "Installing ${app_name}.app to /Applications" | |
| rm -rf "/Applications/${app_name}.app" | |
| cp -R "${source_app}" "/Applications/" | |
| hdiutil detach "${mount_point}" >/dev/null | |
| } | |
| install_app_from_zip() { | |
| local zip_path="$1" | |
| local app_name="$2" | |
| local extract_dir="${TMP_DIR}/$(basename "${zip_path}" .zip)" | |
| rm -rf "${extract_dir}" | |
| mkdir -p "${extract_dir}" | |
| ditto -x -k "${zip_path}" "${extract_dir}" | |
| local source_app | |
| source_app="$(find "${extract_dir}" -maxdepth 4 -type d -name "${app_name}.app" | head -n 1)" | |
| [[ -n "${source_app}" ]] || fail "Could not find ${app_name}.app after unzip" | |
| log "Installing ${app_name}.app to /Applications" | |
| rm -rf "/Applications/${app_name}.app" | |
| cp -R "${source_app}" "/Applications/" | |
| } | |
| install_app_from_7z() { | |
| local archive_path="$1" | |
| local app_name="$2" | |
| local extract_dir="${TMP_DIR}/$(basename "${archive_path}" .7z)" | |
| rm -rf "${extract_dir}" | |
| mkdir -p "${extract_dir}" | |
| unar -quiet -output-directory "${extract_dir}" "${archive_path}" | |
| local source_app | |
| source_app="$(find "${extract_dir}" -maxdepth 4 -type d -name "${app_name}.app" | head -n 1)" | |
| [[ -n "${source_app}" ]] || fail "Could not find ${app_name}.app after extraction" | |
| log "Installing ${app_name}.app to /Applications" | |
| rm -rf "/Applications/${app_name}.app" | |
| cp -R "${source_app}" "/Applications/" | |
| } | |
| remove_quarantine_if_present() { | |
| local app_path="$1" | |
| if [[ -d "${app_path}" ]]; then | |
| xattr -dr com.apple.quarantine "${app_path}" >/dev/null 2>&1 || true | |
| fi | |
| } | |
| install_esde_latest() { | |
| local arch package_name url dmg | |
| arch="$(detect_arch)" | |
| package_name="macOSApple" | |
| [[ "${arch}" == "x86_64" ]] && package_name="macOSIntel" | |
| log "Resolving latest ES-DE package (${package_name})..." | |
| url="$(curl -fsSL "https://raw.githubusercontent.com/RetroDECK/ES-DE/retrodeck-main/latest_release.json" \ | |
| | jq -r --arg name "${package_name}" ' | |
| .release.packages[] | |
| | select(.name == $name) | |
| | .url | |
| ')" | |
| [[ -n "${url}" && "${url}" != "null" ]] || fail "Could not resolve latest ES-DE download URL" | |
| dmg="${DOWNLOADS_DIR}/ES-DE-latest.dmg" | |
| download_to "${url}" "${dmg}" | |
| install_app_from_dmg "${dmg}" "ES-DE" | |
| remove_quarantine_if_present "/Applications/ES-DE.app" | |
| } | |
| install_dolphin_latest() { | |
| local page url dmg | |
| log "Resolving latest Dolphin dev build..." | |
| page="$(curl -fsSL "https://dolphin-emu.org/download/")" | |
| url="$(echo "${page}" \ | |
| | grep -Eo 'https://dl\.dolphin-emu\.org/builds/[^"]+\.dmg' \ | |
| | head -n 1)" | |
| [[ -n "${url}" ]] || fail "Could not resolve latest Dolphin DMG URL" | |
| dmg="${DOWNLOADS_DIR}/Dolphin-latest.dmg" | |
| download_to "${url}" "${dmg}" | |
| install_app_from_dmg "${dmg}" "Dolphin" | |
| remove_quarantine_if_present "/Applications/Dolphin.app" | |
| } | |
| install_pcsx2_latest() { | |
| local url dmg | |
| log "Resolving latest PCSX2 macOS release..." | |
| url="$(github_latest_asset_url "PCSX2/pcsx2" 'macos.*\.dmg$')" | |
| [[ -n "${url}" && "${url}" != "null" ]] || fail "Could not resolve latest PCSX2 macOS asset" | |
| dmg="${DOWNLOADS_DIR}/PCSX2-latest.dmg" | |
| download_to "${url}" "${dmg}" | |
| install_app_from_dmg "${dmg}" "PCSX2" | |
| remove_quarantine_if_present "/Applications/PCSX2.app" | |
| } | |
| install_rpcs3_latest() { | |
| local arch repo url archive | |
| arch="$(detect_arch)" | |
| if [[ "${arch}" == "arm64" ]]; then | |
| repo="RPCS3/rpcs3-binaries-mac-arm64" | |
| else | |
| repo="RPCS3/rpcs3-binaries-mac" | |
| fi | |
| log "Resolving latest RPCS3 build from ${repo}..." | |
| url="$(github_latest_asset_url "${repo}" '\.7z$')" | |
| [[ -n "${url}" && "${url}" != "null" ]] || fail "Could not resolve latest RPCS3 asset" | |
| archive="${DOWNLOADS_DIR}/RPCS3-latest.7z" | |
| download_to "${url}" "${archive}" | |
| install_app_from_7z "${archive}" "RPCS3" | |
| remove_quarantine_if_present "/Applications/RPCS3.app" | |
| } | |
| install_cemu_latest() { | |
| local url dmg | |
| log "Resolving latest Cemu macOS release..." | |
| url="$(github_latest_asset_url "cemu-project/Cemu" 'macos.*\.dmg$')" | |
| [[ -n "${url}" && "${url}" != "null" ]] || fail "Could not resolve latest Cemu macOS asset" | |
| dmg="${DOWNLOADS_DIR}/Cemu-latest.dmg" | |
| download_to "${url}" "${dmg}" | |
| install_app_from_dmg "${dmg}" "Cemu" | |
| remove_quarantine_if_present "/Applications/Cemu.app" | |
| } | |
| xml_escape() { | |
| python3 - <<'PY' "$1" | |
| import sys | |
| from xml.sax.saxutils import escape | |
| print(escape(sys.argv[1])) | |
| PY | |
| } | |
| write_esde_custom_systems() { | |
| cat > "${ESDE_CUSTOM_SYSTEMS_XML}" <<EOF | |
| <?xml version="1.0"?> | |
| <systemList> | |
| <system> | |
| <name>ps3</name> | |
| <fullname>PlayStation 3</fullname> | |
| <path>${ROMS_DIR}/ps3</path> | |
| <extension>.ps3 .PS3 .iso .ISO .pkg .PKG</extension> | |
| <command>${TOOLS_DIR}/launch-rpcs3.sh "%ROM_RAW%"</command> | |
| <platform>ps3</platform> | |
| <theme>ps3</theme> | |
| </system> | |
| <system> | |
| <name>wiiu</name> | |
| <fullname>Wii U</fullname> | |
| <path>${ROMS_DIR}/wiiu</path> | |
| <extension>.wud .WUD .wux .WUX .rpx .RPX .wua .WUA .elf .ELF</extension> | |
| <command>${TOOLS_DIR}/launch-cemu.sh "%ROM_RAW%"</command> | |
| <platform>wiiu</platform> | |
| <theme>wiiu</theme> | |
| </system> | |
| </systemList> | |
| EOF | |
| log "Wrote ES-DE custom systems." | |
| } | |
| write_helper_scripts() { | |
| cat > "${TOOLS_DIR}/launch-rpcs3.sh" <<'EOF' | |
| #!/usr/bin/env bash | |
| set -Eeuo pipefail | |
| ROM="${1:-}" | |
| if [[ -z "${ROM}" ]]; then | |
| open -a "RPCS3" | |
| else | |
| open -a "RPCS3" --args "${ROM}" | |
| fi | |
| EOF | |
| cat > "${TOOLS_DIR}/launch-cemu.sh" <<'EOF' | |
| #!/usr/bin/env bash | |
| set -Eeuo pipefail | |
| ROM="${1:-}" | |
| if [[ -z "${ROM}" ]]; then | |
| open -a "Cemu" | |
| else | |
| open -a "Cemu" --args -g "${ROM}" | |
| fi | |
| EOF | |
| cat > "${TOOLS_DIR}/open-esde.sh" <<'EOF' | |
| #!/usr/bin/env bash | |
| open -a "ES-DE" | |
| EOF | |
| chmod +x \ | |
| "${TOOLS_DIR}/launch-rpcs3.sh" \ | |
| "${TOOLS_DIR}/launch-cemu.sh" \ | |
| "${TOOLS_DIR}/open-esde.sh" | |
| log "Helper scripts written." | |
| } | |
| write_esde_settings() { | |
| mkdir -p "${ESDE_CONFIG_DIR}" | |
| local roms_escaped | |
| roms_escaped="$(xml_escape "${ROMS_DIR}")" | |
| cat > "${ESDE_SETTINGS_FILE}" <<EOF | |
| <?xml version="1.0"?> | |
| <config> | |
| <string name="ROMDirectory" value="${roms_escaped}" /> | |
| <bool name="Fullscreen" value="true" /> | |
| <bool name="EnableSplashScreen" value="false" /> | |
| <bool name="ShowHiddenGames" value="false" /> | |
| <bool name="ShowMediaTypes" value="true" /> | |
| <bool name="ScreenSaverControls" value="true" /> | |
| <bool name="QuickSystemSelect" value="true" /> | |
| <string name="ThemeSet" value="${ESDE_THEME}" /> | |
| </config> | |
| EOF | |
| log "Wrote ES-DE settings defaults." | |
| } | |
| upsert_cfg_key() { | |
| local file="$1" | |
| local key="$2" | |
| local value="$3" | |
| mkdir -p "$(dirname "${file}")" | |
| touch "${file}" | |
| if grep -qE "^${key} = " "${file}"; then | |
| perl -0pi -e "s#^${key} = \".*?\"#${key} = \"${value}\"#mg" "${file}" | |
| else | |
| printf '\n%s = "%s"\n' "${key}" "${value}" >> "${file}" | |
| fi | |
| } | |
| patch_retroarch_if_present() { | |
| if [[ "${PATCH_RETROARCH_IF_PRESENT}" != "true" ]]; then | |
| log "Skipping RetroArch patching." | |
| return | |
| fi | |
| if [[ ! -f "${RETROARCH_CFG}" && ! -d "/Applications/RetroArch.app" ]]; then | |
| log "RetroArch not found; skipping RetroArch config patching." | |
| return | |
| fi | |
| log "Patching RetroArch defaults..." | |
| upsert_cfg_key "${RETROARCH_CFG}" "savefile_directory" "${SAVES_DIR}" | |
| upsert_cfg_key "${RETROARCH_CFG}" "savestate_directory" "${STATES_DIR}" | |
| upsert_cfg_key "${RETROARCH_CFG}" "screenshot_directory" "${SCREENSHOTS_DIR}" | |
| upsert_cfg_key "${RETROARCH_CFG}" "system_directory" "${BIOS_DIR}" | |
| upsert_cfg_key "${RETROARCH_CFG}" "rgui_browser_directory" "${ROMS_DIR}" | |
| upsert_cfg_key "${RETROARCH_CFG}" "video_fullscreen" "true" | |
| upsert_cfg_key "${RETROARCH_CFG}" "input_enable_hotkey" "nul" | |
| upsert_cfg_key "${RETROARCH_CFG}" "input_exit_emulator" "escape" | |
| log "RetroArch config patched." | |
| } | |
| patch_dolphin_defaults() { | |
| mkdir -p "${DOLPHIN_USER_DIR}/Config" | |
| local ini="${DOLPHIN_USER_DIR}/Config/Dolphin.ini" | |
| touch "${ini}" | |
| grep -q "^\[Interface\]" "${ini}" || printf '\n[Interface]\n' >> "${ini}" | |
| grep -q "^\[Display\]" "${ini}" || printf '\n[Display]\n' >> "${ini}" | |
| if ! grep -q "^Fullscreen = " "${ini}"; then | |
| printf 'Fullscreen = True\n' >> "${ini}" | |
| fi | |
| log "Dolphin defaults patched." | |
| } | |
| patch_pcsx2_defaults() { | |
| mkdir -p "${PCSX2_INI_DIR}" | |
| local ini="${PCSX2_INI_DIR}/PCSX2.ini" | |
| touch "${ini}" | |
| grep -q "^\[UI\]" "${ini}" || printf '\n[UI]\n' >> "${ini}" | |
| grep -q "^StartFullscreen = " "${ini}" || printf 'StartFullscreen = true\n' >> "${ini}" | |
| log "PCSX2 defaults patched." | |
| } | |
| write_hotkey_notes() { | |
| cat > "${TOOLS_DIR}/HOTKEYS.txt" <<EOF | |
| Controller-friendly notes | |
| ========================= | |
| ES-DE | |
| ----- | |
| - Fullscreen is enabled by default in the generated settings. | |
| RetroArch | |
| --------- | |
| - This script patches fullscreen and directory defaults if RetroArch is already installed. | |
| - ES-DE's macOS guide notes RetroArch should start in fullscreen so focus handoff works correctly. | |
| Dolphin | |
| ------- | |
| - Fullscreen default is patched. | |
| - Set your controller inside Dolphin once: | |
| Controllers -> Port 1 -> configure gamepad | |
| - For Wii titles, configure Real Wii Remote or emulated Wii Remote as needed. | |
| PCSX2 | |
| ----- | |
| - StartFullscreen is enabled in PCSX2.ini if that file exists/gets created. | |
| - Configure your controller once in PCSX2 settings. | |
| RPCS3 | |
| ----- | |
| - Configure your pad once inside RPCS3: | |
| Pads -> Handler -> SDL | |
| - SDL is usually the least painful option for Xbox / DualSense on macOS. | |
| Cemu | |
| ---- | |
| - On Apple Silicon, current public macOS Cemu builds are still x86-64 and rely on Rosetta 2. | |
| - Configure input once inside Cemu after first launch. | |
| EOF | |
| log "Wrote hotkey/controller notes." | |
| } | |
| write_readme() { | |
| cat > "${EMULATION_ROOT}/README.txt" <<EOF | |
| Mac Emulation Setup | |
| =================== | |
| Installed apps | |
| -------------- | |
| /Applications/ES-DE.app | |
| /Applications/Dolphin.app | |
| /Applications/PCSX2.app | |
| /Applications/RPCS3.app | |
| /Applications/Cemu.app | |
| Folders | |
| ------- | |
| roms/ Put game files here | |
| bios/ BIOS / firmware where needed | |
| saves/ Save files | |
| states/ Save states | |
| screenshots/ Screenshots | |
| tools/ Launcher/helper scripts | |
| Important ROM paths | |
| ------------------- | |
| ${ROMS_DIR}/gamecube | |
| ${ROMS_DIR}/wii | |
| ${ROMS_DIR}/ps2 | |
| ${ROMS_DIR}/ps3 | |
| ${ROMS_DIR}/wiiu | |
| Notes | |
| ----- | |
| 1. Point ES-DE at: | |
| ${ROMS_DIR} | |
| 2. Add your own firmware / BIOS: | |
| - PS2 BIOS | |
| - PS3 firmware | |
| - Wii U keys / files | |
| - any required RetroArch system BIOS | |
| 3. Pair your controller in macOS and map it in each emulator once. | |
| 4. Read: | |
| ${TOOLS_DIR}/HOTKEYS.txt | |
| EOF | |
| log "README written." | |
| } | |
| open_apps_once() { | |
| if [[ "${OPEN_APPS_ON_FINISH}" != "true" ]]; then | |
| log "Skipping first-launch app initialisation." | |
| return | |
| fi | |
| local apps=("Dolphin" "PCSX2" "RPCS3" "Cemu" "ES-DE") | |
| for app in "${apps[@]}"; do | |
| log "Opening ${app} once..." | |
| open -a "${app}" || log "Could not open ${app}; continuing." | |
| sleep 4 | |
| osascript -e "tell application \"${app}\" to quit" >/dev/null 2>&1 || true | |
| sleep 1 | |
| done | |
| } | |
| add_esde_to_login_items() { | |
| if [[ "${ADD_ESDE_TO_LOGIN_ITEMS}" != "true" ]]; then | |
| log "Skipping Login Items change." | |
| return | |
| fi | |
| log "Adding ES-DE to Login Items..." | |
| osascript <<'EOF' | |
| tell application "System Events" | |
| if not (exists login item "ES-DE") then | |
| make login item at end with properties {path:"/Applications/ES-DE.app", hidden:false} | |
| end if | |
| end tell | |
| EOF | |
| } | |
| print_next_steps() { | |
| cat <<EOF | |
| ============================================ | |
| Setup complete | |
| ============================================ | |
| Installed: | |
| - ES-DE | |
| - Dolphin | |
| - PCSX2 | |
| - RPCS3 | |
| - Cemu | |
| Created: | |
| - ${EMULATION_ROOT} | |
| - ${ROMS_DIR} | |
| - ${BIOS_DIR} | |
| - ${TOOLS_DIR} | |
| - ${ESDE_CUSTOM_SYSTEMS_XML} | |
| - ${ESDE_SETTINGS_FILE} | |
| Next steps: | |
| 1. Put ROMs into: | |
| ${ROMS_DIR}/<system> | |
| 2. Add required firmware / BIOS yourself: | |
| - PS2 BIOS | |
| - PS3 firmware | |
| - Wii U keys / install data | |
| - any RetroArch system BIOS files | |
| 3. Open each emulator once and configure your controller: | |
| - Dolphin | |
| - PCSX2 | |
| - RPCS3 | |
| - Cemu | |
| 4. Open ES-DE and verify: | |
| - controller navigation works | |
| - fullscreen is on | |
| - your theme looks right | |
| - scraping is configured | |
| Optional: | |
| - Re-run with: | |
| ADD_ESDE_TO_LOGIN_ITEMS=true ./$(basename "$0") | |
| to add ES-DE to Login Items | |
| Log file: | |
| - ${LOG_FILE} | |
| EOF | |
| } | |
| main() { | |
| require_macos | |
| create_directories | |
| log "Starting Mac emulation bootstrap v3.1..." | |
| log "Architecture: $(detect_arch)" | |
| ensure_xcode_cli_tools | |
| ensure_homebrew | |
| ensure_brew_shellenv_loaded | |
| ensure_rosetta_if_needed | |
| install_prereqs | |
| install_esde_latest | |
| install_dolphin_latest | |
| install_pcsx2_latest | |
| install_rpcs3_latest | |
| install_cemu_latest | |
| write_esde_custom_systems | |
| write_helper_scripts | |
| write_esde_settings | |
| patch_retroarch_if_present | |
| patch_dolphin_defaults | |
| patch_pcsx2_defaults | |
| write_hotkey_notes | |
| write_readme | |
| open_apps_once | |
| add_esde_to_login_items | |
| print_next_steps | |
| log "Bootstrap completed successfully." | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment