Note
This is my summary version of https://gist.github.com/sonicprod/f5a7bb10fb9ed1cc5124766831e120c4 specific to my use case. Credit (and a big thanks) to Sonicprod (+ others) for their hard work on the original!
How to make a dedicated MAME Appliance on a Raspberry Pi 4/Pi 400.
https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit
Download Raspberry Pi OS and write the image to an SD Card.
Activate SSH on initial boot: Create ssh.
file in root of sdcard, or
sudo touch $(findmnt -t vfat PARTUUID=0ee3e8a8-01 --output=target --noheadings)/ssh
Boot Raspberry Pi
Update system packages:
sudo apt update && sudo apt upgrade -y
sudo apt clean -y && sudo apt autoclean -y
System customisations
Configure Time zone and locale
sudo dpkg-reconfigure tzdata
sudo dpkg-reconfigure locales
Configure NTP
sudo nano /etc/systemd/timesyncd.conf
Create aliases
nano ~/.bash_aliases
alias update='sudo apt update && sudo apt upgrade -y && sudo apt clean -y && sudo apt autoclean -y'
alias cputemp='/usr/bin/vcgencmd measure_temp'
# Aliases to switch between Arcade Mode and Service Mode
alias arcademode='sudo systemctl enable mame-autostart.service'
alias servicemode='sudo systemctl disable mame-autostart.service'
alias mode='echo -n "The system is currently in "; systemctl -q is-active mame-autostart.service && echo -n ARCADE || echo -n SERVICE; echo " mode."'
Remove login disclaimer:
sudo rm /etc/motd
Display notices at login
sudo nano /etc/issue
S E R V I C E M O D E
IP Address: \4 \6
sudo nano /etc/bash.bashrc
echo '----------------------------------------------------------------------'
echo "The system is currently in $(systemctl -q is-active mame-autostart.service && echo ARCADE || echo SERVICE) mode."
Adjust pi account privileges
sudo usermod -a -G render pi
Enable VC4 fake KMS mode driver
sudo sed -i 's/dtoverlay=vc4-kms-v3d/dtoverlay=vc4-fkms-v3d/g' /boot/firmware/config.txt
Remove wireless and network services
sudo apt remove -y bluez pi-bluetooth nfs-common avahi-daemon modemmanager && sudo apt autoremove -y
sudo rm /var/lib/man-db/auto-update
sudo systemctl disable bluetooth.service
sudo nano /boot/firmware/config.txt
# Disable Bluetooth
dtoverlay=disable-bt
Pi performance (overclocking) and video configuration
sudo nano /boot/firmware/config.txt
# Overclocking
arm_freq=2000
over_voltage=6
# Video
disable_splash=1
cec_osd_name=Arcade
gpu_mem=128
Switch CPU Governor to Performance
sudo apt install cpufrequtils -y
grep -q GOVERNOR= /etc/init.d/cpufrequtils && sudo sed -i "s/GOVERNOR=.*$/GOVERNOR=\"performance\"/g" /etc/init.d/cpufrequtils
sudo systemctl daemon-reload
sudo systemctl restart cpufrequtils.service
Disable init script that resets Governor to ondemand
sudo systemctl disable raspi-config
Configure Hostname
sudo nano /etc/hostname
sudo nano /etc/hosts
Reboot Pi with new config
sudo reboot
Test
echo Clock Speed=$(($(/usr/bin/vcgencmd measure_clock arm | awk -F '=' '{print $2}')/1000000)) MHz
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
mkdir ~/scripts
nano ~/scripts/sdl2-latest.sh
#!/bin/bash
# This script build the latest SDL2 version without X11.
function sdl2-latest {
CHECKURL=https://github.com/libsdl-org/SDL/releases/latest
HTMLTAG='<title>Release '
LATESTSDL2VER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $2}')
if [ -z $LATESTSDL2VER ]; then echo ERROR; exit; fi # We make sure wget was successful
echo $LATESTSDL2VER
}
function sdl2-ttf-latest {
CHECKURL=https://github.com/libsdl-org/SDL_ttf/releases/latest
HTMLTAG='<title>Release '
LATESTSDL2TTFVER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $2}')
if [ -z $LATESTSDL2TTFVER ]; then echo ERROR; exit; fi # We make sure wget was successful
echo $LATESTSDL2TTFVER
}
VERSION=$(sdl2-latest)
TTFVERSION=$(sdl2-ttf-latest)
if [ "$(sdl2-config --version)" == "$VERSION" ]; then
echo SDL2 is already at the latest version \($VERSION\).
exit
else
if [ "${1,,}" != "nodep" ]; then
echo Installing SDL2 dependencies...
sudo apt install libfreetype6-dev libdrm-dev libgbm-dev libudev-dev libdbus-1-dev libasound2-dev liblzma-dev libjpeg-dev libtiff-dev libwebp-dev -y
echo OpenGL ES 2 dependencies...
sudo apt install libgles2-mesa-dev -y
fi
echo Build dependencies...
sudo apt install build-essential -y
cd ~
echo Buiding SDL2 $VERSION...
# Based from "Compile SDL2 from source"
# https://github.com/midwan/amiberry/wiki/Compile-SDL2-from-source
wget https://libsdl.org/release/SDL2-${VERSION}.zip
unzip SDL2-${VERSION}.zip
rm SDL2-${VERSION}.zip
cd SDL2-${VERSION}
./configure --disable-video-opengl --disable-video-opengles1 --disable-video-x11 --disable-pulseaudio --disable-esd --disable-video-wayland --disable-video-rpi --disable-video-vulkan --enable-video-kmsdrm --enable-video-opengles2 --enable-alsa --disable-joystick-virtual --enable-arm-neon --enable-arm-simd
[ $(uname -m) == "armv7l" ] && make -j $(nproc) CFLAGS='-mtune=cortex-a72 -mfpu=neon-fp-armv8 -mfloat-abi=hard'
[ $(uname -m) == "aarch64" ] && make -j $(nproc) CFLAGS='-mcpu=cortex-a72'
sudo make install
# SDL2_ttf
wget https://libsdl.org/projects/SDL_ttf/release/SDL2_ttf-${TTFVERSION}.tar.gz
tar zxvf SDL2_ttf-${TTFVERSION}.tar.gz
rm SDL2_ttf-${TTFVERSION}.tar.gz
cd SDL2_ttf-${TTFVERSION}
./configure
make -j $(nproc)
sudo make install
sudo ldconfig -v
cd ~
sudo rm -R SDL2-${VERSION}
sudo rm -R SDL2_ttf-${TTFVERSION}
fi
chmod +x ~/scripts/sdl2-latest.sh && ~/scripts/sdl2-latest.sh
nano ~/scripts/mame-updater.sh
#!/bin/bash
# This script update MAME to the latest version or a specific version.
# GCC compiler optimization for ARM-based systems : https://gist.github.com/fm4dd/c663217935dc17f0fc73c9c81b0aa845
#ARCHOPTS='-mcpu=cortex-a72 -mtune=cortex-a72 -mfpu=neon-fp-armv8 -mfloat-abi=hard -mneon-for-64bits -funsafe-math-optimizations -munaligned-access -fexpensive-optimizations'
# A tester.....
ARCHOPTS32='-march=armv8-a+crc+simd -mcpu=cortex-a72 -mtune=cortex-a72 -mfpu=neon-fp-armv8 -mfloat-abi=hard -funsafe-math-optimizations -fprefetch-loop-arrays -fexpensive-optimizations'
ARCHOPTS64='-march=armv8-a+crc+simd -mcpu=cortex-a72 -mtune=cortex-a72 -funsafe-math-optimizations -fprefetch-loop-arrays -fexpensive-optimizations'
MAKEOPTS='TARGETOS=linux NO_X11=1 NOWERROR=1 NO_USE_XINPUT=1 NO_USE_XINPUT_WII_LIGHTGUN_HACK=1 NO_OPENGL=1 USE_QTDEBUG=0 DEBUG=0 REGENIE=1 NO_BGFX=1 FORCE_DRC_C_BACKEND=1 NO_USE_PORTAUDIO=1 SYMBOLS=0'
MAXTHREAD=2 # From MAME version 0.227 and up, we should use a max. of 3 threads to avoid an out of memory error.
secs_to_human() {
echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}
fs_unlock() { # Put root filesystem in read/write mode
for o in $(findmnt -n -o OPTIONS / | sed 's/,/ /g'); do
[ "${o,,}" = ro ] && sudo mount -o remount,rw /
done
}
fs_lock() { # Put root filesystem in read-only mode
for o in $(findmnt -n -o OPTIONS / | sed 's/,/ /g'); do
[ "${o,,}" = rw ] && sudo mount -o remount,ro /
done
}
set_env() { # Make an environment variable persistent
local KEY=$(echo $1 | awk -F '=' '{print $1}')
local VALUE=$(echo $1 | awk -F '=' '{print $2}')
grep -q $KEY= /etc/environment && sudo sed -i "s/$KEY=.*$/$KEY=$VALUE/g" /etc/environment || echo "$KEY=$VALUE" | sudo tee -a /etc/environment
}
mame-latest() { # Get the latest version of MAME
CHECKURL=https://github.com/mamedev/mame/releases/latest
HTMLTAG='<title>Release MAME'
LATESTMAMEVER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $3}')
if [ -z $LATESTMAMEVER ]; then echo ERROR; exit; fi # We make sure wget was successful
echo $LATESTMAMEVER
}
if (systemctl -q is-active mame-autostart.service) then
echo "The system must be put in SERVICE Mode first."
exit
fi
if [ ! $1 ]; then
echo Usage: $0 VER | Latest
echo ' Where VER is the 4-digit version number of MAME to update (for example: 0224).'
echo ' OR use the argument Latest to update to the latest available MAME version.'
else
if [ $(free -g | awk '/^Mem:/{print $2}') -lt 3 ]; then
echo You need 4 GB of RAM to compile MAME with multithread support.
exit
fi
if [ "${1,,}" == "latest" ]; then
MAMEVER=$(mame-latest)
MAMEVER=${MAMEVER//./} # Remove the dot
else
MAMEVER=$1
fi
MAMESRCPATH=/home/pi/mame${MAMEVER}
SCRIPTPATH=${0%/*}
echo Installing/uptading to MAME $MAMEVER...
fs_unlock
# Install MAME dependencies...
sudo apt install fontconfig libfontconfig-dev libx11-dev libpulse-dev -y
if [ ! -d $MAMESRCPATH ]; then
cd ~
if [ ! -f ~/mame${MAMEVER}.zip ]; then # Not already downloaded
wget https://github.com/mamedev/mame/archive/mame${MAMEVER}.zip
fi
if [ -f ~/mame${MAMEVER}.zip ]; then # If download successful, uncompress ...
unzip mame${MAMEVER}.zip
mv mame-mame${MAMEVER} mame${MAMEVER}
rm mame${MAMEVER}.zip
fi
fi
if [ ! -x $MAMESRCPATH/mame ]; then # We build only if not already built
# Build of MAME
# Dependencies
# Swap requirement
sudo apt install dphys-swapfile -y
if [ "$(grep '^CONF_SWAPSIZE' /etc/dphys-swapfile | cut -d= -f2)" != "2048" ]; then
sudo sed -i "s/^#\{0,1\}\s*CONF_SWAPSIZE=.*$/CONF_SWAPSIZE=2048/g" /etc/dphys-swapfile
fi
# The swap value should be 2048
# Activate the swap...
sudo systemctl stop dphys-swapfile.service
sudo systemctl start dphys-swapfile.service
# Build dependencies
sudo apt install build-essential -y
cd $MAMESRCPATH
[ "$(uname -m)" == "armv7l" ] && echo MAKE CMDLINE=make -j$MAXTHREAD ARCHOPTS=\"$ARCHOPTS32\" $MAKEOPTS PLATFORM=arm
[ "$(uname -m)" == "aarch64" ] && echo MAKE CMDLINE=make -j$MAXTHREAD ARCHOPTS=\"$ARCHOPTS64\" $MAKEOPTS PLATFORM=arm64 PTR64=1
BUILDSTART=$(date +%s)
echo Build start time: $(date +"%T")
echo -----------------------------------------------------------------------------------
echo Please wait until the build is completed \(about 10 hours\)...
echo -----------------------------------------------------------------------------------
[ "$(uname -m)" == "armv7l" ] && make -j$MAXTHREAD ARCHOPTS="$ARCHOPTS32" $MAKEOPTS PLATFORM=arm
[ "$(uname -m)" == "aarch64" ] && make -j$MAXTHREAD ARCHOPTS="$ARCHOPTS64" $MAKEOPTS PLATFORM=arm64 PTR64=1
echo Build time took: $(secs_to_human "$(($(date +%s) - ${BUILDSTART}))").
if [ -x $MAMESRCPATH/mame ]; then
echo -----------------------------------------------------------------------------------
echo Build Success!
date +"%T"
echo -----------------------------------------------------------------------------------
# Successful build!
# We make sure the MAME environment variable is persistent...
set_env SDL_VIDEODRIVER=kmsdrm
set_env SDL_RENDER_DRIVER=opengles2
set_env SDL_RENDER_VSYNC=1
set_env SDL_GRAB_KEYBOARD=1
set_env SDL_VIDEO_GLES2=1
# Install MAME True-Type Liberation Sans font
if [ ! -d /usr/share/fonts/truetype/liberation-fonts ]; then
echo Installing Liberation Sans font...
cd ~
wget -q https://dl.dafont.com/dl/?f=liberation_sans -O liberation_sans.zip
if [ -f liberation_sans.zip ]; then
sudo mkdir /usr/share/fonts/truetype/liberation-fonts
sudo unzip -o /home/pi/liberation_sans.zip *.ttf -d /usr/share/fonts/truetype/liberation-fonts
rm liberation_sans.zip
sudo fc-cache -f -v
fi
fi
# MAME binary symlink creation or update
if [ -L ~/mame ]; then rm ~/mame; fi
ln -s $MAMESRCPATH ~/mame
if [ ! -L ~/.mame ] && [ ! -d ~/.mame ]; then # MAME data path symlink creation
[ -d /data/mame ] && ln -s /data/mame ~/.mame || mkdir ~/.mame
fi
if [ ! -f ~/.mame/mame.ini ]; then # If mame.ini does not exist, let's create it
cd ~/.mame
$MAMESRCPATH/mame \
-hashpath '$HOME/mame/hash' \
-languagepath '$HOME/mame/language' \
-pluginspath '$HOME/mame/plugins' \
-artpath '$HOME/.mame/artwork' \
-ctrlrpath '$HOME/.mame/ctrlr' \
-inipath '$HOME/.mame/ini' \
-homepath '$HOME/.mame/lua' \
-rompath '$HOME/.mame/roms' \
-cfg_directory '$HOME/.mame/cfg' \
-diff_directory '$HOME/.mame/diff' \
-input_directory '$HOME/.mame/inp' \
-nvram_directory '$HOME/.mame/nvram' \
-snapshot_directory '$HOME/.mame/snap' \
-state_directory '$HOME/.mame/sta' \
-skip_gameinfo \
-video accel \
-videodriver kmsdrm \
-renderdriver opengles2 \
-audiodriver alsa \
-samplerate 22050 \
-createconfig
fi
# Freeing some space...
for f in src build 3rdparty roms
do
rm -Rf $MAMESRCPATH/$f
done
# Removing swap...
sudo systemctl stop dphys-swapfile.service
sudo rm /var/swap
sudo apt remove dphys-swapfile build-essential -y
sudo apt autoremove -y
else
echo -----------------------------------------------------------------------------------
echo Build FAILED.
date +"%T"
echo -----------------------------------------------------------------------------------
fi
fi
fs_lock
fi
chmod +x ~/scripts/mame-updater.sh
# Compile latest version of MAME (~11 hours):
scripts/mame-updater.sh latest > build.txt 2>&1
Set up MAME configuration
cd ~/.mame
mkdir artwork cabinets cfg cpanels ctrlr diff flyers hi history icons ini inp lua marquees memcard pcb nvram roms snap sta titles ui video
mv mame.ini ui.ini plugin.ini hiscore.ini ini/
ln -s ./ini/mame.ini mame.ini
[ -f ./lua/hiscore/plugin.cfg ] && mv ./lua/hiscore/plugin.cfg ./hi
rmdir ./lua/hiscore
ln -s ./hi ./lua/hiscore
mv ~/mame/artwork/* ~/.mame/artwork/
mv ~/mame/ctrlr/* ~/.mame/ctrlr/
mv ~/mame/roms/* ~/.mame/roms/
mv ~/mame/snap/* ~/.mame/snap/
cd ~/mame; rm -R artwork ctrlr nvram history roms snap
nano ~/.attract/emulators/mame.cfg
artwork marquee $HOME/.mame/marquees
artwork snap $HOME/.mame/video;$HOME/.mame/snap
nano ~/.mame/ini/plugin.ini
#
# PLUGINS OPTIONS
#
cheat 0
cheatfind 0
console 0
data 1
dummy 0
gdbstub 0
hiscore 1
layout 0
portname 0
timer 0
nano ~/.mame/ini/hiscore.ini
hi_path $HOME/.mame/hi
nano ~/.mame/ini/ui.ini
Adjust the paths below by specifying the prefix $HOME/.mame/
followed by the folder name:
historypath $HOME/.mame/history
cabinets_directory $HOME/.mame/cabinets
cpanels_directory $HOME/.mame/cpanels
pcbs_directory $HOME/.mame/pcb
flyers_directory $HOME/.mame/flyers
titles_directory $HOME/.mame/titles
marquees_directory $HOME/.mame/marquees
icons_directory $HOME/.mame/icons
ui_path $HOME/.mame/ui
[Optional] MAME customisations
nano ~/.mame/ini/mame.ini
effect scanlines
chmod 0444 ~/.attract/emulators/mame.cfg
...or SFTP artwork and ini's from backup.
MAME version check script
nano ~/scripts/mame-version-check.sh
#!/bin/bash
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
# This function check the latest available version of MAME and display a notice if the current version is older.
function mame-check {
CHECKURL=https://github.com/mamedev/mame/releases/latest
HTMLTAG='<title>Release MAME'
[ -x /home/pi/mame/mame ] && export MAMEVER=$(/home/pi/mame/mame -version | cut -d' ' -f1)
if [ ! -z $MAMEVER ]; then
LATESTMAMEVER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $3}')
if [ -z $LATESTMAMEVER ]; then echo $MAMEVER ERROR; exit; fi # We make sure wget was successful
if [ $(version $LATESTMAMEVER) -gt $(version $MAMEVER) ]; then
echo $MAMEVER $LATESTMAMEVER
else
echo $MAMEVER Latest
fi
fi
}
echo '+---------------+-----------+-----------+'
echo '| EMULATOR | CURRENT | LATEST |'
echo '+---------------+-----------+-----------+'
[ -x $HOME/mame/mame ] && echo -n '| MAME | '; mame-check | awk '{ printf "%-9s | %-9s |\n", $1, $2}'
echo '+---------------+-----------+-----------+'
chmod +x ~/scripts/mame-version-check.sh
nano ~/scripts/attractplus-build.sh
#!/bin/bash
sudo apt install -y build-essential cmake libxrandr-dev libxcursor-dev libudev-dev libfreetype-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev
CHECKURL=https://github.com/SFML/SFML/releases/latest
HTMLTAG='<title>Release SFML'
LATEST_SFML_VER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $3}')
wget https://github.com/SFML/SFML/archive/refs/tags/$LATEST_SFML_VER.zip -P ~
unzip ~/$LATEST_SFML_VER.zip
cd ~/SFML-$LATEST_SFML_VER/
mkdir build
cd build
cmake .. -DSFML_USE_DRM=TRUE
make -j $(nproc)
sudo make install
sudo ldconfig -v
rm ~/$LATEST_SFML_VER.zip
rm -R ~/SFML-$LATEST_SFML_VER/
sudo apt install -y libavutil-dev libavcodec-dev libavformat-dev libavfilter-dev libswscale-dev libswresample-dev libgl1-mesa-dev libglu1-mesa-dev libcurl4-openssl-dev libopenal-dev
CHECKURL=https://github.com/oomek/attractplus/releases/latest
HTMLTAG='<title>Release Attract-Mode Plus'
LATEST_AMP_VER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $4}')
wget https://github.com/oomek/attractplus/archive/refs/tags/$LATEST_AMP_VER.zip -P ~
unzip ~/$LATEST_AMP_VER.zip
cd ~/attractplus-$LATEST_AMP_VER/
make -j $(nproc) USE_DRM=1 USE_MMAL=1 USE_GLES=1
sudo make install
mv ./config ~/.attract
rm ~/$LATEST_AMP_VER.zip
rm -R ~/attractplus-$LATEST_AMP_VER/
CHECKURL=https://github.com/zpaolo11x/Arcadeflow/releases/latest
HTMLTAG='<title>Release Arcadeflow'
LATEST_AF_VER=$(wget -q -O - $CHECKURL | grep "$HTMLTAG" | awk '{print $3}')
wget https://github.com/zpaolo11x/Arcadeflow/archive/refs/tags/$LATEST_AF_VER.zip -P ~
rm -R ~/.attract/layouts/Arcadeflow/
unzip ~/$LATEST_AF_VER.zip -d ~/.attract/layouts/
mv ~/.attract/layouts/Arcadeflow-$LATEST_AF_VER ~/.attract/layouts/Arcadeflow
rm ~/$LATEST_AF_VER.zip
chmod +x ~/scripts/attractplus-build.sh && ~/scripts/attractplus-build.sh
Front-end startup script
nano ~/scripts/autostart.sh
#!/bin/bash
until /usr/local/bin/attractplus > ~/.mame/run.log 2>&1; do
sleep 1
done
find ~/.mame/log.txt -size +2M -delete
chmod +x ~/scripts/autostart.sh
Front-end auto-start
sudo systemctl edit mame-autostart.service --force --full
[Unit]
Description=MAME Appliance Autostart service
[email protected] rng-tools.service cron.service
Requires=local-fs.target
After=local-fs.target
[Service]
User=pi
Group=pi
PAMName=login
Type=simple
ExecStart=/home/pi/scripts/autostart.sh
Restart=on-abort
RestartSec=5
TTYPath=/dev/tty1
StandardInput=tty
[Install]
WantedBy=multi-user.target
Also=shutdown.service
sudo systemctl disable mame-autostart.service
Reactivation of the cursor in the console when getty@tty1 is activated (Service Mode):
nano ~/.bashrc
setterm --cursor on
Automatic system shutdown on front-end exit
sudo systemctl edit shutdown.service --force --full
[Unit]
Description=Shutdown and poweroff service
After=mame-autostart.service
[Service]
TTYPath=/dev/tty1
ExecStart=/sbin/poweroff
StandardInput=tty
[Install]
WantedBy=multi-user.target
sudo systemctl disable shutdown.service
Re-configure fstab
sudo nano /boot/firmware/cmdline.txt
change root=PARTUUID=xxxxxxxx
to root=/dev/mmcblk0p2
sudo nano /etc/fstab
/dev/mmcblk0p1 /boot vfat defaults,rw 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime,rw 0 1
Hide OS output on console
sudo nano /boot/firmware/cmdline.txt
change console=tty1
to console=tty3
add:
logo.nologo vt.global_cursor_default=0 quiet init=/bin/systemd
Display splash screens on boot and shutdown
sudo apt-get install fim -y
mkdir ~/splash
copy mame.jpg into ~/splash and symlink
ln -s ~/splash/mame.jpg ~/splash/mame-boot.jpg
ln -s ~/splash/mame.jpg ~/splash/mame-shutdown.jpg
Create services:
sudo systemctl edit mame-bootsplash.service --force --full
[Unit]
Description=MAME Boot Splash Screen service
DefaultDependencies=no
After=local-fs.target
[Service]
Type=simple
StandardInput=tty
StandardOutput=tty
ExecStart=/usr/bin/fim -q --no-history /home/pi/splash/mame-boot.jpg
SuccessExitStatus=42
[Install]
WantedBy=sysinit.target
sudo systemctl enable mame-bootsplash.service
sudo systemctl edit mame-shutdownsplash.service --force --full
[Unit]
Description=MAME Shutdown/Poweroff/Reboot Splash Screen service
DefaultDependencies=no
Before=halt.target
Conflicts=mame-bootsplash.service
[Service]
Type=simple
ExecStart=/usr/bin/fim -q --no-history /home/pi/splash/mame-shutdown.jpg
SuccessExitStatus=42
[Install]
WantedBy=reboot.target halt.target poweroff.target
sudo systemctl enable mame-shutdownsplash.service
sudo systemctl start mame-shutdownsplash.service
Download some games to test
cd ~/.mame/roms
wget -i - << 'EOF'
https://archive.org/download/mame-merged/mame-merged/dkong.zip
https://archive.org/download/mame-merged/mame-merged/galaga.zip
https://archive.org/download/mame-merged/mame-merged/puckman.zip
https://archive.org/download/mame-merged/mame-merged/raiden2.zip
https://archive.org/download/mame-merged/mame-merged/simpsons.zip
EOF
Cleanup OS
sudo apt remove -y build-essential cmake
sudo apt autoremove --purge && sudo apt autoclean && sudo apt clean
arcademode
sudo reboot
Note
Now is a good time to backup the SD card in another machine, and shrink/compress the img with PiShrink (https://github.com/Drewsif/PiShrink)
Create separate read-write partition and move atract and mame data there
In Service mode:
nano ~/scripts/move-data.sh
#!/bin/bash
# Expand root parition and create /data parition
sudo fdisk /dev/mmcblk0 << 'EOF'
d
2
n
p
2
1056768
+15G
n
p
3
32514048
+5G
w
EOF
sudo partprobe /dev/mmcblk0
# Resize root file system
sudo e2fsck -f /dev/mmcblk0p2
sudo resize2fs /dev/mmcblk0p2
sync
sudo apt -y install f2fs-tools
# Format and mount new data parition
sudo wipefs /dev/mmcblk0p3
sudo mkfs.f2fs -l data /dev/mmcblk0p3 -f
sudo mkdir /data
sudo mount -t f2fs -o rw /dev/mmcblk0p3 /data
sudo mount -o remount,rw /data
sudo su -c "echo '/dev/mmcblk0p3 /data f2fs defaults,noatime 0 2' >> /etc/fstab"
echo 'Creating /data...'
cd /data
sudo mkdir mame attract
sudo chown -R pi:pi /data
sudo chmod -R 3774 /data
# move MAME configuration
echo 'Moving .mame to /data/...'
mv -b ~/.mame/* /data/mame/
cd ~; rmdir .mame
ln -s /data/mame ~/.mame
# move Attract-Mode Plus configuration
echo 'Moving .attract to /data/...'
mv -b ~/.attract/* /data/attract/
cd ~; rmdir .attract
ln -s /data/attract ~/.attract
chmod +x ~/scripts/move-data.sh && ~/scripts/move-data.sh
nano ~/scripts/read-only-rootfs.sh
#!/bin/bash
# Check /etc/fstab to see if this script has already been executed
awk '/\/ /{print $4}' /etc/fstab | grep -q ro, && awk '/\/boot/{print $4}' /etc/fstab | grep -q ro, && echo 'This script has already been executed and your system is already in read-only mode.' && exit
# From: https://www.dzombak.com/blog/2024/03/Running-a-Raspberry-Pi-with-a-read-only-root-filesystem.html
# Migrate to ntp instead of systemd-timesyncd
sudo apt install -y ntp
sudo sh -c 'echo "driftfile /var/tmp/ntp.drift" >> /etc/ntp.conf'
# sudo systemctl edit ntp
sudo mkdir -p /etc/systemd/system/ntpsec.service.d/
sudo sh -c 'tee -a /etc/systemd/system/ntpsec.service.d/override.conf << "EOF"
[Service]
PrivateTmp=false
EOF'
# Move fake-hwclock to rw partition
# From: https://www.dzombak.com/blog/2024/06/Moving-fake-hwclock-to-a-separate-partition-on-a-read-only-Raspberry-Pi.html
mkdir /data/system
sudo chmod 0777 /data/system
sudo sudo sh -c 'echo "FILE=/data/system/fake-hwclock.data" >> /etc/default/fake-hwclock'
sudo rm /etc/cron.hourly/fake-hwclock
sudo tee -a /etc/cron.hourly/fake-hwclock << 'EOF'
#!/bin/sh
#
# Simple cron script - save the current clock periodically
# https://www.dzombak.com/blog/2024/06/Moving-fake-hwclock-to-a-separate-partition-on-a-read-only-Raspberry-Pi.html
#
if (command -v fake-hwclock >/dev/null 2>&1) ; then
PARAM=/etc/default/fake-hwclock
if [ -f "$PARAM" ]; then
set -o allexport
. "$PARAM"
set +o allexport
fi
fake-hwclock save
fi
EOF
# sudo systemctl edit fake-hwclock.service
sudo mkdir -p /etc/systemd/system/fake-hwclock.service.d
sudo sh -c 'tee -a /etc/systemd/system/fake-hwclock.service.d/override.conf << "EOF"
[Unit]
RequiresMountsFor=/data/system/fake-hwclock.data
EOF'
# NetworkManager
sudo apt install -y resolvconf
[ ! -L /etc/resolv.conf ] && sudo rm /etc/resolv.conf && sudo ln -s /run/resolvconf/resolv.conf /etc/resolv.conf
sudo rm -rf /var/lib/dhcp && sudo ln -s /var/run /var/lib/dhcp
sudo rm -rf /var/lib/NetworkManager && sudo ln -s /var/run /var/lib/NetworkManager
# Update the systemd random seed
[ -f /var/lib/systemd/random-seed ] && sudo mv /var/lib/systemd/random-seed /tmp/systemd-random-seed
[ ! -L /var/lib/systemd/random-seed ] && sudo ln -s /tmp/systemd-random-seed /var/lib/systemd/random-seed
# Disable systemd-rfkill
sudo systemctl disable systemd-rfkill.service
sudo systemctl mask systemd-rfkill.socket
# Move temporary folders to tmpfs
sudo tee -a /etc/fstab << 'EOF'
tmpfs /tmp tmpfs defaults,noatime,nosuid,nodev 0 0
tmpfs /var/tmp tmpfs defaults,noatime,nosuid,nodev 0 0
tmpfs /var/lib/logrotate tmpfs defaults,noatime,nosuid,nodev,noexec,size=1m,mode=0755 0 0
tmpfs /var/lib/sudo tmpfs defaults,noatime,nosuid,nodev,noexec,size=1m,mode=0700 0 0
EOF
# Disable journald
sudo sed -i '/Storage=/c\Storage=none' /etc/systemd/journald.conf
# back to Sonicprod's script
# Shell commands to switch between RO and RW modes
grep -q "alias ro=" /etc/bash.bashrc || sudo tee -a /etc/bash.bashrc << 'EOF'
set_bash_prompt() {
fs_mode=$(mount | sed -n -e "s/^\/dev\/.* on \/ .*(\(r[w|o]\).*/\1/p")
PS1='\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
}
alias ro='sudo mount -o remount,ro / ; sudo mount -o remount,ro /boot'
alias rw='sudo mount -o remount,rw / ; sudo mount -o remount,rw /boot'
PROMPT_COMMAND=set_bash_prompt
EOF
# Disable swap and filesystem check and set fs to read-only
sudo sed -ie 's/^console=serial0,115200.*$/& fsck.mode=skip noswap ro/g' /boot/cmdline.txt
# /etc/fstab: Add read-only mode to /boot or /boot/firmware filesystems
sudo sed -i '/\/boot/{/ro/!s/\S\S*/&,ro/4}' /etc/fstab
# /etc/fstab: Add read-only mode to / root filesystem
sudo sed -i '/\S\s\s*\/\s\s*/{/\(ro,\|,ro\)/!s/\S\S*/&,ro/4}' /etc/fstab
# Disable auto-update daemons
sudo systemctl stop systemd-tmpfiles-clean.timer apt-daily.timer apt-daily-upgrade.timer man-db.timer systemd-tmpfiles-clean.service apt-daily-upgrade.service
sudo systemctl disable systemd-tmpfiles-clean.timer apt-daily.timer apt-daily-upgrade.timer man-db.timer systemd-tmpfiles-clean.service apt-daily-upgrade.service
sudo systemctl mask systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service apt-daily-upgrade.service
# Put file system back to read-only on log out
sudo sudo sh -c 'echo "sudo mount -o remount,ro /boot\nsudo mount -o remount,ro /" >> /etc/bash.bash_logout'
# Alias ajustments
sed -ie "s/^alias arcademode=.*$/alias arcademode=\'rw; sudo systemctl enable mame-autostart.service; ro\'/g" ~/.bash_aliases
sed -ie "s/^alias servicemode=.*$/alias servicemode=\'rw; sudo systemctl disable mame-autostart.service; ro\'/g" ~/.bash_aliases
# Systemd daemon reload to update changes
sudo systemctl daemon-reload
echo ---------------------------------------------------------------------
echo Completed.
echo The system will reboot NOW.
echo ---------------------------------------------------------------------
sudo reboot
chmod +x ~/scripts/read-only-rootfs.sh && ~/scripts/read-only-rootfs.sh
Two aliases (rw and ro) are created to switch from read-write to read-only mode and vice-versa.