# Audio ArchR ships PipeWire + ALSA on top of the RK817 codec. Most things just work; this page documents the corner cases. *** ## Output paths [#output-paths] Six possible audio sinks, picked automatically: ``` SPK internal speakers (default if nothing else plugged) HP headphone jack (auto-detected via extcon) HDMI HDMI audio out (when an HDMI cable is connected) USB USB audio device (DAC, headset, gamepad with audio) BT Bluetooth audio sink (paired headphones / speaker) SIMPLE legacy fallback for clone boards with non-standard amplifier wiring ``` Detection happens in `/etc/profile.d/050-audio` at login + on hotplug via udev. ### Force a path [#force-a-path] ```bash amixer set 'Playback Path' SPK # speakers amixer set 'Playback Path' HP # headphones amixer set 'Playback Path' HDMI # HDMI ``` The setting is reset on next hotplug event, to make it sticky, edit `/storage/.config/system.cfg` → `audio.path=SPK|HP|HDMI`. *** ## Volume [#volume] Volume buttons (VOL+ / VOL-) drive the **DAC** mixer control directly. Range 0-255, step 2 % per press, persists across reboots. ```bash amixer get DAC # current value, 0..255 amixer cset numid=N # set explicitly (find numid with `amixer controls`) ``` Max DAC level on RK817 is capped at 98 % to avoid speaker rattle on small clone speakers; this is set by the `050-audio_path` quirk for RK3326 (`Playback Mux` adjustment). *** ## SR variant ("force simple audio") [#sr-variant-force-simple-audio] Some clone motherboards wire the amplifier to a different GPIO than the standard RK817 codec config expects. The Flasher offers an **SRs** toggle that swaps to a simpler amplifier path. If your device plays everything through the headphone jack but the speakers are silent regardless of jack state, re-flash with **SRs** ON. The setting lives in the panel DTBO file, it can't be changed at runtime; you have to re-flash with the right overlay or copy a different `*_SRs.dtbo` to `/flash/overlays/mipi-panel.dtbo` from a PC. *** ## HDMI audio [#hdmi-audio] Plug an HDMI cable; audio output switches automatically. The HDMI port is HDMI 1.4, supports stereo PCM at 48 kHz. No surround. If the display works but audio stays on speakers: ```bash aplay -L | grep -i hdmi amixer set 'Playback Path' HDMI ``` *** ## USB audio [#usb-audio] Most USB audio class 1.0 devices (the common DACs, headsets, capture devices) work plug-and-play. PipeWire picks them up via `auto-link`. Switch to USB output: ```bash pactl list sinks short pactl set-default-sink ``` *** ## Bluetooth audio [#bluetooth-audio] Pair from ES → System Settings → Bluetooth → Pair Device. Once paired, audio routes there automatically. Caveats: * **Latency**: A2DP is \~150 ms one-way, fine for music, noticeable in games. The RK3326 has no aptX-LL hardware. * **Re-pairing**: if the speaker doesn't reconnect after sleep, in ES BT menu → Manage Paired Devices → Reconnect. * **Both speaker + BT simultaneously**: not supported, pulseaudio routing is single-sink at a time. *** ## Per-emulator audio backend [#per-emulator-audio-backend] ``` RetroArch ALSA via "alsathread" (out of the way of the libretro frame loop) PPSSPP standalone SDL2 audio → PipeWire Flycast standalone SDL2 audio → PipeWire Mupen64Plus standalone SDL2 audio → ALSA DraStic SDL2 audio → ALSA ``` `alsathread` for RetroArch trades a couple ms of latency for predictable scheduling, important on a CPU-bound A35. *** ## Troubleshooting [#troubleshooting] **Speakers crackle in heavy 3D games.** Pre-v8 builds had a `mali_kbase` PM regulator unbalance that perturbed the audio clock domain. Update to v8+. If still happens: `dmesg | grep -iE 'vdd_logic|clk_gpu|mali'`, empty result is what you want. **Audio stutters under load.** PipeWire quantum too low. Edit `/storage/.config/pipewire/pipewire.conf.d/99-quantum.conf`: ```ini default.clock.quantum = 1024 default.clock.min-quantum = 512 default.clock.max-quantum = 2048 ``` Restart PipeWire: `systemctl --user restart pipewire`. **Bluetooth audio choppy / dropouts.** SBC bitpool too high. Force lower-quality: ES → BT → Manage Paired Devices → "Force SBC" toggle. **HP detected but speakers stay on (or vice versa).** Extcon driver issue. Check `cat /sys/class/extcon/*/state`. Log a bug with this output if it doesn't change when you plug/unplug. # Bluetooth ArchR ships BlueZ + PipeWire bluetooth backend. No `bluealsa`, codec routing happens through PipeWire, which gives a cleaner integration but means some quirky devices behave differently than older docs imply. *** ## Pair an audio headset [#pair-an-audio-headset] ### Enable Bluetooth [#enable-bluetooth] ES → **System Settings** → **Controller & Bluetooth Settings** → **Enable Bluetooth** → ON. ### Put your headset in pairing mode [#put-your-headset-in-pairing-mode] Refer to your headset's manual, usually holding the power button until the LED blinks rapidly. ### Pair from the device [#pair-from-the-device] Same menu → **Pair a Bluetooth Device**. Wait for your headset's name to appear, select it. ### Route audio [#route-audio] Once paired, ES → **System Settings** → **Audio Device** → pick the BT device from the list. ES restarts; output now goes through Bluetooth. *** ## Pair a gamepad [#pair-a-gamepad] Same menu, **Pair a Bluetooth Device**, put gamepad in pairing mode (varies, most Xbox/8BitDo gamepads have a tiny pairing button under or next to the USB port). After pairing, ES auto-detects the gamepad and prompts you to map buttons. Mapping is saved per-device in `/storage/.config/SDL2/gamecontrollerdb.txt`. *** ## Caveats on RK3326 [#caveats-on-rk3326] The RK3326's WiFi/BT chip is on a single shared antenna. Real-world implications: * **Range is \~3 m**: Bluetooth headphones across the room will stutter. * **WiFi competes with BT for airtime**: heavy network use during gameplay can crackle the audio. Pause Syncthing/Tailscale/ZeroTier during gameplay (ArchR does this automatically via `pause_background_services` in `runemu.sh`). * **A2DP latency** is \~150 ms, fine for music, noticeable in fast-paced games. ArchR has no aptX-LL hardware. *** ## Codec selection [#codec-selection] PipeWire picks the best codec your headset advertises (LDAC > aptX > AAC > SBC). Force a lower codec if you hit dropouts: ```bash # inspect current codec pactl list sinks | grep -A 1 "Active Port" # force SBC (universal, lowest bandwidth) pactl set-sink-port bluez_sink..a2dp-sink "speaker-output-sbc" ``` For LDAC at higher bitrates the A35 CPU load is non-trivial, `mobile` (330 kbps) and `standard` (660 kbps) profiles work; `high` (990 kbps) on top of an emulator is enough to cause crackle. *** ## Disconnect cleanly [#disconnect-cleanly] Always toggle BT off in ES (or unpair) before powering off. Cold-disconnect (just turning off your headset) sometimes leaves PipeWire holding a stale sink, which means no audio until reboot. If it happens, over [SSH](/docs/ssh): ```bash systemctl --user restart pipewire pipewire-pulse wireplumber ``` *** ## Known issues [#known-issues] * **PCSX-ReARMed core**: silent under Bluetooth audio for unknown reasons (carry-over from upstream). Workaround: use `pcsx_rearmed32` or `swanstation` libretro core, or DuckStation standalone. * **Reconnect-on-resume**: after a fake-suspend wake-up (when RK3326 supports that some day), BT may not reconnect. Manual reconnect in the Bluetooth menu. # Build from source ArchR's build system is fully reproducible, `make docker-RK3326` produces both image variants from a clean checkout, identical to the GitHub release. No hidden steps, no closed-source artifacts. *** ## Requirements [#requirements] * Docker (recommended) or a native Linux build environment * \~40 GB free disk space (cached source + build trees) * \~8 GB RAM recommended (the kernel build is the spike) * A x86\_64 host (cross-toolchain targets aarch64) A native build takes \~2 h on a 12-core x86; Docker adds \~5 % overhead. *** ## Quick build [#quick-build] ```bash git clone https://github.com/archr-linux/Arch-R.git cd Arch-R # First time only: build the Docker build environment make docker-image-build # Build both image variants for R36S make docker-RK3326 ``` Output in `target/`: ``` ArchR-R36S.aarch64-YYYYMMDD-original.img.gz ArchR-R36S.aarch64-YYYYMMDD-clone.img.gz *.sha256 # checksums for the flasher ArchR-R36S.aarch64-YYYYMMDD.tar # rootfs only (for re-imaging) ``` *** ## All commands [#all-commands] | Command | What it does | | ------------------------- | ------------------------------------ | | `make docker-RK3326` | Full Docker build, both variants | | `make RK3326` | Native build (toolchain on host) | | `make docker-image-build` | (Re)build the Docker build container | | `make clean` | `rm -rf build.* target` | | `make distclean` | `clean` + nuke source caches | For other devices: `make docker-RK3588`, `make docker-S922X`, etc., see the per-device options under `projects/ArchR/devices/`. *** ## Incremental rebuilds [#incremental-rebuilds] The build system uses **stamps** in `build.ArchR-RK3326.aarch64/.stamps/` to skip already-built packages. To force a rebuild of a specific package: ```bash rm -rf build.ArchR-RK3326.aarch64/.stamps/ make docker-RK3326 ``` Common ones: ``` linux kernel + DTS + modules mali-bifrost libmali kernel module mesa Mesa Panfrost retroarch RetroArch frontend + libretro frame ppsspp-sa PPSSPP standalone flycast-sa Flycast standalone archr scripts / configs / runemu.sh emulators es_systems.cfg generation ``` After a stamp removal, `make docker-RK3326` rebuilds just that package and re-images. *** ## Project layout [#project-layout] ``` Arch-R/ ├── packages/ upstream LibreELEC-style package recipes │ ├── compress/zlib/ (ArchR uses zlib-ng compat, see PKG_NAME) │ ├── graphics/mesa/ │ ├── linux/ │ └── ... ├── projects/ArchR/ │ ├── devices/RK3326/ RK3326-specific patches, DTS, kernel config │ │ ├── linux/ kernel config + DTS overrides │ │ ├── patches/linux/ per-version kernel patches │ │ └── options device-level vars (BOOT_SIZE, ROOT_SIZE…) │ ├── packages/ ArchR-specific packages on top of upstream │ │ ├── archr/ scripts/runemu.sh, profile.d/, autostart/ │ │ ├── linux-drivers/mali-bifrost/ libmali kernel patch │ │ ├── graphics/gpudriver/ panfrost ↔ libmali switch │ │ ├── apps/portmaster/ PortMaster + scripts/start_portmaster.sh │ │ ├── emulators/standalone/ PPSSPP/Flycast/melonDS/DraStic per-device cfg │ │ └── ui/emulationstation/ ES patches │ └── options project-level vars ├── distributions/ArchR/ │ ├── options distro-level vars (BOOT_SIZE=272, ROOT_SIZE=4608…) │ └── config/ ├── config/ │ ├── archr-dts/ 43 motherboard revisions × {original/clone/soysauce} │ └── mipi-generator/ generator.sh + archr-dtbo.py (DTS → DTBO) ├── scripts/ │ ├── build per-package build driver │ ├── extract tarball extract │ ├── image wraps mkimage │ └── mkimage partition layout, FAT32 BOOT, ext4 ROOT, ext4 STORAGE ├── tools/ │ ├── bench.sh on-device benchmark (PSI, freq, temp) │ ├── audit-build.sh on-device build audit │ └── ... └── docs/ internal design docs / decision logs ``` *** ## Common edits [#common-edits] **Tune a kernel option:** ``` projects/ArchR/devices/RK3326/linux/linux.aarch64.conf ``` Then `rm -rf build.ArchR-RK3326.aarch64/.stamps/linux && make docker-RK3326`. **Tune CPU OPP / regulator:** ``` projects/ArchR/devices/RK3326/patches/linux/000-rk3326-dts.patch projects/ArchR/devices/RK3326/linux/dts/rockchip/rk3326-gameconsole-r3xs.dtsi ``` **Add an emulator default:** ``` projects/ArchR/packages/virtual/emulators/package.mk projects/ArchR/packages/emulators/standalone//scripts/start_.sh ``` **Edit the autostart trace path:** ``` projects/ArchR/packages/sysutils/autostart/sources/autostart ``` *** ## Validating a build [#validating-a-build] `tools/audit-build.sh` runs on the device and verifies: ```bash bash /flash/audit-build.sh ``` …that no kernel debug flags slipped through, that all libretro cores are stripped, that no sanitizer symbols leaked into binaries. Use it in CI. `tools/bench.sh` collects PSI / freq / temp / vmstat / dmesg for a 60 s window: ```bash bash /flash/bench.sh psp 60 # launch a PSP game in a separate terminal / TTY # results land in /storage/bench-results/-psp/ ``` # Cloud sync Three approaches to keeping your handheld's `/storage` in sync with another machine. Pick whichever fits your setup. *** ## Syncthing (peer-to-peer) [#syncthing-peer-to-peer] Best when you have a PC or another handheld and want continuous, automatic sync without involving a cloud account. ### Setup on the handheld [#setup-on-the-handheld] 1. ES → **System Settings** → **Network** → **Syncthing** → ON. 2. Note the device IP (visible in **System Information**). 3. Note the `root` password under **Security** (default `archr`, change it). ### Setup on your PC [#setup-on-your-pc] Install Syncthing from your distro's package manager or [syncthing.net](https://syncthing.net). ### Connect them [#connect-them] Browse to `http://:8384` from your PC. Login: user `root`, password whatever you set. In the handheld's web UI: * **Add Remote Device** → paste the PC's Device ID (visible in your PC Syncthing's UI under Actions → Show ID). Same-LAN devices auto-discover, otherwise paste manually. * **Add Folder** → label it (e.g. `roms`), path `/storage/roms`. In the **Sharing** tab, tick the PC. On the PC, accept the incoming device + folder. Pick a local folder to mirror to. Done, sync starts. ### What to sync [#what-to-sync] | Folder | Content | Sync? | | ---------------------- | --------------------- | -------------------- | | `/storage/roms` | Your games | sometimes, large | | `/storage/saves` | RetroArch SRAM saves | yes, small, valuable | | `/storage/states` | RetroArch save-states | tricky, see below | | `/storage/screenshots` | Screenshots | yes | | `/storage/.config` | All app configs | usually no | **RetroArch save-states caveat:** states are tied to the **exact core build**. If your PC RetroArch and the handheld run different libretro core versions, states can corrupt. Saves (SRAM) are core-version-agnostic, states aren't. Sync `saves/` always; sync `states/` only if you keep core versions identical. ArchR has Syncthing paused during gameplay so it never competes with the emulator for CPU. It resumes on exit. *** ## rclone (cloud storage) [#rclone-cloud-storage] For Google Drive / OneDrive / Dropbox / S3 / etc. ArchR ships rclone preconfigured with a wrapper at ES → **Tools** → **Cloud Backup** / **Cloud Restore**. ### Setup [#setup] ### Enable SSH [#enable-ssh] [As in the SSH guide](/docs/ssh). ### Configure your cloud provider [#configure-your-cloud-provider] ```bash ssh root@archr.local rclone config ``` Follow the prompts; refer to [rclone provider docs](https://rclone.org/remote_setup/) for OAuth headless setup if needed. ### Edit `cloud_sync.conf` [#edit-cloud_syncconf] Default at `/storage/.config/cloud_sync.conf`. Most useful keys: | Key | Default | Meaning | | --------------- | ---------------------- | --------------------------------------------------------------------- | | `BACKUPPATH` | `/storage/roms` | what to back up | | `RESTOREPATH` | `/storage/roms` | where to restore (different from `BACKUPPATH` to test restore safely) | | `BACKUPFOLDER` | `/storage/roms/backup` | local backup files | | `SYNCPATH` | `/GAMES` | path on the cloud remote | | `BACKUPMETHOD` | `sync` | `sync` mirrors / `copy` adds without deleting | | `RESTOREMETHOD` | `copy` | `copy` preserves existing local / `sync` overwrites | | `RCLONEOPTS` | filters & verbosity | per [rclone flags](https://rclone.org/flags/) | ### Run a backup [#run-a-backup] ES → **Tools** → **Cloud Backup**. Same menu has **Cloud Restore**. ### Logs [#logs] ```bash tail -f /var/log/cloud_sync.log ``` ### Filter rules [#filter-rules] `/storage/.config/cloud_sync-rules.txt` controls what's included/excluded. The `.defaults` version next to it is the upstream template, copy keys from there if you want to restore defaults. *** ## NFS (LAN file server) [#nfs-lan-file-server] If you have a NAS or a Linux PC running an NFS server, you can mount the NFS share over the games folder. ROMs live on the server; saves stay on the handheld. ### Drop the mount file [#drop-the-mount-file] ``` /storage/.nfs-mount ``` Single line: ``` NFS_PATH=192.168.1.10:/srv/roms ``` ### Mount [#mount] ES → **Tools** → **Mount NFS**. The remote path is mounted at `/storage/games-external` and an overlay merge is created at `/storage/roms`, saves go to the local upper layer (`/storage/games-internal`), game reads come from the NFS share. This is nice for big collections, store the 200 GB MAME romset on a NAS, have the handheld just see them on `/storage/roms/arcade`. ### Caveats [#caveats] * NFS over WiFi is sensitive to packet loss; expect occasional latency spikes when the WiFi signal dips. * ROMs that need fast random reads (PSP ISO seek-heavy scenes) feel better when the file is local, copy your most-played titles into `/storage/games-internal/roms/` to get the local-disk path of the overlay. * The mount drops on `power-off` and reconnects on next boot if the file is still present. # Collections EmulationStation collections let you create cross-system game lists, every RPG you've ever owned, your "now playing" backlog, "kid-friendly", "speedruns to try". They live alongside the per-system lists in the home view. All settings live under ES → **Game Collection Settings**. *** ## Three flavours [#three-flavours] ### Automated [#automated] ES has built-ins for **Favorites**, **Last Played**, and **All Games**. Toggle individually under **Automated Game Collections**. To add a game to **Favorites**: highlight it → press `Y`. Same `Y` again to remove. ### Editable (curated) [#editable-curated] Pick a name, then walk your library and tag games one by one. ES → **Game Collection Settings** → **Create New Custom Collection** → name it. Browse to a game → press `Y` → in the "Add to collection" picker, select the new collection. Useful for thematic lists: `Final Fantasy series`, `Top 50 of all time`, `Kids`. ### Dynamic (filter-based) [#dynamic-filter-based] Create a collection driven by metadata filters, every game whose Genre is "RPG", every game from 1992-1995, every game with rating ≥ 4 stars. New games matching the filter automatically appear. ES → **Game Collection Settings** → **Create New Custom Collection from Theme** → choose **Dynamic** → set filters. The filters come from the metadata you scraped, see [Scraping](/docs/scraping). Without scraped metadata, dynamic collections have nothing to match. *** ## Now Playing, the backlog tool [#now-playing-the-backlog-tool] A built-in dynamic collection labelled **Now Playing**. Curates the handful of games you're actively in the middle of, separate from the 800-game library. Enable it: ES → **Game Collection Settings** → **Create New Custom Collection from Theme** (with Art Book Next active) → tick **Now Playing**. Add a game: highlight → `Y` → **Add to Collection** → **Now Playing**. Boot directly into it: ES → **Game Collection Settings** → **Start on System** → `Now Playing`, plus enable **Start on Gamelist**. Now ArchR boots straight into your active backlog instead of the system list. *** ## Hide systems you don't use [#hide-systems-you-dont-use] Same menu, **Systems Displayed**: untick systems you don't have ROMs for to declutter the home view. The systems still exist (ROMs aren't deleted), they just don't show up. This is also how to hide the empty systems for stuff you've never bought ROMs for. *** ## Where collections live [#where-collections-live] `/storage/.config/emulationstation/collections/` One `.cfg` per collection. The format is plain text, one `:` per line for editable collections, an XML `` block for dynamic. You can edit them on a PC if you want, but the in-ES UI is usually friendlier. # Controls ## Standard hotkeys [#standard-hotkeys] The R36S has a `MODE` (or `Function`) button. Combined with another button it acts as a hotkey. ``` VOL+ / VOL- Volume (audio DAC, 2% / step) MODE + VOL+ / VOL- Brightness (3% / step, persists across reboots) MODE + B Quit emulator (returns to ES) MODE + X Open RetroArch quick menu (libretro cores only) MODE + R1 Save state MODE + L1 Load state MODE + R2 Next save-state slot MODE + L2 Previous save-state slot MODE + Y Take screenshot (saved to /storage/screenshots/) MODE + START Reset emulator MODE + SELECT Toggle FPS/perf overlay ``` Long-press `POWER` for shutdown menu (suspend / restart / power-off). *** ## Joypad variants [#joypad-variants] R36S clones use one of two button-mapping conventions. ArchR auto-detects the right one based on the **board profile** you flashed, but you can override it. ### Auto (default) [#auto-default] The Flasher reads the motherboard revision and picks the right variant: * **K36 layout**: most R36S clones, including K36, R33S, EE Clone * **MyMini layout**: soysauce / Y3506 hardware (e.g. mid-2024 batches) ### Manual override [#manual-override] In the [ArchR Flasher](/docs/installation), the **Joypad variant** dropdown forces a specific layout. Useful if Auto picks wrong (rare). Choices: | Variant | Effect on physical buttons | | ------- | ----------------------------------------------------- | | Auto | Detect from motherboard revision | | K36 | Standard Nintendo / Xbox layout | | MyMini | Mirrored layout (B↔A and X↔Y swapped relative to K36) | ### Switching after flash [#switching-after-flash] Edit `/storage/.config/system.cfg` over [SSH](/docs/ssh) and set: ```ini joypad.variant=K36 # or MyMini ``` Reboot. *** ## JP layout [#jp-layout] Toggle in the Flasher under **JP layout**: swaps A↔B and X↔Y for Japanese-style button labels (matches Famicom / SFC physical positions). This is purely a controller remap, ROMs don't need to be Japanese. *** ## RetroArch in-game [#retroarch-in-game] Press `MODE + X` for the Quick Menu. Common sub-menus: ``` Resume Back to game State Slot Pick save slot 0-9 Save State / Load State In-place save (faster than ROM save) Take Screenshot PNG to /storage/screenshots/ Options Per-core knobs (resolution, frameskip, BIOS) Controls Remap on the fly Cheats Cheat list (if .cht for the ROM) Disk Control Multi-disc PSX swap Information Core / system / authors ``` `MODE + B` exits without going through the menu. *** ## Standalone emulator hotkeys [#standalone-emulator-hotkeys] Each standalone has its own internal menu: | Emulator | Menu hotkey | | --------------------- | -------------- | | PPSSPP | `MODE` (alone) | | Flycast | `MODE` (alone) | | Dolphin (not RK3326) | `MODE + X` | | DraStic (NDS) | `MODE` (alone) | | Yabasanshiro (Saturn) | `MODE + X` | | melonDS | `MODE` (alone) | `MODE + B` exits all of them. *** ## Custom remaps [#custom-remaps] Per-system: ES menu → Game Settings → Default Controllers (per system). Saved to `/storage/.config/retroarch/config/.cfg`. Per-game (single ROM): launch the game → `MODE + X` → Controls → Save Game Remap File. Custom hotkey override: edit `/storage/.config/retroarch/retroarch.cfg`, look for `input_hotkey_*` keys. *** ## PortMaster ports [#portmaster-ports] Ports use a separate input layer (`gptokeyb`) that translates joypad input to keyboard events for the original PC binary. Default mapping is in `/storage/roms/ports/PortMaster/control.txt`, most ports override it with their own config. In-game hotkey to exit a port: usually `MODE + START + SELECT` or just `MODE + START`. Each port documents its own combo. # FAQ The R36S (genuine + all known clones) and the broader RK3326 family, Odroid Go Advance / Super, Anbernic RG351V/M, GameForce Chi, MagicX XU10/XU-Mini-M, BatLexp G350, Powkiddy RGB10/RGB10X/RGB20S. 43 motherboard revisions, both image variants combined. If your device has a Rockchip RK3326 SoC and a supported MIPI panel, it almost certainly works. * **Open-source GPU stack**: Panfrost via Mesa 26.0.5 by default; libmali still available as opt-in. * **Built on top of ROCKNIX** with an Arch Linux build environment, same release pipeline, different userspace conventions. * **Cleaner runtime**: `runemu.sh` is gated by loglevel (no `set -x` permanente), Saturn/Wii/GC hidden on A35, gptokeyb kill is defensive, etc. * **Two-image build with autodetect**: same image works on every motherboard of a variant; SARADC reads the board ID at boot. * **CPU turbo is opt-in via UI**: no overclock until you toggle it. Battery-friendly default. * **Reproducible**: `make docker-RK3326` matches the GitHub release byte-for-byte. * **Pre-installed PortMaster + Python lzma + udev /dev/uinput rule**: ports work without pre-flight. Yes within a *variant*, original-image SDs work in any original-variant board, clone-image SDs in any clone-variant board. The board DTB is selected at boot by SARADC. Across variants (original ↔ clone) the U-Boot differs, so an original SD won't boot a clone or vice-versa. Re-flash with the right variant. Because 1.4 GHz is a better default, it's stable on every chip we've tested at the lower 1.35 V, runs cooler, and gives noticeably longer battery life. Going to 1.5 GHz needs 1.4 V on most chips, raises temperature, and only helps for the really CPU-hungry games (PSP heavy, Dreamcast heavy, box86 ports). Toggle "Enable CPU Overclock" in System Settings → Performance when you actually need it. See [Overclock](/docs/overclock). A Mesa option that moves GL command processing to a separate thread. Helps when an app issues lots of small GL calls and has its own render thread, Flycast and PPSSPP standalones tick all those boxes. Hurts when an app uses fences/glReadPixels heavily, most libretro 2D cores. ArchR uses a whitelist (default off, opt-in per emulator) to avoid regressing 2D cores. Full matrix in `docs/mesa-glthread-matrix.md` in the source tree. * `/storage` is the writable partition (ROMS in the Flasher, the third partition), your ROMs, save-states, configs, screenshots, BIOS files. ext4. Use the full SD capacity after first-boot resize. * `/` is read-only system. ROOT partition, ext4. \~4.6 GB used, the rest is reserved for system updates / snapshots. * `/flash` is the BOOT partition, kernel, DTBs, panel overlays, U-Boot, fs-resize log. FAT32, 272 MB. Read-only at runtime; writable for flasher updates. Re-flash. The new image's first boot sees that `/storage` already has a `.config` and skips re-creation, preserving your saves and ROMs. Even safer: back up `/storage/roms/`, `/storage/.config/`, `/storage/saves/` over [SSH](/docs/ssh) before flashing, then restore after, same effect with no risk. No. ArchR is built LibreELEC-style, no package manager at runtime, no rolling updates. The "Arch Linux" in the name refers to the build environment, not the runtime. Updates ship as full image releases. Normal, the first boot does a partition resize. About 30 seconds total, then automatic reboot, then ES. Subsequent boots are 19 seconds. If you hit a hang, after the eventual successful boot run: ```bash cat /storage/.boot_last_hang ``` That file only exists if the previous boot hung, its contents name the script that hung. Open an issue with that name and the dmesg snippet from `/var/log/boot.log`. (In v22+ a 60 s timeout on `archr-autostart.service` and a 10 s timeout on `drm_tool list` ensure the UI eventually comes up even if a script hangs.) Because shipping a unique-per-device password requires either e-fuse provisioning at the factory (we don't have a factory) or a setup wizard on first boot (UX cost). Published default + clear instruction to change it on first login is the pragmatic compromise. Change it with `passwd` after first SSH login. Or use key-based auth and disable password login (instructions in [SSH](/docs/ssh)). RetroArch + libretro core suite (\~250 cores total) covering NES/SNES/GB family/MD/MS/PSX/N64/Arcade/etc. plus standalones for the heavier systems: PPSSPP, Flycast, Mupen64Plus, DraStic, melonDS, Yabasanshiro, DuckStation. PortMaster is pre-installed for native PC ports. GameCube/Wii/Wii U/PS2/Switch/3DS are deliberately not exposed on RK3326, the A35 doesn't run them at playable speeds. You picked the wrong panel overlay. Re-flash with the [ArchR Flasher](/docs/installation) and double-check the motherboard revision printed on the back of your device. The Flasher's panel dropdown matches the silkscreen text exactly. If you can't open the device, try flashing with the most generic overlay for your variant first; the colors will be off but the screen should be readable enough to find the right one. The `gpudriver` script auto-falls back if the chosen driver doesn't bind, so you should always end up with *some* graphics. If somehow you didn't: 1. Power off, remove SD card, mount on a PC. 2. On the STORAGE partition, edit `/storage/.config/system.cfg` → set `gpu.driver=libmali`. 3. Reinsert and boot. (See [GPU driver](/docs/gpu-driver) for the full table.) See [Build from source](/docs/build). The repo is at [github.com/archr-linux/Arch-R](https://github.com/archr-linux/Arch-R). Issues and PRs welcome. # Features ## Hardware [#hardware] ``` SoC Rockchip RK3326 (4× Cortex-A35, in-order dual-issue) CPU 1416 MHz default · 1512 MHz with "Enable CPU Overclock" GPU Mali-G31 MP2 (Bifrost), pinned at 650 MHz during gameplay DRAM 1 GB DDR3L @ 924 MHz Display 640×480 MIPI DSI (43 panel revisions covered) Audio RK817 codec, speaker, headphone jack, USB audio, HDMI audio Network WiFi 802.11n + Bluetooth (RTL/AP6611S/etc.), auto-detect Battery 3200 mAh (rk817-battery + rk817-charger), LED warning at low Storage microSD (hot-swap supported, image works on any size 8 GB+) ``` *** ## Software stack [#software-stack] ``` Kernel 6.12 LTS (BSP fork, board auto-detect via SARADC) GPU driver Panfrost (default) or libmali (toggle in ES) ├─ Mesa 26.0.5 with +speed +lto, NEON-optimised └─ zlib-ng for SIMD inflate/deflate (ROM zip 10–30% faster) Init systemd 255 Frontend EmulationStation Backend RetroArch + 18 standalone emulators Shell bash + GNU coreutils (full PortMaster compatibility) ``` **CMA**: 96 MB (raised from 64 MB after audit found V4L2/Mali allocations were tight in idle). **ZRAM**: 512 MB lzo-rle (chosen over zstd because Cortex-A35 in-order runs lzo-rle 3–4× faster). *** ## Pre-installed emulators [#pre-installed-emulators] ``` SNES, NES, GB/GBC/GBA, NDS, MD, MS, GG, PCE, NeoGeo, Atari (2600/5200/7800/Lynx/Jaguar), WonderSwan, PC-FX, ColecoVision, Intellivision, Vectrex, ScummVM, DOS PSX pcsx_rearmed (default), beetle_psx_hw, swanstation, duckstation (software) N64 Mupen64Plus standalone (GLideN64 performance / Rice fallback) PSP PPSSPP standalone (frameskip 3, FPS cap 30, GLTHREAD on) Dreamcast Flycast standalone (240 res, threaded rendering, GLTHREAD on) Saturn yabasanshiro standalone (experimental on A35) Arcade MAME 2003+, FBNeo, FBA-CPS1/CPS2/Neogeo NDS DraStic (default), melonDS as fallback ``` **Not included on RK3326**: GameCube, Wii, Wii U: Cortex-A35 doesn't run them at usable framerates, so they're hidden from the menu. *** ## Display panels [#display-panels] 43 pre-generated MIPI panel overlays: * **15 original** (R36S V20–V22 board revisions, OGS variants) * **18 clone** (K36, R33S, RX6S, R36 Max, multiple panel families) * **10 soysauce** (Y3506-based hardware) Overlay names mirror the motherboard revision exactly: ``` R36S-V21_2024-12-18_2551.dtbo G80CA-MB_V1.3-20251212_Panel_8.dtbo RX6S-2024_05_15-Panel_5.dtbo ``` The panel overlay is selected by the [ArchR Flasher](/docs/installation) at write time. The same image works on every board of a variant; only the overlay differs per panel. *** ## Network features [#network-features] | Feature | Service | Default | | ------------------------- | -------------------------------- | ------------------------- | | WiFi | iwd + connman | auto-on if config present | | Bluetooth audio + gamepad | bluez | enabled | | Local play | RetroArch netplay | UDP discovery | | Remote play | Tailscale / ZeroTier / WireGuard | opt-in via UI | | Sync ROMs/saves | Syncthing | opt-in | | Web server | simple-http-server | opt-in | | Samba (file share) | smbd + nmbd + wsdd | opt-in | | SSH | sshd | opt-in (ES toggle) | All "opt-in" services are paused automatically during gameplay so they don't compete with the emulator for CPU cycles. *** ## Boot timeline [#boot-timeline] ``` 0.0 s U-Boot start 0.7 s Initramfs splash + DTB selection ~7 s Kernel hand-off ~9 s systemd target ~12 s archr-autostart (quirks + governor + display) ~17 s EmulationStation ~19 s Ready to play ``` First boot adds \~10 s for the partition resize and creates ROM directories, only on the very first power-on after flashing. *** ## What's tuned for the RK3326 [#whats-tuned-for-the-rk3326] Highlights of the RK3326-specific work (full list in [Technical](/docs/technical)): * GPU OPP 650 MHz turbo at 1.150 V (same voltage as 600 MHz, avoided the original BSP 1.175 V which was unstable on some chips) * CPU OPP 1512 MHz at 1.400 V with `vdd_arm` regulator-max bumped to 1.45 V (RK817 DCDC\_REG2 spec is 1.5 V max) * `mali_kbase` PM patch, eliminates the regulator/clock unbalance warnings that were causing audio crackle in Mario 64 * BOOT partition 272 MB FAT32 with 4 KB clusters (above 65525 spec minimum + boot sector & backup auto-synced) * `rq_affinity=2` on microSD I/O queue (irq landing on the issuing core) * BFQ scheduler with `low_latency=1` and `read_ahead_kb=2048` # GPU driver Two GPU drivers are shipped. ArchR boots **Panfrost (Mesa)** by default, with **libmali** available as opt-in. *** ## Quick reference [#quick-reference] | | Panfrost | libmali | | ------------------ | ---------------------- | ---------------------- | | Source | Open-source (Mesa) | Proprietary (ARM blob) | | API | OpenGL 3.1, GLES 3.1 | GLES 3.1 only | | Vulkan | PanVK (experimental) | Yes (Mali-G31 only) | | Default | ✅ | , | | Older RetroArch | ⚠️ may need newer Mesa | ✅ closer to stock | | Active development | ✅ Mesa weekly | Frozen at vendor BSP | *** ## Switch driver [#switch-driver] EmulationStation → **System Settings** → **Graphics** → **GPU driver** → choose `panfrost` or `libmali`. Reboot. The setting persists in `/storage/.config/system.cfg` (`gpu.driver=panfrost|libmali`). On boot, the `gpudriver --start` script: 1. Loads the chosen kernel module (`panfrost` or `mali_kbase`) 2. Bind-mounts the right user-space libraries over Mesa 3. Masks the wrong Vulkan ICD JSON If the chosen driver fails to bind to the GPU node (e.g. mali\_kbase reports a regulator error), the script automatically falls back to the other one so you get a graphical system either way. *** ## Why Panfrost is the default [#why-panfrost-is-the-default] * **Mesa 26.0.5** has solid Mali-G31 (Bifrost v9) support, no missing extensions for any libretro core we ship. * **MESA\_GLTHREAD** matrix tuned per-emulator (Flycast, PPSSPP) gives extra throughput. * **Active upstream**: bug fixes land continuously; frozen vendor blob means stuck on whatever 2019 quirk shipped. * **Same API surface** as libmali for everything except Vulkan. *** ## When to try libmali [#when-to-try-libmali] Specific edge cases reported in community testing: * A specific emulator / game shows render glitches under Panfrost * You want to try Vulkan-via-mali (PanVK at G31 isn't conformant) * libretro shader pack X works one way but not the other The runtime fallback means switching is low-risk, if the chosen driver fails to attach to the GPU at boot, the system swaps to the alternate automatically. *** ## Mesa quirks applied (Panfrost path) [#mesa-quirks-applied-panfrost-path] For RK3326 / Mali-G31, ArchR exports these globally for emulators: ```bash PAN_MESA_DEBUG=forcepack # forces format-pack on G31 # (despite the name, NOT a debug flag) MESA_NO_ERROR=1 # skip GL error tracking MESA_SHADER_CACHE_DIR=/storage/.cache/mesa_shader_cache MESA_SHADER_CACHE_MAX_SIZE=128MB # shader cache survives reboots ``` Per-emulator (only where benefit was confirmed): ```bash MESA_GLTHREAD=true # Flycast, PPSSPP standalone, see /docs/mesa-glthread-matrix ``` *** ## Vulkan [#vulkan] PanVK on Mali-G31 (Bifrost v9) is **not conformant**: the upstream Mesa team only ships PanVK officially for Mali-G610 (Valhall). ArchR disables PanVK at build time on RK3326 so emulators that probe for Vulkan don't try and fail. If a future emulator update needs Vulkan and we've validated PanVK against it, the toggle would land in the same Graphics menu. *** ## Troubleshooting [#troubleshooting] **Black screen after switching to Panfrost.** The DTBO overlay for your panel might not have loaded. Check with `cat /sys/class/drm/card0/connector/*/status`. Switch back to libmali via the recovery path: 1. Power off, remove the SD card 2. On a PC, mount the BOOT partition 3. Edit `/storage/.config/system.cfg` (on the storage partition), set `gpu.driver=libmali` 4. Re-insert and boot **`gpudriver` warns "did not bind to GPU".** The auto-fallback already triggered, your effective driver is the *other* one. Check `journalctl -t gpudriver` (over [SSH](/docs/ssh)) for the exact message. **Vulkan probe spam in logs.** Some PortMaster ports try Vulkan first. Harmless, they fall back to GLES. # HDMI output The R36S ships with a Mini-HDMI port. Plug it into a TV or monitor; ArchR auto-detects and mirrors. The default behaviour is good in 90% of cases, this page covers the 10%. *** ## Plug timing [#plug-timing] **Connect HDMI before powering on**, when possible. Some R36S clones detect HDMI only at boot, hot-plug works partially (audio routes correctly, video may not). If hot-plug doesn't pick up: 1. Plug the cable 2. Power off the device fully 3. Power back on with the cable connected *** ## Resolution [#resolution] Default is to mirror the device panel resolution (640×480 on R36S) and let the TV upscale. If your TV handles non-standard 480p poorly, force a friendlier mode: ### Find your TV's preferred mode [#find-your-tvs-preferred-mode] Almost all modern TVs natively accept 1280×720\@60 or 1920×1080\@60. Pick one. ### Drop an autostart script [#drop-an-autostart-script] Over [SSH](/docs/ssh): ```bash mkdir -p /storage/.config/autostart nano /storage/.config/autostart/090-sway-hdmi-resolution ``` Content: ```bash #!/bin/bash echo "output HDMI-A-1 resolution 1280x720" >> /storage/.config/sway/config ``` (Replace `1280x720` with `1920x1080` for full HD.) ### Make it executable [#make-it-executable] ```bash chmod +x /storage/.config/autostart/090-sway-hdmi-resolution ``` ### Reboot [#reboot] `sudo reboot`. Sway picks up the new config and the HDMI output runs at the requested resolution. ### Mirroring exactly [#mirroring-exactly] If the TV is showing the device output offset / cropped, force the position: ```bash echo "output HDMI-A-1 resolution 1280x720 position 0 0" >> /storage/.config/sway/config ``` *** ## Audio [#audio] ArchR automatically routes audio through HDMI when an HDMI cable is detected. To override (e.g. you want speakers on the device while the TV displays): ```bash amixer set 'Playback Path' SPK ``` For stickier preference, edit `/storage/.config/system.cfg`: ```ini audio.path=SPK ``` …and add a quirk in `/storage/.config/profile.d/050-audio_path` if needed. *** ## Why HDMI sometimes shows nothing on a clone [#why-hdmi-sometimes-shows-nothing-on-a-clone] Clone devices ship with a different SPL/U-Boot than the original R36S. Some clones don't initialize HDMI at U-Boot stage at all, meaning you see no kernel messages or splash on the TV. The kernel still drives HDMI fine after boot, but if your TV insists on a signal during U-Boot to lock on, it may not display anything until ArchR is fully up. There's no fix at the bootloader level today; the workaround is to power on with the device's internal panel as primary, then once ArchR is loaded, HDMI mirrors. *** ## Disabling the internal panel when on HDMI [#disabling-the-internal-panel-when-on-hdmi] Some users prefer the device panel off when docked to a TV. Add to the same autostart script: ```bash #!/bin/bash echo "output HDMI-A-1 resolution 1280x720" >> /storage/.config/sway/config echo "output DSI-1 disable" >> /storage/.config/sway/config ``` Reboot. Now the device's panel is dark, all output is on HDMI. To revert: delete the autostart file, reboot. # Documentation Custom Linux distribution for the **R36S** and its variants, built on top of [ROCKNIX](https://github.com/ROCKNIX/distribution) with an Arch Linux build environment. Kernel 6.12 LTS, Mesa 26 Panfrost (open-source GPU), 43 pre-generated MIPI panel overlays covering every known motherboard revision. Two images, both with hardware auto-detection: * **Original**: for genuine R36S, R33S, Odroid Go family, Anbernic RG351V/M, GameForce Chi, MagicX XU10 * **Clone**: for K36 clones, EE Clone, Powkiddy RGB10/RGB10X/RGB20S, MagicX XU-Mini-M, BatLexp G350 *** ## What's different [#whats-different] * **Open-source GPU driver** (Panfrost via Mesa 26.0.5), no proprietary blobs. `libmali` is also available as opt-in. * **Two-image build with autodetect**: same image boots on every motherboard of a variant. SARADC reads the board ID at boot. * **CPU overclock toggle**: switch in the ES menu lifts the cap from 1.4 GHz to 1.5 GHz when you need it. * **Reproducible builds**: Docker-based, no hidden steps. `make docker-RK3326` produces both image variants. * **Network play out of the box**: Syncthing, Tailscale, ZeroTier, RetroAchievements, scraping. * **PortMaster pre-installed**: runs PC ports (Half-Life, Diablo II, Doom 3, Quake) on the device. *** ## Quick links [#quick-links] * **[Releases →](https://github.com/archr-linux/Arch-R/releases)** download the latest image * **[ArchR Flasher →](https://github.com/archr-linux/archr-flasher/releases)** GUI installer (Linux, macOS, Windows) * **[Source →](https://github.com/archr-linux/Arch-R)** kernel + rootfs + image build * **[Issues →](https://github.com/archr-linux/Arch-R/issues)** bug reports # Installation You only need a microSD card (8 GB+, 16 GB+ recommended) and 5 minutes. The recommended path is the **ArchR Flasher**: it picks the right image variant for your board, applies the correct panel overlay automatically, and verifies the write. *** ## Option 1, ArchR Flasher (recommended) [#option-1-archr-flasher-recommended] The Flasher is a small desktop app (Tauri) that downloads the latest release, picks the correct panel overlay for your motherboard, writes the SD card, and verifies the write. Available for Linux, macOS and Windows. ### Download the Flasher [#download-the-flasher] Get the latest release from [archr-flasher releases](https://github.com/archr-linux/archr-flasher/releases). * **Linux**: `.AppImage` (no install) or `.deb` for Debian/Ubuntu/derivatives * **macOS**: `.dmg` * **Windows**: `.msi` installer ### Plug your microSD [#plug-your-microsd] 8 GB or larger. The Flasher will erase **everything** on the card before writing, back up anything you need first. ### Pick the board / panel [#pick-the-board--panel] Open the Flasher and: 1. Choose your **board variant**: Original or Clone. If unsure, check the back of your device or the [boards table](/docs/features#display-panels) on the Features page. 2. Choose your **motherboard revision** from the dropdown (e.g. `R36S-V21_2024-12-18_2551`, `G80CA-MB_V1.3-20251212_Panel_8`). The Flasher reads the silkscreen text printed on the back of the motherboard. 3. Optional toggles: * **Joypad variant**: Auto · K36 · MyMini (only matters for clones) * **JP layout**: Japanese button labels (A↔B, X↔Y swap) * **SRs (force simple audio)**: for boards with non-standard amplifier wiring * **Dno (skip vendor mode)**: disables the boot-time vendor screen on some boards ### Write [#write] Click **Flash**. The Flasher downloads the image (or uses the cache), wipes the SD, writes, then reads back to verify the SHA256. About 3 minutes total on a class 10 card. ### Boot [#boot] Insert the SD into the device, power on. First boot does an automatic partition resize and reboots once, total \~30 s. After the second boot you land in EmulationStation. *** ## Option 2, Manual `dd` [#option-2-manual-dd] If you prefer the command line or are on Windows (use Rufus, Etcher, or the Raspberry Pi Imager equivalent). ### Download an image [#download-an-image] From [Arch-R releases](https://github.com/archr-linux/Arch-R/releases) grab one of: * `ArchR-R36S.aarch64-DATE-original.img.gz`, for genuine R36S * `ArchR-R36S.aarch64-DATE-clone.img.gz`, for K36 / clone hardware ### Decompress [#decompress] ```bash gunzip ArchR-R36S.aarch64-*.img.gz ``` ### Identify the SD device [#identify-the-sd-device] ```bash lsblk # look for your card, usually /dev/sdX or /dev/mmcblkX ``` Double-check the device path. Writing to the wrong disk will erase it. ### Write [#write-1] ```bash sudo dd if=ArchR-R36S.aarch64-*.img of=/dev/sdX bs=4M conv=fsync status=progress sync ``` ### Apply panel overlay (manual path only) [#apply-panel-overlay-manual-path-only] The default image boots a generic panel overlay. If your screen is blank or distorted, mount the BOOT partition and copy the correct overlay: ```bash sudo mount /dev/sdX1 /mnt ls /mnt/overlays/ # see all 43 available overlays sudo cp /mnt/overlays/.dtbo /mnt/overlays/mipi-panel.dtbo sudo umount /mnt ``` Find your motherboard revision printed on the back of the board. ### Boot [#boot-1] Insert and power on. First boot resizes and reboots; second boot reaches ES. *** ## First boot [#first-boot] After flashing, the **first** power-on does the following automatically and then reboots: ``` 1. parted resizepart → /storage uses the full SD 2. e2fsck + resize2fs → ext4 filesystem grows 3. tune2fs -U random → fresh UUID 4. fsck.fat -a -w → BOOT FAT primário/backup sincronizados 5. reboot ``` The resize log lives at `/flash/fs-resize.log` if you ever need to inspect it. The **second** boot creates ROM directories (one per system) and lands in EmulationStation. From there you can [add ROMs](/docs/roms) and [enable SSH](/docs/ssh). *** ## Variants explained [#variants-explained] | Variant | U-Boot | Boot logo | Hardware | | ---------- | ----------------- | --------- | --------------------------------------------------------------------------- | | `original` | BSP Rockchip | Yes | R36S, R33S, Odroid Go family, Anbernic RG351, GameForce Chi, MagicX XU10 | | `clone` | Mainline v2025.10 | No | K36, EE Clone, Powkiddy RGB10/RGB10X/RGB20S, MagicX XU-Mini-M, BatLexp G350 | The two images differ **only** in U-Boot and panel overlay set. Kernel, rootfs, RetroArch, EmulationStation are identical. *** ## Troubleshooting [#troubleshooting] **Black screen after the boot logo.** Wrong panel overlay. Re-flash with the correct motherboard revision selected, or copy the right `.dtbo` to `/flash/overlays/mipi-panel.dtbo` from another machine. **Boot loops or hangs after first reboot.** Capture `cat /storage/.boot_last_hang` after the eventual successful boot, it names the script that hung. See [Troubleshooting](/docs/troubleshooting). **"BOOT FAILED" on a clone device with the original image.** You picked the wrong variant. Use the clone image. **Flasher says "image checksum mismatch".** The download was corrupt. Click "Flash" again; the Flasher re-downloads and retries. # Netplay RetroArch netplay lets up to 4 players play the same libretro core in real-time. Works over your home WiFi (LAN mode) or directly between two handhelds (device-to-device mode). Both devices must run **the same ArchR version** with **the same core**. *** ## Pre-flight [#pre-flight] ### Same version on both sides [#same-version-on-both-sides] ES → **System Information**: confirm matching ArchR version on host and clients. ### Enable netplay globally [#enable-netplay-globally] ES → **Game Settings** → **Netplay Settings** → **NetPlay** → ON. Also tap **Index Games** so RetroArch knows which ROMs you have. ### Same core, same ROM [#same-core-same-rom] Each player needs the same core (e.g. `snes9x_libretro`) and the same ROM file. RetroArch hashes the ROM, if hashes differ, connection fails. *** ## Option 1, over your home WiFi (LAN) [#option-1-over-your-home-wifi-lan] ### Host [#host] ES → **Network Settings** → enable WiFi → disable **Local Play Mode** → set role **1 (Host)**. In ES, highlight the game, press `Y` → **Netplay Options** → **Host a Netplay Session**. RetroArch starts in host mode and waits for clients. ### Clients (up to 3) [#clients-up-to-3] ES → **Network Settings** → WiFi on → **Local Play Mode** OFF → role **2 / 3 / 4 (Client)**. In ES, same game, `Y` → **Netplay Options** → **Connect to a Netplay Session**. Pick the host from the discovered list. *** ## Option 2, device-to-device (no router) [#option-2-device-to-device-no-router] When you're away from your home WiFi (parks, road trips). One device acts as a hotspot, the other connects to it. ### Host [#host-1] ES → **Network Settings** → **Local Play Mode** → ON → role **1 (Host)**. ArchR creates a temporary WiFi access point on the host. Note the SSID + password it prints. ### Clients [#clients] ES → **Network Settings** → connect to the host's SSID → **Local Play Mode** ON → role **2-4**. Same game-launch flow as LAN play. Device-to-device mode disables internet, you can't scrape, RetroAchievements, or update while it's active. *** ## Game-side setup [#game-side-setup] Both players launch the **same** ROM. RetroArch's session menu shows handshake status and frame buffer alignment. If you see "checksum mismatch", your ROMs aren't byte-identical (different region / dump). For cores with rollback netcode (FBNeo, some 16-bit), RetroArch enables it automatically, input lag on a fast LAN feels close to local. For cores without rollback (most), play feels best when the host has the better connection, they run at near-zero lag, the client gets a few frames of buffered delay. *** ## Optimal host pick [#optimal-host-pick] If you have one wired (eth-via-USB-C) device and one on WiFi, host on the wired one. RetroArch picks the host's frame as canonical, wired hosts mean fewer rollbacks. If both are on WiFi, pick the device closer to the router as host. *** ## Compatible cores [#compatible-cores] Any libretro core with `netplay = supported` in its info file. Quick list of known-working on RK3326 ArchR: * snes9x2010, snes9x * gambatte (GB/GBC), mgba (GBA) * fceumm (NES), nestopia * genesis\_plus\_gx (MD/MS/GG) * fbneo (Arcade), rollback enabled * mednafen\_psx\_hw (PSX) Heavy 3D cores (mupen64plus\_next, ppsspp\_libretro) work technically but the A35 plus rendering load means stutter; not great experience. *** ## Troubleshooting [#troubleshooting] **"Checksum mismatch"**: different ROM dumps. Use the no-intro / Redump version on both sides. **"Connection timeout"**: host's IP is wrong (LAN mode) or hotspot isn't visible (device-to-device). In LAN mode, double-check both devices on the same subnet (`ip addr` over [SSH](/docs/ssh)). **Lag spikes**: WiFi airtime contention. Move closer to the router, or disable Bluetooth on both devices during the session. **Audio out of sync**: turn off the netplay audio buffer override. Quick Menu → Settings → Netplay → "Allow Asymmetric Frame Rate" → OFF. # CPU Overclock The R36S Cortex-A35 has a turbo OPP at 1512 MHz that sits dormant by default. Toggle it on for the games that need every Hz; leave it off for cooler / longer battery life on the rest. *** ## How to enable [#how-to-enable] EmulationStation → \*\* \*\* (Start) → **System Settings** → **Performance** → **Enable CPU Overclock** → ON. The toggle persists in `/storage/.config/system.cfg` (`enable.turbo-mode=1`) and is reapplied on every boot by `095-turbo-mode` autostart. To turn it off, flip the toggle. No reboot required for either direction. *** ## What changes when it's on [#what-changes-when-its-on] ``` OFF (default) ON ───────────────────────────────────────────────── cpufreq/boost 0 1 top freq idle 1416 MHz 1416 MHz top freq game 1416 MHz 1512 MHz voltage @ top 1.350 V 1.400 V ``` Idle/menu doesn't change, the governor decides on demand. Only when the CPU actually needs the top OPP does the extra \~7 % kick in. During gameplay, `runemu.sh` switches the governor to `performance` and pins `scaling_max_freq` to the highest value `scaling_available_frequencies` exposes (which depends on whether boost is on). *** ## Why this exists [#why-this-exists] Stock RK3326 BSP exposes 1512 MHz only as `turbo-mode` in the OPP table, the kernel hides it from `cpuinfo_max_freq` until `cpufreq/boost=1` is set. Plus, the `vdd_arm` regulator default in the upstream PX30 device tree is capped at 1.35 V, which is too low to drive 1.5 GHz reliably on most chips. ArchR raised that cap to 1.45 V (RK817 DCDC\_REG2 spec is 1.5 V), set the OPP to 1.4 V, and exposed the toggle. Some chips run 1.5 GHz at 1.35 V fine. Some need 1.4 V. None have been reported failing at 1.4 V yet. *** ## When to use it [#when-to-use-it] * **PSP** (PPSSPP standalone), God of War, Tekken 6, Daxter, small but consistent fps gain * **Dreamcast** (Flycast), Crazy Taxi, Sonic Adventure 2, frametime smoothing * **N64** (Mupen64Plus), heavy scenes (SM64 castle, Zelda OoT Hyrule Field) * **PSX** (Beetle PSX HW), texture-heavy 3D * **PortMaster**: box86/box64 ports that throttle at 1.4 GHz For 2D / handheld / 8-16 bit cores, leaving it OFF is the right choice, they don't need it and you save battery + heat. *** ## Verify it's working [#verify-its-working] Over [SSH](/docs/ssh): ```bash # OFF expected: 0 # ON expected: 1 cat /sys/devices/system/cpu/cpufreq/boost # OFF expected: 408000 816000 1008000 1416000 # ON expected: 408000 816000 1008000 1416000 (turbo OPP shows up separately) cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies # ON expected: 1512000 cat /sys/devices/system/cpu/cpufreq/policy0/scaling_boost_frequencies # Live freq during a game (run in a tight loop): watch -n 0.5 cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq ``` If you see `1512000` flowing through `scaling_cur_freq` while a heavy game runs, the overclock is active. *** ## Risks [#risks] * **Thermals**: 1.5 GHz at 1.4 V dissipates more heat than 1.4 GHz at 1.35 V. The R36S is passively cooled; in a hot room, in a sustained heavy load, it will throttle sooner. The kernel handles this, it just means you cap closer to 1.4 GHz once temperature hits the passive trip (\~70 °C). * **Battery**: \~10–15 % shorter battery life under heavy load with the overclock on. Negligible at idle / menu. * **Per-chip variation**: a small minority of chips report instability at 1.5 GHz / 1.4 V. If your device reboots while playing PSP/DC, turn the toggle off as a first diagnostic. *** ## Reverting / disabling permanently [#reverting--disabling-permanently] Toggle in ES → off. To force-off via shell (e.g. if ES is stuck): ```bash echo 0 | sudo tee /sys/devices/system/cpu/cpufreq/boost sudo sed -i 's/^enable.turbo-mode=1/enable.turbo-mode=0/' /storage/.config/system.cfg ``` # PortMaster [PortMaster](https://portmaster.games/) is a launcher / port repository that runs native PC binaries (Half-Life, Diablo II, Doom 3, Quake, Stardew Valley, Vvvvvv, …) directly on the handheld using `box86` / `box64` translation when needed. ArchR ships PortMaster pre-installed and pre-wired. *** ## What's included by default [#whats-included-by-default] ``` PortMaster GUI /storage/roms/ports/PortMaster/ gptokeyb joypad → keyboard event translator oga_controls extra device-specific input glue control.txt default key map SDL2 + lib32 / box86 32-bit x86 binaries via box86 SDL2 + box64 64-bit x86_64 binaries via box64 Python 3 + lzma used by the GUI and many ports ``` Verified at boot: ```bash python3 -c 'import lzma' # works ls -la /dev/uinput # crw-rw-rw- (udev rule 91-uinput.rules) ``` *** ## Run a port [#run-a-port] ### Open PortMaster [#open-portmaster] In EmulationStation, scroll to the **Ports** system. The first entry is "PortMaster", open it. The GUI launches. ### Browse / install [#browse--install] Press `B` on a port to install. Files land in `/storage/roms/ports//`. PortMaster downloads any data files (ROM-style assets aren't included, copy `wad`/`mpq`/etc. to the port folder per its README). ### Quit PortMaster GUI [#quit-portmaster-gui] `MODE + B` returns to ES. Installed ports appear as launchers (`.sh` files) in the **Ports** system list. ### Play a port [#play-a-port] Just like a ROM, select the launcher, press A. ArchR pauses background services (Syncthing, Tailscale, ZeroTier) for the duration, switches the CPU governor to `performance`, and pins the GPU at 650 MHz. ### Exit [#exit] Each port has its own hotkey, usually `MODE + START + SELECT`. Some use `MODE + START` alone. The combo is documented in the port's README inside `/storage/roms/ports//README.md`. *** ## Bring-your-own-data ports [#bring-your-own-data-ports] Some ports ship only the engine. They expect you to drop the original game data into the port's folder. Examples: | Port | What to drop | | -------------------------- | ------------------------------------------------------------------- | | **HalfLife** | `valve/` and `cstrike/` from your Steam install | | **Diablo II** | `*.mpq` files from a legitimate copy | | **Doom 3** | `pak*.pk4` from the retail install | | **Quake** | `id1/pak0.pak` and `pak1.pak` | | **Stardew Valley** | `Content/`, `StardewValley.dll`, etc. from your Steam Linux install | | **TombRaider** | `data/` from the original CD | | **DevilutionX (Diablo I)** | `diabdat.mpq` | PortMaster shows a per-port checklist of missing files in the GUI before letting you launch. *** ## Network play [#network-play] PortMaster ports that support multiplayer (Quake, Half-Life DM, etc.) can use: * **Tailscale** or **ZeroTier** for "internet LAN" play with friends, toggle on in ES → System Settings → Network. * **Local LAN** broadcast, works out of the box; both devices need to be on the same WiFi. ArchR keeps all VPN services paused until you launch a game that needs them, then resumes when you exit. *** ## Performance expectations [#performance-expectations] Cortex-A35 + Mali-G31 is a budget SoC. Realistic baseline: | Tier | Examples | Expectation | | ----------------------- | ----------------------------------------- | ------------------------------------------ | | Native ARM, lightweight | Vvvvvv, Stardew Valley, Cute Fame, OpenRA | full speed, full battery life | | box86 (32-bit x86) | Diablo II, Half-Life, Quake, Doom 3 | 25-60 fps depending on settings | | box64 (64-bit x86\_64) | Newer indie titles (Hollow Knight class) | playable but heavy, needs CPU Overclock on | | Heavy 3D / RT | DOOM Eternal, modern AAA | not viable | Toggle **Enable CPU Overclock** in ES (lifts cap from 1.4 → 1.5 GHz) for box86/64 ports that struggle. *** ## Troubleshooting [#troubleshooting] **Port hangs at black screen.** Joypad permissions probably wrong. Check `ls -la /dev/uinput` returns mode `666`. If not, the udev rule didn't apply, `sudo chmod 666 /dev/uinput` for now and report a bug. **`gptokeyb: Permission denied`.** Same as above. **Port asks for missing data files.** Drop them in `/storage/roms/ports//` per the port's README. PortMaster's GUI lists missing files before launch. **`box86: assertion failure`.** The port was built against a newer libstdc++ than what ArchR ships. Wait for an update or report it on the [PortMaster Discord](https://discord.gg/CMX5sTrgHQ). **PortMaster GUI itself fails to start.** Run `start_portmaster.sh` over [SSH](/docs/ssh) and read the trace. The script is now idempotent (`rm -rf` + guards) so re-launching is always safe. *** ## Where things live [#where-things-live] ``` /storage/roms/ports/ ├── PortMaster/ GUI + scripts (managed) │ ├── PortMaster.sh │ ├── control.txt default joypad → key map │ ├── gptokeyb input translator │ └── ... ├── DiabloII.sh generated launcher per port ├── DiabloII/ actual port files ├── HalfLife.sh ├── HalfLife/ └── ... ``` Removing a port: delete the `.sh` and the matching folder, then ES → Game Settings → Rescan ROMs. # RetroAchievements [RetroAchievements](https://retroachievements.org/) is a community-run service that adds achievements (and leaderboards) to retro games. ArchR's RetroArch is wired up out of the box, sign in once and you start unlocking. *** ## Setup [#setup] ### Create an account [#create-an-account] Free at [retroachievements.org](https://retroachievements.org/createaccount.php). ### Get on the internet [#get-on-the-internet] ES → **System Settings** → **Network** → enable WiFi. ### Sign in [#sign-in] ES → **Game Settings** → **RetroAchievement Settings** → toggle **Enable RetroAchievements** ON, then enter your username and password. ### Optional toggles [#optional-toggles] In the same menu: * **Hardcore Mode**: disables save-states and rewind. Required to earn the *Mastered* badge. * **Unlock Sound**: plays the classic chime on every unlock. * **Automatic Screenshot**: saves the moment of unlock to `/storage/screenshots/`. * **Encore Mode**: re-trigger achievements you already earned. * **Verbose Mode**: show progress toward achievements with per-step hints. *** ## Which games / cores are supported [#which-games--cores-are-supported] RetroAchievements only works with specific libretro cores and only on games that have an achievement set. Quick list of known-working on RK3326 ArchR: | Console | Core | Notes | | -------- | -------------------------------- | ----------------------- | | NES | `fceumm`, `nestopia` | huge catalog | | SNES | `snes9x2010`, `snes9x` | most games covered | | GB/GBC | `gambatte`, `sameboy` | full | | GBA | `mgba` | full | | MD/MS/GG | `genesis_plus_gx` | full | | N64 | `mupen64plus_next` | partial, heavy on A35 | | PSX | `mednafen_psx_hw`, `swanstation` | full | | PSP | `ppsspp_libretro` | partial | | NDS | `melonds_ds` | partial | | Arcade | `fbneo`, `mame_2003` | works with mame romsets | Full list at [docs.retroachievements.org/Emulator-Support-and-Issues/](https://docs.retroachievements.org/Emulator-Support-and-Issues/). Standalones (PPSSPP, Flycast, Mupen64Plus standalone, DraStic, melonDS standalone) **do not** route through RetroAchievements, only libretro cores do. If you want achievements on PSP/Dreamcast, use the libretro core (`ppsspp_libretro` / `flycast_libretro`) instead of the standalone. *** ## Hardcore vs softcore [#hardcore-vs-softcore] | Hardcore ON | Hardcore OFF | | ---------------------------- | ------------------------------------------------- | | No save-states | Save-states allowed | | No rewind | Rewind allowed | | No cheats | Cheats allowed | | Unlocks count toward Mastery | Unlocks count toward your profile but not Mastery | Most people leave **Hardcore OFF** for normal play and turn it on for runs they want to officially record. *** ## Where unlocks are tracked [#where-unlocks-are-tracked] * **In-game**: a notification shows on every unlock (top of the screen). * **Online profile**: `https://retroachievements.org/user/`. Profile, recent unlocks, leaderboards, master sets, everything is on the website. * **Local screenshots**: if "Automatic Screenshot" is on, every unlock leaves a PNG in `/storage/screenshots/`. *** ## During gameplay performance [#during-gameplay-performance] The RetroAchievements client polls memory each frame to detect achievement triggers. Net cost on RK3326 ≈ 0.5–1 % CPU per active achievement set, negligible for 8 / 16-bit cores, slightly noticeable on PSX/N64 if you're already CPU-bound. The game doesn't have to be online during play, unlocks queue locally and sync on next internet contact. Useful for road trips. *** ## Troubleshooting [#troubleshooting] **No unlocks happening even though I see the achievements list.** Wrong ROM dump. RetroAchievements matches by hash, if your ROM differs, no triggers fire. Use no-intro / Redump-verified dumps. **"Login failed".** Re-enter username + password in the menu. Confirm `https://retroachievements.org` is reachable: `curl -sI https://retroachievements.org`. **"Hardcore mode is disabled because save-state was loaded".** Once you load a state in a hardcore session, RA flips to softcore. Restart the core (Quick Menu → Reset) to re-enter hardcore. # Adding ROMs ROMs live on the **ROMS** partition (the larger of the two, formatted ext4 on Linux/macOS, accessible as a folder named `roms/` from the device). The folder layout mirrors EmulationStation's system list, one folder per system. *** ## Three ways to copy ROMs [#three-ways-to-copy-roms] ### 1. Plug the SD into a PC [#1-plug-the-sd-into-a-pc] Mount the larger partition (label `ROMS`) and drop files into the per-system folders. Works from Linux/macOS natively. On Windows, Linux ext4 partitions need a driver like [Linux File Systems for Windows by Paragon](https://www.paragon-software.com/home/linuxfs-windows/) or [WSL with `--mount`](https://learn.microsoft.com/en-us/windows/wsl/wsl2-mount-disk). ### 2. SSH / SCP from your computer [#2-ssh--scp-from-your-computer] Faster than re-plugging the SD. See [SSH access](/docs/ssh). Once connected: ```bash scp ~/roms/snes/*.sfc root@:/storage/roms/snes/ ``` ### 3. Samba (Windows-friendly) [#3-samba-windows-friendly] Enable Samba in EmulationStation menu → System Settings → Network → Samba. Then on your computer: * **Windows**: open Explorer, type `\\archr` (or the IP). * **macOS**: Finder → `Cmd-K` → `smb://archr.local`. * **Linux**: file manager → "Connect to server" → `smb://archr.local/roms`. The whole `/storage/roms/` tree is shared. Drop files in. *** ## Supported systems and folders [#supported-systems-and-folders] ``` /storage/roms/ ├── nes/ .nes .zip ├── snes/ .sfc .smc .zip ├── n64/ .z64 .v64 .n64 .zip ├── gb/ gbc/ gba/ .gb .gbc .gba .zip ├── nds/ .nds ├── megadrive/ .md .gen .smd .bin .zip ├── mastersystem/ .sms .zip ├── gamegear/ .gg .zip ├── pcengine/ .pce .zip ├── neogeo/ .neo .zip (BIOS in /storage/roms/bios) ├── psx/ .pbp .chd .iso .bin/.cue .ecm ├── psp/ .iso .cso .pbp ├── dreamcast/ .gdi .cdi .chd ├── saturn/ .chd .cue .iso (experimental on RK3326) ├── arcade/ .zip (MAME 2003+ romset) ├── fbneo/ .zip ├── atari2600/ atari5200/ atari7800/ .a26 .a52 .a78 .bin ├── lynx/ jaguar/ .lnx .jag ├── ws/ wsc/ .ws .wsc ├── pcfx/ ngp/ ├── ports/ PortMaster .sh launchers, see /docs/portmaster ├── scummvm/ .scummvm short-cuts ├── dos/ .conf or .dosbox launchers └── bios/ BIOS files for systems that need them ``` EmulationStation rescans on next boot. To trigger a manual rescan: ES menu → Game Settings → Rescan ROMs. *** ## BIOS files [#bios-files] Some emulators need BIOS dumps you provide yourself (legality is on you, Arch R does not ship BIOS files). ``` /storage/roms/bios/ ├── scph5500.bin PSX (US) ├── scph5501.bin PSX (Japan) ├── scph5502.bin PSX (Europe) ├── PSP/ PSP, only if not using PPSSPP HLE ├── neogeo.zip Neo Geo (MAME romset; often part of arcade/) ├── lynxboot.img Atari Lynx ├── disksys.rom Famicom Disk System ├── gba_bios.bin GBA (mGBA HLE works without) ├── pcengine_cd.pce PC Engine CD (PCE-CD games) ├── saturn_bios.bin Saturn (Yabasanshiro) └── dc/dc_boot.bin Dreamcast (Flycast) ``` RetroArch tells you which BIOS is missing in the in-game menu (Quick Menu → Show in load core). *** ## Compressed ROMs [#compressed-roms] Most cores accept `.zip` directly, the [zlib-ng](https://github.com/zlib-ng/zlib-ng) drop-in replacement that ArchR uses gives \~10–30 % faster decompression than plain zlib on the A35. For PSX and Saturn, prefer `.chd` (Compressed Hunks of Data), it gives smaller files than `.bin/.cue` with zero decode loss. Convert with `chdman createcd -i game.cue -o game.chd`. For PSP, prefer `.cso` over `.iso` to halve disk usage with negligible CPU cost. *** ## Sync ROMs between devices [#sync-roms-between-devices] The optional **Syncthing** service keeps a folder identical between your handheld and your PC over LAN/WiFi. Useful if you want save-states and ROMs mirrored automatically without re-mounting the SD. Enable: ES menu → System Settings → Network → Syncthing. The web UI lives at `http://archr.local:8384`. The service is paused automatically while you're playing a game so it never competes for CPU. *** ## ROM scraping [#rom-scraping] EmulationStation includes Skyscraper preconfigured. ES menu → Game Settings → Scraper → Start Scraper. Pulls metadata from ScreenScraper / TheGamesDB / Mobygames. Pro tip: scrape with the device on AC power and the screen off (you can SSH in, run `pkill emulationstation && skyscraper -p ` and let it churn for an hour). *** ## Folder permissions / ownership [#folder-permissions--ownership] The ROMS partition is mounted with `noatime`. ROM files don't need a specific owner, emulators run with file-mode permissions, and ArchR doesn't enforce ownership on the ROMS partition. If you ever delete the per-system folders by accident, they're regenerated on next boot from `/storage/.config/system_directories.txt`. # Samba (network share) Samba lets your computer browse `/storage/roms/` (and your save-states, screenshots, configs) as a regular network share. Faster than re-mounting the SD; works without an SSH client. *** ## Enable [#enable] EmulationStation → **System Settings** → **Network** → **Samba** → ON. ArchR creates the trigger file `/storage/.cache/services/smbd.conf` and starts: * `smbd`, the actual SMB server * `nmbd`, NetBIOS name browser * `wsdd2`, Windows 10/11 discovery (so the device shows up under "Network" in Explorer) *** ## Connect [#connect] ### Windows [#windows] Open File Explorer and type into the address bar: ``` \\archr ``` If the device doesn't show up under "Network" automatically, force the address: ``` \\archr.local \\ ``` You'll see one share: **`roms`**: that's `/storage/roms/`. No login required by default; the share is open to the LAN. ### macOS [#macos] Finder → **Go** → **Connect to Server** (`Cmd+K`) → enter: ``` smb://archr.local ``` Or use the IP. macOS will list the available shares; pick **roms**. ### Linux [#linux] Most file managers (Nautilus, Dolphin, Nemo, Thunar) accept this in the address bar: ``` smb://archr.local/roms ``` Or mount it from CLI: ```bash sudo mkdir /mnt/archr-roms sudo mount -t cifs //archr.local/roms /mnt/archr-roms -o guest,uid=$(id -u),gid=$(id -g) ``` *** ## What's shared [#whats-shared] Default share `roms` exposes the entire `/storage/roms/` tree, read-write, no auth. From a security standpoint this is **LAN-only**: fine for home use, do not enable on a hostile network. To add auth or change the share path, edit `/storage/.config/samba/smb.conf` over [SSH](/docs/ssh): ```ini [roms] path = /storage/roms browseable = yes read only = no guest ok = yes # change to "no" + set "valid users = archr" ``` Then `sudo systemctl restart smbd`. *** ## During gameplay [#during-gameplay] Samba is paused automatically when you launch a game (along with Syncthing, Tailscale, ZeroTier, simple-http-server) and resumed when you exit. The pause is per-process, TCP connections keep their state on the client side and reopen when the service comes back. *** ## Troubleshooting [#troubleshooting] **"Network discovery is turned off" on Windows.** Samba mDNS works but Windows network discovery is off in Settings → Network & Internet → Properties. Either turn it on, or just type `\\archr.local` directly. **"The specified server cannot perform the requested operation".** Old SMB1 client trying to connect. ArchR ships SMB2/3 only. Update your OS or force the client to SMB2+. **Permission denied writing to a folder.** The share is wide-open, but the per-folder permissions on the device default to mode `755`. From SSH: ```bash sudo chmod -R 775 /storage/roms/ ``` **Slow transfers (\< 5 MB/s).** WiFi quality. Check `iwconfig wlan0` signal. The R36S has a small antenna, moving 3 m closer to the router typically doubles throughput. # Scraping Scraping fills your gamelist with cover art, screenshots, descriptions, release dates, and rating, the difference between a list of filenames and an actual library view. EmulationStation has scraping built in. | Before | After | | ----------------------- | ---------------------------------------------- | | `Super Mario World.sfc` | full SNES art + box + screenshot + description | *** ## Setup [#setup] ### Get a ScreenScraper account [#get-a-screenscraper-account] Free, mandatory: [register at screenscraper.fr](https://screenscraper.fr/membreinscription.php). Anonymous scraping rate-limits you to a trickle; an account is mostly to track requests. ### Connect your handheld to the internet [#connect-your-handheld-to-the-internet] ES → **System Settings** → **Network** → enable WiFi. ### Open the scraper [#open-the-scraper] ES → **Scraper** (top-level menu). ### Login [#login] Enter your ScreenScraper username + password in the Username / Password fields. ### Pick what to download [#pick-what-to-download] Tick which assets you want, screenshots, box art (2D and/or 3D), wheel logos, marquee, manuals, videos. Each adds bytes per game; for a 200-game library, **screenshots + box 2D + wheel** is a sane default. ### Pick which systems [#pick-which-systems] The scraper offers per-system selection. Skip systems that won't scrape well (PortMaster ports, custom collections). ### Start [#start] Press **Start Scraper**. ES walks the system list. Slow part: rate-limited by ScreenScraper. *** ## Recommended for the default Art Book Next theme [#recommended-for-the-default-art-book-next-theme] Match the theme's expectations to avoid empty thumbnails: | Setting | Value | | ------------ | -------------------------------------------------- | | Image Source | `Screenshot` (or `Box 2D` for boxart-first themes) | | Box Source | `Box 2D` | | Logo Source | `Wheel` | See [themes](/docs/themes#recommended-settings) for the full pairing table. *** ## During gameplay [#during-gameplay] The scraper takes nontrivial CPU and lots of small file I/O, don't run it while a game is running. If you want it to grind through 1000 games while you're away: 1. SSH in (so ES doesn't have to hold the screen on). 2. Stop ES with `sudo systemctl stop emustation`. 3. Sadly, the in-ES scraper won't run with ES stopped, there's no headless scraper in the ArchR build today. 4. Keep ES running but lock the screen: in ES, **System Settings** → **Screensaver Settings** → set timeout to 1 min, choose the "Black" screensaver. Plug in the charger. ArchR pauses Syncthing/Tailscale/ZeroTier during emulation but not during scraping, those services keep running, so heavy scraping + active VPN means a bit of WiFi contention. *** ## Manual metadata [#manual-metadata] Sometimes ScreenScraper gets the wrong region / wrong title (e.g. JP imports). Edit per-game: ES → highlight game → press `Y` (Edit Metadata) → fix any field. Saved to `/storage/roms//gamelist.xml`. If you want to bulk edit on a PC, just edit `gamelist.xml` directly with your favorite XML editor, ES picks up changes on rescan. *** ## Backup before scraping [#backup-before-scraping] Big scrape sessions hit ScreenScraper rate limits and may leave half-populated metadata if interrupted. Before a long run, copy `/storage/roms//gamelist.xml` somewhere safe so you can roll back if you don't like the result. ```bash ssh root@archr.local cd /storage/roms/snes cp gamelist.xml gamelist.backup.xml ``` # Shaders RetroArch ships with hundreds of shaders that recreate CRT scanlines, LCD grids, NTSC artifacts, etc. ArchR enables them but defaults to **off**: A35 + Mali-G31 is tight, and naive multi-pass shaders will halve your framerate. *** ## Try a built-in shader [#try-a-built-in-shader] ### Launch a game [#launch-a-game] Any libretro core. Standalones (PPSSPP, Flycast, etc.) have their own shader systems; this guide is for libretro/RetroArch. ### Open the menu [#open-the-menu] `MODE + X` → Quick Menu → **Shaders**. ### Set Video Shaders to ON [#set-video-shaders-to-on] Then **Load Preset** → browse `shaders_glsl/`. ### Pick a preset [#pick-a-preset] Recommended starting points for the R36S 480p panel: | File path | Use case | | -------------------------------------- | --------------------------- | | `shaders_glsl/handheld/lcd1x.glslp` | LCD grid for GB / GBA / NDS | | `shaders_glsl/handheld/lcd3x.glslp` | Sharper LCD grid | | `shaders_glsl/crt/crt-pi.glslp` | Light CRT for arcade | | `shaders_glsl/scanline/scanline.glslp` | Just scanlines, ultra-cheap | The preset applies immediately. Back out of the menu to play. *** ## Save the preset for the system [#save-the-preset-for-the-system] In the Shaders menu, **Save** → **Save Game Preset** (just this game) or **Save Core Preset** (every game on this core) or **Save Content Directory Preset** (all games in this folder). Per-game presets land in `/storage/.config/retroarch/config//.glslp`. Per-core in `/storage/.config/retroarch/config//.glslp`. *** ## Build a custom multi-pass preset [#build-a-custom-multi-pass-preset] The example below is the canonical "GBA real hardware" look, color-correct + LCD grid in two passes: ### Open Shaders, change passes to 2 [#open-shaders-change-passes-to-2] Quick Menu → Shaders → **Shader Passes** → 2. ### Pass 1, color correction [#pass-1-color-correction] Click pass 1 → browse to `shaders_glsl/handheld/` → choose `vba-color.glslp`. ### Pass 2, LCD grid [#pass-2-lcd-grid] Click pass 2 → browse to `shaders_glsl/handheld/` → choose `lcd1x.glslp`. ### Apply [#apply] Hit **Apply Changes**. ### Tune parameters [#tune-parameters] **Shader Parameters** is where each shader exposes its knobs. For this combo: * Darken: `0.25` * Brighten Scanlines: `28.0` * Brighten LCD: `6.0` ### Save [#save] Back at the Shaders menu → **Save** → set **Simple Presets** to **off** → **Save Shader Preset As** → name it (e.g. `gba-real-hw`). The named preset shows up under **Load Preset** the next time, and is also selectable from ES → Game Settings → Per System Advanced Configuration → Shader Set. *** ## Performance budget [#performance-budget] The R36S has very little overhead for shader passes. As a rule of thumb on RK3326 with overclock OFF (1.4 GHz CPU + 650 MHz GPU): | Tier | Ok? | Examples | | ------------------ | --------- | ------------------------------- | | 1 cheap pass | ✅ | scanline, lcd1x, vba-color | | 1 expensive pass | usually ✅ | crt-pi, crt-easymode | | 2 cheap passes | usually ✅ | gba-real-hw (vba-color + lcd1x) | | 2 expensive passes | ⚠️ | crt-royale, crt-aperture-grille | | 3+ passes | ❌ | most CRT-Royale variants | Dreamcast / N64 / PSP cores already eat the GPU; even a 1-pass shader on top can drop you below 60 fps. Reserve fancy shaders for 8 / 16-bit cores. Toggle **Enable CPU Overclock** if you want to try heavier shaders, see [Overclock](/docs/overclock). *** ## ROCKNIX-style shader sets via ES menu [#rocknix-style-shader-sets-via-es-menu] ES exposes a shader-set picker without going into RetroArch: ES → **Game Settings** → **Per System Advanced Configuration** → **Shader Set** → pick from the dropdown. This applies the chosen shader to every game on that system. Useful as a quick toggle. Custom presets you saved earlier appear here too. # SSH access SSH is **off by default** for security. Turn it on through the EmulationStation menu, then connect from your computer. *** ## Enable SSH [#enable-ssh] ### Toggle in ES [#toggle-in-es] EmulationStation → \*\* \*\* (Start) → **System Settings** → **Network** → **Enable SSH** → ON. ArchR creates `/storage/.cache/services/sshd.conf` (the trigger file the systemd unit watches), then `systemctl start sshd`. ### Optional: enable on every boot [#optional-enable-on-every-boot] Same menu, leave the toggle on. The trigger file persists, so `sshd.service` starts at boot via `ConditionPathExists=`. ### Find the device's IP [#find-the-devices-ip] In ES → System Information shows IP. Or via ssh from any device on the same LAN: ```bash ping archr.local ``` mDNS publishes `archr.local` automatically (Avahi). *** ## Connect [#connect] ```bash ssh root@archr.local # or with the IP: ssh root@192.168.x.x ``` Default credentials: ``` user root password archr ``` Change the password after the first successful login. The default is published, anyone on the LAN can guess it. ```bash passwd ``` *** ## Key-based auth (recommended) [#key-based-auth-recommended] ```bash # on your computer ssh-copy-id root@archr.local ``` Then disable password auth on the device (over SSH already logged in): ```bash sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config sudo systemctl restart sshd ``` *** ## File transfer [#file-transfer] `scp` (one-shot copy) is the simplest way to move files. ROMs, save-states, screenshots: ```bash # upload a ROM scp game.sfc root@archr.local:/storage/roms/snes/ # download all screenshots scp root@archr.local:'/storage/screenshots/*' ~/Pictures/r36s-screenshots/ # upload a directory scp -r ~/roms/psx root@archr.local:/storage/roms/ ``` For continuous sync, use `rsync` instead, it skips files that didn't change: ```bash rsync -avh --delete ~/roms/snes/ root@archr.local:/storage/roms/snes/ ``` For graphical access, your file manager probably supports `sftp://root@archr.local/storage/roms/`. *** ## SSH tips [#ssh-tips] **EmulationStation in the way.** ES locks the framebuffer; you can't run an X/wayland program over SSH. But you can stop ES and run a one-shot tool: ```bash sudo systemctl stop emustation # now you can run things sudo systemctl start emustation ``` **Long-running tasks (scrape, rsync TBs, etc.).** Use `screen` or `nohup` so they survive SSH disconnects: ```bash screen -S scrape skyscraper -p snes # Ctrl-A D to detach. Reattach later: screen -r scrape ``` **Inspecting boot.** Logs land in `/var/log/boot.log` (not `journalctl`, journald is volatile). The autostart trace is in `/storage/.boot_last_step` only when the previous boot hung. **Bench scripts.** `/flash/bench.sh [duration]` collects PSI / freq / temp / vmstat for the given duration into `/storage/bench-results/`. Useful when reporting a perf regression. *** ## Disable SSH [#disable-ssh] Same menu, toggle OFF. The trigger file is removed and `sshd.service` stops. The next boot doesn't start it. *** ## Security notes [#security-notes] * **Public LAN risk**: with default creds and SSH on, anyone on the same WiFi can log in. Change the password. * **Tailscale/ZeroTier**: if you enable a VPN, your device is reachable from anywhere on that VPN, same advice applies. * **Port forwarding**: do **not** forward port 22 to your handheld from your home router. The default install isn't hardened for the public internet. * **Disabling root**: `sudo passwd -l root` if you'd rather no one log in as root over SSH. The `archr` user has `sudo` for whenever you need elevated commands. # Status ## Validated on hardware (R36S V21) [#validated-on-hardware-r36s-v21] ``` kernel 6.12.79 ok display MIPI DSI ok 43 panel DTBOs gpu / panfrost Mesa 26.0.5 ok Mali-G31, GLES 3.1 gpu / libmali r52p0-00eac0 ok PM patch eliminates regulator unbalance gpu turbo 650 MHz 1.150 V (= 600 MHz V) ok cpu turbo 1512 MHz 1.4 V (regulator 1.45 V) ok toggle via Enable CPU Overclock audio.spk RK817 ok volume hotkeys, persistence audio.hp RK817 ok jack detect, auto path switch audio.hdmi rk-hdmi ok emustation ROCKNIX fork ok runs at panel refresh retroarch libretro ok KMS/DRM + EGL + GLES 2.0 ppsspp-sa v1.18+ ok GLTHREAD on flycast-sa recent ok 240 res default, GLTHREAD on mupen64plus-sa latest ok GLideN64 performance preset drastic-sa NDS default ok yabasanshiro-sa Saturn experimental ok light scenes only duckstation-sa Software renderer ok battery rk817 ok 3200 mAh, charge support, LED warning brightness sysfs + udev ok MODE+VOL, persists usb_otg u2phy ok keyboard input shutdown pmic ok two-stage rail disable boot initramfs splash ok 19 s typical zlib-ng 2.2.4 --zlib-compat ok libz.so.1 ABI preserved mesa lto+speed ok +5–8 % CPU side cma 96 MB ok ~30 MB free at idle (was 8 MB at 64 MB) boot fat sync ok primary == backup, 65 525+ clusters fs-resize ok fsck.fat -a -w após fatlabel runemu pause services ok Syncthing/Tailscale/ZeroTier/HTTP suspended ksm pause-during-game ok save+restore, frametime jitter ↓ ``` *** ## In progress [#in-progress] ``` panel auto-detect ⬜ replaces panel selector wizard preempt voluntary A/B ⬜ pending bench reproducibility mesa-glthread per-core ⬜ standalones done, libretro left as off ``` *** ## Untested in 6.12 (carry-over from 6.6) [#untested-in-612-carry-over-from-66] ``` wifi networkmanager , iwd + connman default; NM legacy carry-over bluetooth controller , a2dp not validated on this kernel hdmi audio out , on bench, no display tested yet ``` *** ## Clone compatibility [#clone-compatibility] ``` Original R36S Clone (K36, G80CA, RX6S, Powkiddy …) u-boot BSP (display+logo) Mainline v2025.10 (no logo) volume gpio-keys-vol adc-keys SARADC ch2 boot config boot.ini boot.scr display dtb uboot-display.dtb not needed kernel shared 6.12.79 shared rootfs shared shared panel set 15 original DTBOs 18 clone + 10 soysauce DTBOs ``` *** ## What's NOT supported on RK3326 [#whats-not-supported-on-rk3326] These systems are explicitly hidden from the menu on RK3326 because the Cortex-A35 simply can't run them at usable framerates: ``` GameCube, Wii, WiiWare not viable on A35 Wii U not viable PS2 (AetherSX2) not viable on A35 Switch (Yuzu/Ryujinx) not viable 3DS (Citra) not viable ``` Other RK3326 distros that *do* expose them effectively run them at slideshow-class FPS, ArchR doesn't lure users into trying. # Technical reference ## Build pipeline [#build-pipeline] `make docker-RK3326` runs through the full LibreELEC-style stage system inside Docker. Roughly 700 install steps for \~263 packages. ``` toolchain cross-gcc 14.2 / glibc / binutils, host stage linux 6.12 LTS kernel + RK3326 DTS + ArchR patches mali-bifrost out-of-tree mali_kbase + ArchR PM unbalance patch mesa 26.0.5 Panfrost (GLES 3.1, +speed +lto) zlib (-ng) zlib-ng 2.2.4 in --zlib-compat mode (libz.so.1) retroarch 1.x + libretro core suite emulators Flycast, PPSSPP, melonDS, DraStic, Yabasanshiro, Mupen64Plus standalones es EmulationStation (ROCKNIX fork) image FAT32 BOOT (272 MB, 4 KB clusters) + ext4 ROOT + ext4 STORAGE ``` Output in `target/ArchR-R36S.aarch64-DATE-{original,clone}.img.gz`. *** ## Kernel [#kernel] ``` Source Linux 6.12 LTS (BSP fork, Rockchip patches + ArchR) Toolchain gcc 14.2.0 Config projects/ArchR/devices/RK3326/linux/linux.aarch64.conf HZ 300 PREEMPT CONFIG_PREEMPT=y (PREEMPT_VOLUNTARY A/B pending bench) DEBUG all sanitizers / KASAN / KMEMLEAK / FTRACE off in release DEBUG_FS y (intentional, needed for CMA debugfs and PSI introspection) CMA 96 MB (raised from 64 MB after audit; see /docs/features) ZRAM lzo-rle by default (zstd opt-in via memory-manager) ZSWAP disabled (zswap.enabled=0 cmdline) ``` ### DTS [#dts] Source: `rk3326-gameconsole-r36s.dts` (board) + `rk3326-gameconsole-r3xs.dtsi` (board family) + `px30.dtsi` (SoC) + `000-rk3326-dts.patch` (ArchR overrides). | Node | What we changed | Why | | ------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------- | | `cpu0_opp_table` | Added 1512 MHz turbo OPP @ 1.4 V; kept full 408–1416 ladder | Reachable via "Enable CPU Overclock" | | `vdd_arm` regulator | `regulator-max-microvolt` 1.35 V → 1.45 V | Otherwise kernel rejects 1512 OPP silently | | `gpu_opp_table` | Restored 200/300/400 MHz lower OPPs; added 600/650 MHz top | Lower OPPs needed for boot; 650 MHz is daily turbo at 1.150 V | | `dmc` / `dfi` | Defined explicitly so devfreq can be controlled | Upstream px30.dtsi has them disabled | | `gpu` | Added `clock-names="bus"`, `resets`, `power_policy="coarse_demand"`, power\_models | Required for modern panfrost/mali\_kbase | | `ethernet0` alias | Added | Future USB-Ethernet hot-plug naming | | Power model | `mali-simple-power-model` + `mali-g31-power-model` | Thermal-aware DVFS | ### Kernel patches [#kernel-patches] Notable ArchR-specific kernel patches (`projects/ArchR/packages/linux-drivers/mali-bifrost/patches/6.12-LTS/` and `projects/ArchR/packages/linux/patches/6.12-LTS/`): * **`mali-bifrost/002-fix-unbalanced-regulator-clk-pm.patch`**: eliminates `unbalanced disables for vdd_logic` warnings when `vdd_logic` is shared with rockchip-pm-domain. Adds a `gpu_power_held` flag tracking the driver's own enable state. * **`linux/0005-drm-panfrost-Add-SYSTEM_TIMESTAMP*.patch`**: required by Mesa 26.0.5 for `GL_TIMESTAMP` queries and `VK_KHR_calibrated_timestamps`. * **`linux/0006-drm-panfrost-Add-cycle-counter-job-requirement.patch`**: `VK_KHR_shader_clock` plumbing. * **`linux/0007-0010-drm-panfrost-Add-BO-labelling*.patch`**: debug aid; Mesa calls the new IOCTL even though the labels are debug-only. *** ## Mesa 26.0.5 [#mesa-2605] Built with `+speed +lto` in `packages/graphics/mesa/package.mk`. Configure flags: ``` -Dgallium-drivers=panfrost -Dvulkan-drivers= -Dgles1=disabled -Dgles2=enabled -Dglvnd=disabled -Dplatforms=wl -Dglx=disabled -Dshader-cache=enabled -Dopengl=true -Dgbm=enabled -Degl=enabled -Dlibunwind=disabled -Dbuild-tests=false -Ddraw-use-llvm=false ``` `-Dgallium-drivers=panfrost` only, no software fallback shipped (LLVMpipe's overhead would dwarf hardware on G31). `-Dvulkan-drivers=` empty because PanVK isn't conformant on Bifrost v9. ### Runtime env [#runtime-env] Exported globally in `/etc/profile.d/041-panfrost`: ```bash PAN_MESA_DEBUG=forcepack # G31-specific format-pack hint MESA_NO_ERROR=1 MESA_SHADER_CACHE_DIR=/storage/.cache/mesa_shader_cache MESA_SHADER_CACHE_MAX_SIZE=128MB ``` Per-emulator `MESA_GLTHREAD=true` whitelist, only Flycast & PPSSPP standalone (see [GPU driver](/docs/gpu-driver) and the in-repo `docs/mesa-glthread-matrix.md`). *** ## libmali (alternative) [#libmali-alternative] Out-of-tree `mali_kbase` r52p0-00eac0 with the regulator/clk PM patch. Switched at boot via `/usr/bin/gpudriver --start` reading `/storage/.config/system.cfg → gpu.driver`. Auto-fallback if either driver fails to attach to the GPU node. *** ## U-Boot [#u-boot] | | Original | Clone | | ----------- | ----------------------------------------------- | -------------------------------- | | Source | `bootloader/u-boot-rk3326/` (BSP) | mainline + ROCKNIX patches | | Boot config | `boot.ini` | `boot.scr` (`mkimage -T script`) | | Display | hwrev SARADC ch0 → board DTB → DRM → `logo.bmp` | none | | Defconfig | `odroidgoa` | `rk3326-handheld_defconfig` | The two binaries differ enough that you can't boot a clone with the BSP build. ArchR ships both, the build picks based on `--variant` (or `make docker-RK3326` produces both). *** ## Image layout [#image-layout] ``` GPT ├── 1 FAT32 BOOT 272 MB 65525+ clusters of 4 KB (FAT32 spec compliant) │ Kernel, DTBs, all 43 panel overlays, U-Boot script ├── 2 ext4 ROOT ~4.6 GB read-only system (squashfs in some legacy builds) └── 3 ext4 STORAGE rest writable: configs, ROMs, save-states, screenshots ``` `mkimage` runs `fsck.fat -a -w` on the BOOT FAT before cementing the image, guarantees primary and backup boot sectors are byte-identical. The on-device `fs-resize` script does the same after `fatlabel` regenerates the UUID, so the post-resize boot stays consistent. *** ## Boot flow [#boot-flow] ``` 0.0 POR / SPL (ROM-resident) 0.x U-Boot reads SARADC ch0 → board ID 0.5 loads board DTB + panel overlay from /flash/ 0.7 initramfs splash (BMP via xxd, static aarch64 /init) ~7 kernel hand-off ~9 systemd target ~12 archr-autostart (quirks + governor + display + bluetooth + ssh check) ~17 EmulationStation ~19 ready to play ``` Trace is written to `/storage/.boot_last_step` before each autostart script runs and erased on success, if the previous boot hung, the file at `/storage/.boot_last_hang` names the script that hung. Load it for bug reports. *** ## Display panels [#display-panels] 43 panel overlays generated from vendor DTB dumps: ``` config/archr-dts/ ├── original/ (15) R36S V20–V22, OGS variants ├── clone/ (18) K36, R33S, R36 Max, RX6S, multiple panel families └── soysauce/ (10) Y3506-based hardware ``` The generator (`config/mipi-generator/generator.sh` → `archr-dtbo.py`) extracts panel init-sequence + timings from the vendor DTB, applies a base ArchR overlay template, and emits one `.dtbo` per motherboard revision. Six variants per panel are produced: ``` default K36 layout, no JP swap, normal audio _JPk36 K36 layout, JP A↔B / X↔Y _JPmm MyMini layout, JP swap _SRs force simple audio path _JPk36_SRs K36 + JP + SRs _JPmm_SRs MyMini + JP + SRs ``` 258 DTBOs total. The Flasher (or manual copy) drops one of these into `/flash/overlays/mipi-panel.dtbo` at write time. *** ## Pitfalls [#pitfalls] Edge cases that bit during development. None of them apply to a normal release flash, they only matter if you're hacking on the build. **Hunk header counts in DTS patches matter.** A wrong `@@ -X,Y +A,B @@` count causes `patch` to silently abort the rest of the hunks, recovered "garbage". Validate with `patch --dry-run --verbose` on a pristine source after every DTS edit. **`turbo-mode` OPPs hide behind `cpufreq/boost`.** Setting `scaling_max_freq=1512000` directly does nothing if `boost=0`, the freq isn't in `scaling_available_frequencies` until you write `1` to `cpufreq/boost`. ArchR's `set_cpu_max_freq highest` reads both `scaling_available_frequencies` *and* `scaling_boost_frequencies`. **`vdd_arm` regulator-max gates OPP availability silently.** If an OPP requests a voltage above `regulator-max-microvolt`, kernel drops the OPP with no `dmesg`, and `policy_has_boost_freq()` returns false → `cpufreq/boost` sysfs file is **never created**. **`fatlabel -i -r` desyncs FAT primary/backup.** It rewrites the UUID/label only on sector 0; backup at sector 6 keeps the old values. Some U-Boot builds occasionally read the backup, causing intermittent boot hangs after resize. Solution: run `fsck.fat -a -w` immediately after. **`patch.fat` / `cluster < 65525` warnings are mostly harmless** but occasionally bite specific U-Boot ROM versions. ArchR uses `BOOT_SIZE=272 MB` with 4 KB clusters → 69500+ clusters → above spec. **Mesa shader cache root must exist.** Mesa won't `mkdir` `MESA_SHADER_CACHE_DIR` itself, just falls through to no-cache mode silently. `runemu.sh` creates `/storage/.cache/mesa_shader_cache` if missing. **`panfrost_devfreq` needs low GPU OPPs to initialize.** Removing 200/300/400 MHz OPPs (to force a higher floor) blocks GPU init at boot, the driver expects the lowest published OPP to be reachable. Keep the ladder; force a high floor at runtime with `set_gpu_min_freq highest` instead. **`PREEMPT` vs `PREEMPT_VOLUNTARY` is unbenched.** Kernel currently uses `PREEMPT`; ROCKNIX uses `VOLUNTARY`. Pending A/B with the on-device bench. **Mali\_kbase regulator unbalance is a kernel patch, not a quirk.** Without `002-fix-unbalanced-regulator-clk-pm.patch`, every PM transition emits `unbalanced disables for vdd_logic` and `Enabling unprepared clk_gpu`, audible as crackle in Mario 64. # Themes ArchR ships with [**Art Book Next**](https://github.com/anthonycaccese/art-book-next-es) as the default theme. It's the same theme ROCKNIX uses, designed to support every aspect ratio (16:9, 4:3, 16:10, 5:3, 3:2, 1:1). It's installed at `/usr/share/themes/es-theme-art-book-next` and symlinked into the user theme path as `system-theme`. *** ## Configure the default theme [#configure-the-default-theme] ES → **UI Settings** → **Theme Configuration** has six knobs: | Option | What it does | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Distribution** | Which folder under your ROMs is checked for theme customizations. Leave at `ROCKNIX` (carry-over from upstream), it points at the right path. | | **Aspect Ratio** | Auto-detected (640×480 ≈ 4:3 on R36S). Override if the layout looks off. | | **Color Scheme** | Pick a built-in color scheme or `custom` to use your own (see below). | | **Font Size** | `default` or `custom`. | | **System View Style** | Layout for the home screen, Centered Artwork, Multi Artwork, Fullscreen Artwork, Carousel. | | **Gamelist View Style** | Layout per game list, Metadata On / Metadata Off / Immersive variants. | *** ## Recommended settings [#recommended-settings] For an R36S (4:3, 640×480) with screenshots: | Setting | Value | | --------------------- | ------------------------------------------------------- | | Aspect Ratio | `4:3` (auto) | | Scraper: Image Source | `Screenshot` | | Scraper: Box Source | `Box 2D` | | Scraper: Logo Source | `Wheel` | | Gamelist View Style | `Metadata On (Immersive)` or `Metadata Off (Immersive)` | For boxart-first: | Setting | Value | | --------------------- | ------------- | | Scraper: Image Source | `Box 2D` | | Scraper: Box Source | `None` | | Scraper: Logo Source | `Wheel` | | Gamelist View Style | `Metadata On` | *** ## Customizing Art Book Next [#customizing-art-book-next] Art Book Next reads customizations from a specific path so your changes survive ArchR updates: ``` /storage/roms/rocknix/theme-customizations/art-book-next/ ``` (The folder name is `rocknix` because that's the upstream's distribution key, the theme reuses it across forks. Don't rename.) ### Background art [#background-art] ``` /storage/roms/rocknix/theme-customizations/art-book-next/backgrounds/ _default.jpg fallback for systems without a custom image snes.jpg per-system override (filename = es system tag) psx.png ... ``` In Theme Configuration, set **System View Style** to one of the `(Custom)` variants: * Centered Artwork (Custom) * Multi Artwork (Custom) * Fullscreen Artwork (Custom) To craft a custom centered artwork that follows the theme's aspect mask, the original author publishes a [resources mask](https://github.com/anthonycaccese/art-book-next-es/tree/main/resources/customizations). ### Custom logos [#custom-logos] ``` /storage/roms/rocknix/theme-customizations/art-book-next/logos/ snes.svg SVG preferred (scales clean) psx.png ``` **Color Scheme** → `Custom` is what enables the override. ### Color scheme [#color-scheme] [Download the template](https://github.com/anthonycaccese/art-book-next-es/blob/main/resources/customizations/colors.xml) → save as: ``` /storage/roms/rocknix/theme-customizations/art-book-next/colors.xml ``` Edit the values, set **Color Scheme** to `Custom`. ### Font size [#font-size] [Download the template](https://github.com/anthonycaccese/art-book-next-es/blob/main/resources/customizations/fonts.xml) → save as: ``` /storage/roms/rocknix/theme-customizations/art-book-next/fonts.xml ``` Set **Font Size** to `Custom`. *** ## Adding additional themes [#adding-additional-themes] Drop a third-party theme into: ``` /storage/.emulationstation/themes/ ``` Each theme is its own folder. ES picks them up on next boot (or **UI Settings** → **Theme Set** → choose). ### Sources of compatible themes [#sources-of-compatible-themes] ArchR's EmulationStation is forked from the same Batocera/ROCKNIX lineage, so themes designed for either generally work: * **[Batocera themes](https://batocera.org/themes.php)**: large catalog. Some don't ship every system or fit non-16:9. * **[es-theme-gbz35-jelos](https://github.com/booYah187/es-theme-gbz35-jelos)**: works well at 4:3. * **[es-theme-albedo](https://github.com/mluizvitor/es-theme-albedo)**: 3:2, 4:3, 5:3. * **[es-theme-elementerial](https://github.com/mluizvitor/es-theme-elementerial)**: 3:2, 4:3, 5:3. Themes designed for portrait-oriented or 16:9 devices may look cramped on the R36S 4:3 panel. Test before committing, the theme picker is reversible. *** ## Writing your own theme [#writing-your-own-theme] Out of scope for this page, but the [Batocera theme guide](https://wiki.batocera.org/write_themes_for_emulationstation) is the closest match for the EmulationStation flavour ArchR ships. # Troubleshooting ## Boot issues [#boot-issues] ### Black screen, just the splash [#black-screen-just-the-splash] Wrong panel overlay. Most common cause when first flashing a clone device with a recent motherboard revision. **Fix:** re-flash with the [Flasher](/docs/installation) and pick the exact silkscreen-printed motherboard revision in the panel dropdown. ### Boot loops, never reaches ES [#boot-loops-never-reaches-es] Three possibilities, in order of likelihood: 1. **CPU instability at turbo** (only with Enable CPU Overclock on, very rare). Power off, remove SD, edit `/storage/.config/system.cfg` from a PC and set `enable.turbo-mode=0`. 2. **fs-resize hung** on first boot. Capture `cat /storage/.boot_last_hang` after the eventual successful boot, that file names the script that stalled. 3. **Wrong image variant** (original on a clone or vice versa). Re-flash with the right variant. ### Boot reaches ES but freezes there [#boot-reaches-es-but-freezes-there] Stop ES, then check what's holding things up: ```bash sudo systemctl stop emustation sudo journalctl -t archr-autostart -f ``` Or read the boot log (`/var/log/boot.log`, note that journald is volatile so logs from the previous boot are lost). Restart ES with `sudo systemctl start emustation` once you've identified the issue. ### "Boot anterior travou em \