#!/usr/bin/env bash ############################################################################### # GEEKCADE INSTALLER # Cabinet Setup Script # Version: v2.0 (build 38) # # Assumptions: # - Ubuntu is already installed # - User "geekcade" already exists # - This script is run as user "geekcade" # - This is for the real arcade cabinet, directly connected to a monitor # - No VNC, no Openbox, no Xvfb desktop # # Installs/configures: # - MAME # - Attract-Mode Plus KMS release for direct cabinet display # - Visible folder layout under /home/geekcade # - Launcher scripts: mame.sh and attractplus.sh # - Attract-Mode config using Attrac-Man # - MAME romlist generation # # Flags: # --compile Build Attract-Mode Plus from latest GitHub source DEFAULT # --release Install official Ubuntu KMS .deb release (matched to OS) # --skip-attract Skip Attract-Mode Plus install/update # --debug Show shell trace # --brief Hide command output, still log to file ############################################################################### # ============================== # [0] SAFETY CHECKS # ============================== if [ "$(whoami)" != "geekcade" ]; then echo "ERROR: This installer must be run as user 'geekcade'." echo "Current user: $(whoami)" exit 1 fi # ============================== # [1] GLOBAL CONFIG # ============================== BASE_DIR="$HOME" MAME_DIR="$BASE_DIR/mame" ATTRACT_DIR="$BASE_DIR/attractplus" LOG_DIR="$BASE_DIR/logs" WALLPAPER_DIR="$BASE_DIR/wallpaper" INSTALL_LOG="$LOG_DIR/geekcade_install_$(date +%Y%m%d_%H%M%S).log" MAME_LAUNCHER="$BASE_DIR/mame.sh" ATTRACT_LAUNCHER="$BASE_DIR/attractplus.sh" # Base URL for all GeekCade-hosted files. Override via env to test a # branch or alternate server: # GEEKCADE_BASE_URL=http://other.host/path ./geekcade_install.sh GEEKCADE_BASE_URL="${GEEKCADE_BASE_URL:-https://install.geekcade.com/deploy}" DONKEY_KONG_ROM_URL="${GEEKCADE_BASE_URL}/dkong.zip" WALLPAPER_URL="${GEEKCADE_BASE_URL}/geekcade_wallpaper.jpg" MENU_SCRIPT_URL="${GEEKCADE_BASE_URL}/geekcade_menu.sh" ASSETS_SCRIPT_URL="${GEEKCADE_BASE_URL}/geekcade_assets.sh" # Attract-Mode Plus latest release is fetched live from GitHub at install # time (no version pinning) so we always get the newest build. The asset # is selected based on the running Ubuntu version. If no matching .deb # exists for this Ubuntu release, we automatically fall back to compile. ATTRACT_GITHUB_API="https://api.github.com/repos/oomek/attractplus/releases/latest" SUCCESS_FLAG_FILE="$BASE_DIR/.geekcade_install_success" GREEN=$'\033[0;32m' RED=$'\033[0;31m' YELLOW=$'\033[1;33m' NC=$'\033[0m' LIGHTGREY=$'\033[90m' # ============================== # [2] ARGUMENTS # ============================== VERBOSITY_LEVEL=1 ATTRACT_MODE_ACTION="compile" for arg in "$@"; do case "$arg" in --debug) VERBOSITY_LEVEL=2 ;; --brief) VERBOSITY_LEVEL=0 ;; --compile) ATTRACT_MODE_ACTION="compile" ;; --release) ATTRACT_MODE_ACTION="release" ;; --skip-attract) ATTRACT_MODE_ACTION="skip" ;; *) echo "Unknown option: $arg" echo "Valid options: --compile --release --skip-attract --debug --brief" exit 1 ;; esac done if [ "$VERBOSITY_LEVEL" -ge 2 ]; then set -x fi # ============================== # [3] PREPARE LOGGING # ============================== mkdir -p "$LOG_DIR" touch "$INSTALL_LOG" # ============================== # [4] BANNER # ============================== display_banner() { clear echo -e "${GREEN}" cat << "EOF" ____ _ ____ _ / ___| ___ ___| | __/ ___|__ _ __| | ___ | | _ / _ \/ _ \ |/ / | / _` |/ _` |/ _ \ | |_| | __/ __/ <| |__| (_| | (_| | __/ \____|\___|\___|_|\_\\____\__,_|\__,_|\___| EOF echo -e " Cabinet Installer v2.0 (build 38)${NC}" echo } display_banner # ============================== # [5] SUDO CHECK # ============================== echo "Requesting sudo access..." if sudo -v; then echo "Sudo access granted." else echo "Failed to get sudo access. Exiting." exit 1 fi echo "" # ============================== # [6] STEP WRAPPER # ============================== run_step() { local label="$1" shift if [ "$VERBOSITY_LEVEL" -ge 1 ]; then echo -e "${YELLOW}======> ${label}${NC}" fi echo -e "\n========== ${label} ==========" >> "$INSTALL_LOG" if [ "$VERBOSITY_LEVEL" -ge 1 ]; then "$@" 2>&1 | tee -a "$INSTALL_LOG" | sed -e "s/^/ ${LIGHTGREY}/" -e "s/$/${NC}/" status=${PIPESTATUS[0]} else "$@" >> "$INSTALL_LOG" 2>&1 status=$? fi if [ "$status" -eq 0 ]; then if [ "$VERBOSITY_LEVEL" -ge 1 ]; then echo -e "${YELLOW}======> ${GREEN}DONE${NC}" echo "" fi else echo -e "${YELLOW}======> ${RED}FAILED${NC}" echo -e "${RED}Error in step: '${label}'. Check log file:${NC} $INSTALL_LOG" exit 1 fi } # ====================================================================== # [7] SYSTEM PACKAGES # ====================================================================== run_step "Updating system package list" bash -c ' sudo apt-get update -y sudo apt-get upgrade -y ' run_step "Installing core packages" sudo apt-get install -y \ git build-essential wget curl unzip nano rsync \ mame feh pkgconf coreutils ca-certificates \ alsa-utils udisks2 # ====================================================================== # [7a] AUDIO STACK (PipeWire + PulseAudio shim for AttractMode Plus) # ====================================================================== # AttractMode Plus uses miniaudio which on Linux speaks the PulseAudio # protocol. PipeWire-Pulse provides that protocol while keeping the # lighter PipeWire stack underneath. pulseaudio-utils gives us pactl. run_step "Installing PipeWire audio stack" sudo apt-get install -y \ pipewire pipewire-pulse pipewire-audio wireplumber \ pulseaudio-utils run_step "Enabling PipeWire user services" bash -c " systemctl --user enable --now pipewire pipewire-pulse wireplumber " # ====================================================================== # [7a2] USER-LEVEL LINGERING (for USB auto-mount and other user services) # ====================================================================== # Without lingering, user services only run while the user is logged in. # Enabling lingering lets udisks2 (and PipeWire, etc.) keep working in # the background, including auto-mounting USB drives plugged in while # nobody is logged in on the cabinet keyboard. run_step "Enabling user lingering for geekcade" \ sudo loginctl enable-linger geekcade # ====================================================================== # [7a3] GEEKCADE USB AUTO-MOUNT # ====================================================================== # udisks2 only auto-mounts when there's an active graphical/polkit session. # A headless cabinet doesn't have one, so plug-in events get ignored. # Solution: a systemd .mount + .automount unit pair that watches for any # partition labeled GEEKCADE and mounts it at /media/GEEKCADE on demand. # Works headless, no session needed. run_step "Configuring GEEKCADE USB auto-mount" bash -c " # The mount unit - defines what gets mounted where sudo tee /etc/systemd/system/media-GEEKCADE.mount >/dev/null <<'EOF' [Unit] Description=GeekCade USB Drive (GEEKCADE label) # Don't fail boot if the USB isn't present ConditionPathExists=/dev/disk/by-label/GEEKCADE [Mount] What=/dev/disk/by-label/GEEKCADE Where=/media/GEEKCADE Type=auto # uid/gid so geekcade can read+write without sudo; nofail so missing USB # doesn't block boot; x-systemd.idle-timeout for clean unmount when idle. Options=uid=1000,gid=1000,nofail,noatime,x-systemd.idle-timeout=60 [Install] WantedBy=multi-user.target EOF # The automount unit - lazy-mounts when the path is accessed, and # also reacts to device hotplug. sudo tee /etc/systemd/system/media-GEEKCADE.automount >/dev/null <<'EOF' [Unit] Description=Automount for GeekCade USB Drive [Automount] Where=/media/GEEKCADE TimeoutIdleSec=60 [Install] WantedBy=multi-user.target EOF sudo mkdir -p /media/GEEKCADE sudo systemctl daemon-reload sudo systemctl enable media-GEEKCADE.automount sudo systemctl start media-GEEKCADE.automount || true " # ====================================================================== # [7b] USER GROUPS (required for KMSDRM + evdev access) # ====================================================================== run_step "Adding geekcade to video/input/render groups" \ sudo usermod -aG video,input,render geekcade # ====================================================================== # [7e] AUTO-LOGIN ON tty1 (cabinet boots straight to geekcade shell) # ====================================================================== # After AM+ exits, the .bash_profile autostart restarts getty@tty1 to # clear stuck KMSDRM input grabs from MAME's SDL2 backend. That requires # passwordless sudo for that one specific systemctl command. run_step "Granting geekcade passwordless getty@tty1 restart" bash -c " sudo tee /etc/sudoers.d/geekcade-getty >/dev/null <<'EOF' geekcade ALL=(root) NOPASSWD: /bin/systemctl restart getty@tty1, /usr/bin/systemctl restart getty@tty1 EOF sudo chmod 440 /etc/sudoers.d/geekcade-getty sudo visudo -c -f /etc/sudoers.d/geekcade-getty " run_step "Configuring auto-login on tty1" bash -c " sudo mkdir -p /etc/systemd/system/getty@tty1.service.d sudo tee /etc/systemd/system/getty@tty1.service.d/autologin.conf >/dev/null <<'EOF' [Service] ExecStart= ExecStart=-/sbin/agetty --autologin geekcade --noclear %I \$TERM EOF sudo systemctl daemon-reload " # ====================================================================== # [7f] AUTO-START AM+ ON LOGIN (only on tty1) # ====================================================================== # When geekcade auto-logs in on tty1, the .bash_profile auto-starts # the AttractMode Plus launcher. From SSH (no tty1, different VT), # you get a normal shell. To skip auto-launch on tty1 for debugging, # touch ~/.attract_no_autostart before reboot. run_step "Configuring AM+ auto-start on tty1 login" bash -c " PROFILE=\"\$HOME/.bash_profile\" touch \"\$PROFILE\" # Strip any prior block so this step is idempotent sed -i '/# >>> GeekCade autostart >>>/,/# <<< GeekCade autostart <<> \"\$PROFILE\" <<'EOF' # >>> GeekCade autostart >>> # When logging in on tty1 (the cabinet monitor), launch the GeekCade menu. # The menu has a boot gate that auto-launches AM+ unless a key is pressed, # and after AM+ exits, the menu restarts getty@tty1 to clear stuck KMSDRM # keyboard grabs (that part is handled inside geekcade_menu.sh). # SSH sessions land on different ttys and get a normal shell here. if [ \"\$(tty)\" = \"/dev/tty1\" ] && [ -x \"\$HOME/geekcade_menu.sh\" ]; then \"\$HOME/geekcade_menu.sh\" fi # <<< GeekCade autostart <<< EOF # Also make sure ~/.bashrc is sourced from .bash_profile (Ubuntu default # behavior when no .bash_profile exists - we just added one so we need # to be explicit). if ! grep -q 'bashrc' \"\$PROFILE\"; then cat >> \"\$PROFILE\" <<'EOF' # Source ~/.bashrc for interactive shells (added by GeekCade installer) if [ -n \"\$BASH_VERSION\" ] && [ -f \"\$HOME/.bashrc\" ]; then . \"\$HOME/.bashrc\" fi EOF fi " # ====================================================================== # [7c] DISABLE TTY BLANKING (so attract mode doesn't black out) # ====================================================================== run_step "Disabling TTY screen blanking" bash -c " sudo tee /etc/systemd/system/disable-tty-blank.service >/dev/null <<'EOF' [Unit] Description=Disable console blanking on tty1 After=getty@tty1.service [Service] Type=oneshot ExecStart=/bin/sh -c 'setterm -blank 0 -powerdown 0 -powersave off >/dev/tty1 2>&1 || true' StandardInput=tty StandardOutput=tty TTYPath=/dev/tty1 RemainAfterExit=yes [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable disable-tty-blank.service " # ====================================================================== # [8] CREATE GEEKCADE FOLDER STRUCTURE # ====================================================================== run_step "Creating GeekCade folder structure" bash -c " mkdir -p '$MAME_DIR' mkdir -p '$MAME_DIR/roms' mkdir -p '$MAME_DIR/artwork' mkdir -p '$MAME_DIR/cfg' mkdir -p '$MAME_DIR/comments' mkdir -p '$MAME_DIR/diff' mkdir -p '$MAME_DIR/inp' mkdir -p '$MAME_DIR/memcard' mkdir -p '$MAME_DIR/nvram' mkdir -p '$MAME_DIR/samples' mkdir -p '$MAME_DIR/snap' mkdir -p '$MAME_DIR/sta' mkdir -p '$MAME_DIR/hi' mkdir -p '$ATTRACT_DIR' mkdir -p '$ATTRACT_DIR/emulators' mkdir -p '$ATTRACT_DIR/romlists' mkdir -p '$ATTRACT_DIR/layouts' mkdir -p '$ATTRACT_DIR/flyers' mkdir -p '$ATTRACT_DIR/marquees' mkdir -p '$ATTRACT_DIR/snaps' mkdir -p '$ATTRACT_DIR/wheels' mkdir -p '$LOG_DIR' mkdir -p '$WALLPAPER_DIR' " # ====================================================================== # [9] LAUNCHER SCRIPTS # ====================================================================== run_step "Creating MAME launcher" bash -c "cat > '$MAME_LAUNCHER' <<'EOF' #!/usr/bin/env bash # ========================================================== # GeekCade MAME Launcher # ---------------------------------------------------------- # Runs MAME using /home/geekcade/mame as its visible base. # Configs, saves, artwork, nvram, snaps, and ROM paths are # kept under /home/geekcade/mame. # # Forces SDL to use KMSDRM (no X server) since this is a # direct-to-monitor cabinet. # ========================================================== export SDL_VIDEODRIVER=kmsdrm export SDL_AUDIODRIVER=alsa export SDL_VIDEO_KMSDRM_CRTC=0 exec /usr/games/mame \\ -homepath /home/geekcade/mame \\ -inipath /home/geekcade/mame \\ \"\$@\" EOF chmod +x '$MAME_LAUNCHER' " run_step "Creating Attract-Mode Plus launcher" bash -c "cat > '$ATTRACT_LAUNCHER' <<'EOF' #!/usr/bin/env bash # GeekCade Attract-Mode Plus launcher. # Runs AM+ with our config dir. When AM+ exits, this script exits. if command -v attractplus >/dev/null 2>&1; then AM_BIN=attractplus elif command -v attract >/dev/null 2>&1; then AM_BIN=attract else echo \"ERROR: attractplus/attract not found in PATH.\" exit 1 fi exec \"\$AM_BIN\" --config /home/geekcade/attractplus \"\$@\" EOF chmod +x '$ATTRACT_LAUNCHER' " # ====================================================================== # [9b] SHELL ALIASES (force every invocation through the .sh launchers) # ====================================================================== # Without these, typing 'mame dkong' or 'attractplus' uses system defaults # and writes config to ~/.mame and ~/.attractplus, creating a parallel # config world that diverges from the visible /home/geekcade tree. # Aliasing the bare commands to the .sh launchers keeps everything # pointing at /home/geekcade/mame and /home/geekcade/attractplus. run_step "Installing shell aliases for mame/attractplus" bash -c " ALIASES_FILE=\"\$HOME/.bash_aliases\" touch \"\$ALIASES_FILE\" # Strip any prior GeekCade alias block so this step is idempotent sed -i '/# >>> GeekCade aliases >>>/,/# <<< GeekCade aliases <<> \"\$ALIASES_FILE\" <<'EOF' # >>> GeekCade aliases >>> # Force bare 'mame' and 'attractplus' commands through the .sh launchers # so config always lands in /home/geekcade/mame and /home/geekcade/attractplus # instead of ~/.mame and ~/.attractplus. alias mame='/home/geekcade/mame.sh' alias attractplus='/home/geekcade/attractplus.sh' alias attract='/home/geekcade/attractplus.sh' # <<< GeekCade aliases <<< EOF # Make sure ~/.bashrc actually sources ~/.bash_aliases (Ubuntu default does, # but we verify in case .bashrc was edited). if ! grep -q 'bash_aliases' \"\$HOME/.bashrc\"; then cat >> \"\$HOME/.bashrc\" <<'EOF' # Source ~/.bash_aliases if present (added by GeekCade installer) if [ -f ~/.bash_aliases ]; then . ~/.bash_aliases fi EOF fi " # ====================================================================== # [9c] FETCH MENU SCRIPT # ====================================================================== # Pull the latest geekcade_menu.sh from the GeekCade server so the cabinet # always has the matching menu without manual copy. The menu is what # .bash_profile auto-runs on tty1 login. run_step "Fetching GeekCade menu script" bash -c " TMP=\"\$(mktemp)\" if wget -q -O \"\$TMP\" '$MENU_SCRIPT_URL'; then if [ -s \"\$TMP\" ]; then mv \"\$TMP\" '$BASE_DIR/geekcade_menu.sh' chmod +x '$BASE_DIR/geekcade_menu.sh' echo 'Menu script installed at $BASE_DIR/geekcade_menu.sh' else rm -f \"\$TMP\" echo 'WARNING: menu script downloaded but is empty - skipping.' exit 1 fi else rm -f \"\$TMP\" echo 'WARNING: failed to fetch menu script from $MENU_SCRIPT_URL' echo 'You can place geekcade_menu.sh at $BASE_DIR manually.' exit 1 fi " # ====================================================================== # [9d] FETCH ASSETS SYNC SCRIPT # ====================================================================== # geekcade_assets.sh handles syncing extras/multimedia/roms/support from # a GEEKCADE-labeled USB drive. Pulled here so it's available immediately # after install for users who plug in their assets USB. run_step "Fetching GeekCade assets sync script" bash -c " TMP=\"\$(mktemp)\" if wget -q -O \"\$TMP\" '$ASSETS_SCRIPT_URL'; then if [ -s \"\$TMP\" ]; then mv \"\$TMP\" '$BASE_DIR/geekcade_assets.sh' chmod +x '$BASE_DIR/geekcade_assets.sh' echo 'Assets script installed at $BASE_DIR/geekcade_assets.sh' else rm -f \"\$TMP\" echo 'WARNING: assets script downloaded but is empty - skipping.' exit 1 fi else rm -f \"\$TMP\" echo 'WARNING: failed to fetch assets script from $ASSETS_SCRIPT_URL' echo 'You can place geekcade_assets.sh at $BASE_DIR manually.' exit 1 fi " # ====================================================================== # [10] MAME CONFIGURATION # ====================================================================== run_step "Configuring MAME" bash -c " if [ -f '$MAME_DIR/mame.ini' ]; then echo 'Existing mame.ini found - preserving.' else echo 'Generating MAME config in $MAME_DIR...' cd '$MAME_DIR' '$MAME_LAUNCHER' -cc if [ ! -f '$MAME_DIR/mame.ini' ]; then echo 'ERROR: MAME did not create mame.ini.' exit 1 fi echo 'Applying GeekCade MAME defaults...' # Use absolute paths to remove ambiguity around cwd-relative # resolution. With relative paths, MAME resolves them against the # current working directory, not homepath, which causes ROM-not-found # errors when launching from anywhere other than ~/mame. sed -i 's#^rompath[[:space:]].*#rompath /home/geekcade/mame/roms#' '$MAME_DIR/mame.ini' sed -i 's#^samplepath[[:space:]].*#samplepath /home/geekcade/mame/samples#' '$MAME_DIR/mame.ini' sed -i 's#^artpath[[:space:]].*#artpath /home/geekcade/mame/artwork#' '$MAME_DIR/mame.ini' sed -i 's#^cfg_directory[[:space:]].*#cfg_directory /home/geekcade/mame/cfg#' '$MAME_DIR/mame.ini' sed -i 's#^nvram_directory[[:space:]].*#nvram_directory /home/geekcade/mame/nvram#' '$MAME_DIR/mame.ini' sed -i 's#^snapshot_directory[[:space:]].*#snapshot_directory /home/geekcade/mame/snap#' '$MAME_DIR/mame.ini' sed -i 's#^state_directory[[:space:]].*#state_directory /home/geekcade/mame/sta#' '$MAME_DIR/mame.ini' sed -i 's#^input_directory[[:space:]].*#input_directory /home/geekcade/mame/inp#' '$MAME_DIR/mame.ini' sed -i 's#^diff_directory[[:space:]].*#diff_directory /home/geekcade/mame/diff#' '$MAME_DIR/mame.ini' sed -i 's#^comment_directory[[:space:]].*#comment_directory /home/geekcade/mame/comments#' '$MAME_DIR/mame.ini' sed -i 's#^sound[[:space:]].*#sound sdl#' '$MAME_DIR/mame.ini' fi " # ====================================================================== # [11] ROMS + WALLPAPER # ====================================================================== run_step "Downloading Donkey Kong ROM" bash -c " cd '$MAME_DIR/roms' if [ -f dkong.zip ]; then echo 'dkong.zip already exists - preserving.' else wget -O dkong.zip '$DONKEY_KONG_ROM_URL' fi " run_step "Downloading GeekCade wallpaper" bash -c " cd '$WALLPAPER_DIR' if [ -f geekcade_wallpaper.jpg ]; then echo 'Wallpaper already exists - preserving.' else wget -O geekcade_wallpaper.jpg '$WALLPAPER_URL' fi " # ====================================================================== # [12] ATTRACT-MODE PLUS INSTALL (always-latest from GitHub) # ====================================================================== # Approach: # 1. Query GitHub API for the latest release tag. # 2. Try .deb assets matching this Ubuntu version, then 24.04, then 22.04. # 3. If none match, fall back to compiling from source (master branch). if [ "$ATTRACT_MODE_ACTION" = "release" ]; then . /etc/os-release || true ARCH_CHECK="$(dpkg --print-architecture || echo amd64)" VER_CHECK="${VERSION_ID:-}" if [ "$ARCH_CHECK" != "amd64" ]; then echo -e "${YELLOW}======> Non-amd64 architecture detected (${ARCH_CHECK}). Falling back to --compile.${NC}" ATTRACT_MODE_ACTION="compile" else run_step "Discovering latest Attract-Mode Plus release" bash -c " set -euo pipefail LATEST_JSON=\"\$(curl -fsSL '$ATTRACT_GITHUB_API')\" LATEST_TAG=\"\$(echo \"\$LATEST_JSON\" | python3 -c 'import json,sys; print(json.load(sys.stdin)[\"tag_name\"])')\" echo \"Latest Attract-Mode Plus version: \$LATEST_TAG\" # Prefer assets matching: this Ubuntu version, then 24.04, then 22.04 CANDIDATES=\"$VER_CHECK 24.04 22.04\" ASSET_URL=\"\" for V in \$CANDIDATES; do ASSET_NAME=\"attractplus_\${LATEST_TAG}_ubuntu-\${V}_KMS_amd64.deb\" URL=\"\$(echo \"\$LATEST_JSON\" | python3 -c \" import json,sys d=json.load(sys.stdin) for a in d['assets']: if a['name']=='\$ASSET_NAME': print(a['browser_download_url']); break \")\" if [ -n \"\$URL\" ]; then echo \"Selected asset: \$ASSET_NAME\" echo \"\$URL\" > /tmp/.attract_url ASSET_URL=\"\$URL\" break fi done if [ -z \"\$ASSET_URL\" ]; then echo 'No matching .deb asset found - will fall back to compile.' rm -f /tmp/.attract_url exit 0 fi " if [ ! -s /tmp/.attract_url ]; then echo -e "${YELLOW}======> No KMS .deb available for Ubuntu ${VER_CHECK}. Falling back to --compile.${NC}" ATTRACT_MODE_ACTION="compile" fi fi fi if [ "$ATTRACT_MODE_ACTION" = "release" ]; then run_step "Installing latest Attract-Mode Plus KMS release" bash -c " set -euo pipefail ASSET_URL=\"\$(cat /tmp/.attract_url)\" TMPD=\"\$(mktemp -d)\" cd \"\$TMPD\" wget -q -O attractplus.deb \"\$ASSET_URL\" sudo dpkg -i attractplus.deb || sudo apt-get -y -f install command -v attractplus >/dev/null 2>&1 || { echo 'ERROR: attractplus not found after release install.' exit 1 } cd / rm -rf \"\$TMPD\" rm -f /tmp/.attract_url " fi if [ "$ATTRACT_MODE_ACTION" = "compile" ]; then run_step "Installing Attract-Mode Plus build dependencies" sudo apt-get install -y \ libx11-dev libxi-dev libxrandr-dev libxcursor-dev libudev-dev \ libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1 libglu1-mesa-dev \ libavformat-dev libavcodec-dev libswscale-dev libavutil-dev libswresample-dev \ libfontconfig1-dev libfreetype6-dev libjpeg-dev libarchive-dev libdrm-dev \ libcurl4-gnutls-dev libgbm-dev libboost-filesystem-dev libboost-system-dev cmake run_step "Compiling Attract-Mode Plus from source" bash -c " cd '$BASE_DIR' if [ ! -d '$BASE_DIR/attractplus-source' ]; then git clone https://github.com/oomek/attractplus.git attractplus-source else cd '$BASE_DIR/attractplus-source' git pull --ff-only || true fi cd '$BASE_DIR/attractplus-source' make clean make -j\"\$(nproc)\" USE_DRM=1 sudo make install command -v attractplus >/dev/null 2>&1 || { echo 'ERROR: attractplus not found after compile.' exit 1 } " elif [ "$ATTRACT_MODE_ACTION" = "skip" ]; then echo -e "${YELLOW}======> Skipping Attract-Mode Plus install/update (--skip-attract)${NC}" echo "" fi # ====================================================================== # [13] ATTRACT-MODE CONFIGURATION # ====================================================================== if [ "$ATTRACT_MODE_ACTION" != "skip" ]; then run_step "Creating Attract-Mode MAME emulator config" bash -c " EMU_CFG='$ATTRACT_DIR/emulators/mame.cfg' if [ -f \"\$EMU_CFG\" ]; then echo 'Existing mame.cfg found - preserving.' else cat > \"\$EMU_CFG\" <<'EOF' executable /home/geekcade/mame.sh args [name] rompath /home/geekcade/mame/roms romext .zip system Arcade info_source listxml exit_hotkey Escape artwork flyer /home/geekcade/attractplus/flyers artwork marquee /home/geekcade/attractplus/marquees artwork snap /home/geekcade/attractplus/snaps artwork wheel /home/geekcade/attractplus/wheels EOF fi " run_step "Creating Attract-Mode main config if missing" bash -c " ATTRACT_CFG='$ATTRACT_DIR/config/attract.cfg' mkdir -p '$ATTRACT_DIR/config' if [ -f \"\$ATTRACT_CFG\" ]; then echo 'Existing attract.cfg found - preserving.' else cat > \"\$ATTRACT_CFG\" <<'EOF' display Arcade layout Attrac-Man romlist mame in_cycle yes in_menu yes EOF fi " run_step "Building MAME romlist if missing" bash -c " ROMLIST='$ATTRACT_DIR/romlists/mame.txt' if [ -f \"\$ROMLIST\" ]; then echo 'Existing MAME romlist found - preserving.' else echo 'Building MAME romlist...' '$ATTRACT_LAUNCHER' --build-romlist mame || true fi " fi # ====================================================================== # [14] CONFIG SHORTCUTS # ====================================================================== run_step "Creating config shortcuts" bash -c " CONFIGS_DIR='$BASE_DIR/configs' mkdir -p \"\$CONFIGS_DIR\" rm -f \"\$CONFIGS_DIR/mame\" \"\$CONFIGS_DIR/attractplus\" ln -s '$MAME_DIR' \"\$CONFIGS_DIR/mame\" ln -s '$ATTRACT_DIR' \"\$CONFIGS_DIR/attractplus\" echo \"Config shortcuts created in \$CONFIGS_DIR.\" " # ====================================================================== # [14b] USB ASSET SYNC (if USB present) # ====================================================================== # If a GEEKCADE-labeled USB is plugged in, automatically sync its assets # now. Missing folders on the USB are skipped silently. If no USB is # present, this step is a no-op and the user can run the asset sync later # from the menu. if [ -x "$BASE_DIR/geekcade_assets.sh" ]; then if "$BASE_DIR/geekcade_assets.sh" --check >/dev/null 2>&1; then echo "" echo -e "${YELLOW}======> GEEKCADE USB detected - running asset sync${NC}" "$BASE_DIR/geekcade_assets.sh" || \ echo -e "${YELLOW}Asset sync had issues - check log. Cabinet install is still complete.${NC}" else echo "" echo -e "${YELLOW}======> No GEEKCADE USB detected - skipping asset sync${NC}" echo " Plug in your USB later and run 'Sync Assets from USB'" echo " from the GeekCade menu." fi fi # ====================================================================== # [15] FINAL MESSAGES # ====================================================================== IP=$(hostname -I | awk '{print $1}') echo -e "\n${GREEN}========== INSTALLATION COMPLETE ==========${NC}" printf "%b\n" "${GREEN}All steps completed successfully!${NC}" printf "%-18s %s\n" "Log file:" "$INSTALL_LOG" printf "%-18s %s\n" "MAME dir:" "$MAME_DIR" printf "%-18s %s\n" "Attract dir:" "$ATTRACT_DIR" printf "%-18s %s\n" "MAME launcher:" "$MAME_LAUNCHER" printf "%-18s %s\n" "Attract launcher:" "$ATTRACT_LAUNCHER" printf "%-18s %s\n" "Wallpaper dir:" "$WALLPAPER_DIR" printf "%-18s %s\n" "Config shortcuts:" "$BASE_DIR/configs" printf "%-18s %s\n" "Cabinet IP:" "$IP" echo "" echo -e "${YELLOW}========================================================${NC}" echo -e "${YELLOW}CABINET BEHAVIOR:${NC}" echo " - On boot: tty1 auto-logs in and runs ~/geekcade_menu.sh" echo " - Boot gate: 5s countdown; press a key for menu, else AM+" echo " - When AM+ exits: tty1 resets, fresh login lands in menu" echo " - SSH sessions: get a normal shell (no auto-launch)" echo -e "${YELLOW}========================================================${NC}" echo "" echo -e "${GREEN}NEXT STEP:${NC} reboot to activate cabinet mode." echo " sudo reboot" echo "" touch "$SUCCESS_FLAG_FILE"