A note on this project: I'm building this to get my hands dirty with agentic AI and see what it can actually do for coding. This is a non-commercial, educational project by hiitsgabe.
I know there's a lot of "AI slop" being sold to gamers right now, and I want to be clear: this isn't that. This is my personal sandbox for testing the limits of AI agents while creating something useful. It is experimental and a bit unstable, but it's a genuine effort to create something cool and helpful while I improve my skills w/ AI.
I will never ask you to donate money to this project, or for me, because of this. The only thing I ask is that if you find a bug, you use the issues here on GitHub to report it so I can make it better for everyone. You can also use issues to give ideas. Pull requests are welcomed, help me build this! :)
⚠️ Disclaimer: This application does not endorse any form of piracy. Only download games you legally own.
A PyGame-based utility suite for handheld gaming consoles, with a retro CRT-themed interface designed for D-pad and controller navigation. Browse, download, organize, and manage game files from configurable sources. Supports HTML directory listings, JSON APIs, and Internet Archive. Runs on Batocera/Knulli handhelds, macOS, Windows, Linux, and Android.
- Batch Downloads — Select multiple games and download them in a queue with real-time progress, speed, and ETA
- Resume Capability — Interrupted downloads can be resumed right where they left off
- Automatic Extraction — ZIP and RAR files are extracted and organized into the correct system folder
- NSZ Decompression — Built-in NSZ to NSP conversion for Nintendo Switch files
- Multiple View Modes — List view and grid layout with box art thumbnails
- Search — Filter games by name within any system
- Region Filtering — USA-only filter with configurable regex per system
- Installed Detection — Optionally hide games already downloaded
- Live Roster Updates — Fetch current or historical season rosters from public sports APIs (ESPN, NHL API, API-Football) and patch them into your legally owned game ROMs
- Multi-Platform Support — Patchers for retro soccer, baseball, and hockey titles across PS1, SNES, Genesis, and PSP
- Step-by-Step Workflow — Guided process: select season → fetch rosters → preview teams → pick your ROM → patch
- Roster Preview — View fetched teams and players before committing changes
- Player Attributes — Maps real-world stats (goals, assists, speed, etc.) to in-game attribute scales
- Team Customization — Update team names, kit/jersey colors, and flag designs where supported
- Historical Seasons — Access roster data from past seasons via public APIs (availability varies by provider)
- Non-Destructive — Saves patched output to a new file, leaving your original ROM untouched
📖 See the Sports ROM Patcher Guide for detailed usage instructions.
- 🎮 Steam Shortcut Creator — Search Steam games and create
.steamshortcut files for ES-DE frontends on Android — browse with banner images, pick a folder, and generate the shortcut - 🔗 Direct URL Download — Download a file from any URL
- 📚 Internet Archive Integration — Download individual files or add entire IA collections as systems
- 🖼️ Image Scraping — Scrape game artwork from multiple providers (Libretro, ScreenScraper, TheGamesDB, RAWG, IGDB) with batch mode support
- 🔄 File Deduplication — Detect and remove duplicate files (safe and fuzzy matching)
- ✏️ Filename Cleanup — Batch rename files to clean formats
- 👻 Ghost File Cleaner — Find and remove orphaned split archives
- 📂 ZIP/RAR Extraction — Extract archives from the file browser
- Cross-Device Save Sync — Synchronize game saves between your computer and Knulli/Batocera consoles or Android devices using Syncthing
- Hub-and-Spoke Architecture — Computer acts as the hub, with multiple consoles and Android devices as spokes
- Per-System Folders — Auto-configures shared folders for 22+ gaming systems (PSX, SNES, N64, GBA, NDS, PSP, and more)
- Custom Save Support — Add custom save folders with selective file syncing and
.stignorewhitelisting - Versioning — Built-in 5-file rollback support for save safety
- Auto-Detection — Discovers Syncthing API key from the local config automatically
Note: This is not Syncthing itself — it's a helper that configures an existing Syncthing installation through its REST API. See the Syncthing Save Sync Guide for setup instructions.
- Add Custom Systems — Discover systems from directory listings or manually configure new sources
- Per-System Settings — Custom ROM folders, hide/show systems
- Multiple Server Formats — HTML directory listings, JSON APIs, Internet Archive metadata API
- Authentication — Bearer tokens, cookies, and IA S3 credentials
- CRT Theme — Phosphor green retro aesthetic with scanlines, vignette, and bezel effects
- Controller & Keyboard — Full D-pad/gamepad support with acceleration and touch/mouse input
- Auto-Updates — Check for and install app updates from within the app
# Setup conda environment and install dependencies
make setup
# Run with auto-restart on file changes
make run
# Run without auto-restart (full error logs)
make debugOr manually:
conda env create -f environment.yml
conda activate console_utilities
pip install -e .[dev]
DEV_MODE=true python src/app.py- Create a
downloaderfolder inside your console'spygameroms directory - Run
make bundleto create the distribution package - Copy
dist/pygame.zipcontents (console_utils.pygame+assets/) to the console folder - Rescan games in EmulationStation
- Navigate to the PyGame library and launch Console Utilities
make bundle # 🎮 PyGame bundle for consoles
make bundle-macos # 🍎 macOS .app standalone
make bundle-windows # 🪟 Windows .exe standalone
make build-android # 🤖 Android APK (Docker-based)Systems are configured via JSON files that define where to find files and how to parse server responses. See the docs for details:
- 📄 Server Response Format — How your server needs to respond for the app to detect files
- 📄 Adding a System — How to add custom systems
Runtime settings are auto-generated and stored in config.json:
- 📁 Directories — Working directory, ROMs directory
- 🖥️ Display — Box art thumbnails, USA-only filter, skip installed games
- 📚 Internet Archive — Enable/disable, S3 credentials
- 🖼️ Scraper — Provider selection (Libretro, ScreenScraper, TheGamesDB, RAWG, IGDB), frontend format (EmulationStation, ES-DE, RetroArch, Pegasus), API credentials
- 🔐 NSZ — Enable/disable, keys file path
- 🔄 Syncthing — Enable/disable, role (host/console), device IDs, base path, custom saves
| Input | Action |
|---|---|
| ⬆️⬇️ D-pad / Arrow Keys | Navigate systems |
| Select system | |
| Exit application | |
| SELECT | Toggle list/grid view |
| Input | Action |
|---|---|
| ⬆️⬇️ D-pad / Arrow Keys | Navigate games |
| ⬅️➡️ D-pad / Page Up/Down | Jump by letter |
| Toggle game selection | |
| Return to systems | |
| START / Enter | Begin download |
| SELECT | Toggle view/thumbnails |
| Input | Action |
|---|---|
| Cancel download | |
| — | Real-time progress with speed & ETA |
console_utilities/
├── src/
│ ├── app.py # 🚀 Main application entry point
│ ├── state.py # 📊 Centralized state management
│ ├── constants.py # 🔢 Global constants and paths
│ ├── config/
│ │ └── settings.py # ⚙️ User settings persistence
│ ├── services/
│ │ ├── data_loader.py # 📦 System/game data loading
│ │ ├── download_manager.py # 📥 Download queue management
│ │ ├── file_listing.py # 📋 Remote file listing (HTML/JSON/IA)
│ │ ├── image_cache.py # 🖼️ Thumbnail caching
│ │ ├── installed_checker.py # ✅ Local file detection
│ │ ├── internet_archive.py # 📚 Internet Archive API
│ │ ├── scraper_manager.py # 🔍 Image scraper orchestration
│ │ ├── scraper_providers/ # 🎨 Libretro, ScreenScraper, etc.
│ │ ├── we_patcher/ # ⚽ PS1 soccer ROM patcher
│ │ ├── iss_patcher/ # ⚽ SNES soccer ROM patcher
│ │ ├── kgj_mlb_patcher/ # ⚾ SNES baseball ROM patcher
│ │ ├── nhl94_genesis_patcher/ # 🏒 Genesis hockey ROM patcher
│ │ ├── nhl94_snes_patcher/ # 🏒 SNES hockey ROM patcher
│ │ └── nhl07_psp_patcher/ # 🏒 PSP hockey ISO patcher
│ ├── input/
│ │ ├── controller.py # 🎮 Controller/gamepad input
│ │ ├── navigation.py # 🕹️ D-pad navigation with acceleration
│ │ └── touch.py # 👆 Touch/mouse input
│ ├── ui/ # 🎨 UI components (Atomic Design)
│ │ ├── theme.py # 🎨 Design tokens and theming
│ │ ├── atoms/ # 🔵 Basic components
│ │ ├── molecules/ # 🟢 Composite components
│ │ ├── organisms/ # 🟡 Complex sections
│ │ ├── templates/ # 🟠 Page layouts
│ │ └── screens/ # 🔴 Complete screens and modals
│ ├── utils/ # 🔧 Logging, formatting, NSZ wrapper
│ └── nsz/ # 🔐 Embedded NSZ library
├── assets/
│ ├── bundled_data.json # 📋 System configuration
│ ├── docs/ # 📖 Platform-specific build docs
│ ├── examples/ # 📝 Example configuration files
│ ├── fonts/ # 🔤 VT323 retro font
│ └── images/ # 🖼️ Logo and screenshots
├── docs/ # 📖 User documentation
├── workdir/ # 🗂️ Development runtime data
├── dist/ # 📦 Built distributions
├── Makefile # 🏗️ Build and development commands
├── buildozer.spec # 🤖 Android build configuration
├── environment.yml # 🐍 Conda environment specification
├── pyproject.toml # 📋 Python project configuration
└── README.md
- 🐍 Python 3.11+
- 🎮 pygame >= 2.0.0
- 🌐 requests >= 2.25.0
- 📦 rarfile (bundled for console)
- 👀 watchdog (development only)
- 🖤 black, flake8 (development only)
| Platform | Details |
|---|---|
| 🎮 Console | Knulli RG35xxSP and other Batocera-based handhelds |
| 💻 Desktop | macOS (.app), Windows (.exe), Linux |
| 📱 Mobile | Android (APK via Buildozer) |
| 🖥️ Display | 800x600 resolution, optimized for small screens |
- 🔄 Syncthing Save Sync Guide — Setting up cross-device save synchronization
- ⚽ Sports ROM Patcher Guide — How the sports roster patcher works
- 📄 Server Response Format — How your server needs to respond for the app to detect and list files
- ➕ Adding a System — How to add custom gaming systems
- 🎮 PyGame Bundle Guide — Deploying to Batocera/Knulli consoles
- 🍎 macOS Build Guide — Building the macOS standalone app
- 🪟 Windows Build Guide — Building the Windows standalone app
- 🤖 Android Build Guide — Building the Android APK
-
No ROM Data Storage — This system does not host, store, or distribute any ROM files, game data, or copyrighted content. It is purely a download management and ROM patching tool.
-
No Game Copies — This application contains no copies of games, ROMs, ISOs, or any copyrighted gaming content whatsoever.
-
ROM Patching — Original Copies Only — The sports ROM patcher feature requires users to provide their own legally obtained ROM or ISO files dumped from original game media they own. This project does not provide, link to, or facilitate the acquisition of any game files. Any ROM or ISO used with the patcher must come from an original copy of the game that you legally own.
-
No Affiliation — This project is not affiliated with, endorsed by, or associated with any game publishers, developers, sports leagues, or rights holders.
-
Example Configuration — Any included configuration files serve as examples only. They demonstrate how the system works but do not endorse or recommend any specific download sources.
-
Legal Responsibility — Users are solely responsible for:
- Ensuring they have legal rights to download any content
- Providing only legally owned ROM/ISO files for patching
- Complying with copyright laws in their jurisdiction
- Verifying the legality of any download sources they configure
- Understanding that downloading copyrighted content without permission may be illegal
-
Third-Party Sources — Any websites or download sources referenced in configuration examples are third-party services. Users should research and evaluate the legal status of such sources independently.
-
Legal Use Only — This tool is intended exclusively for downloading legally owned content, homebrew games, or content explicitly permitted for distribution. The ROM patching features are intended solely for personal use with games you legally own.
-
No Liability — The developers and contributors of this project assume no responsibility or liability for any misuse of this software. Users bear full responsibility for how they use this tool and must ensure compliance with all applicable laws.
By using this software, you acknowledge that you understand these legal responsibilities and agree to use it only for lawful purposes.
This project incorporates the following open source libraries and projects:
- NSZ Library — NSZ compression/decompression functionality provided by nicoboss/nsz — A compression/decompression tool with fast compression and decompression for various file formats.
- Syncthing — Save synchronization powered by Syncthing — An open-source continuous file synchronization program.
- Check
error.login the application directory for detailed error information - On Batocera systems:
/userdata/roms/pygame/downloader/error.log - Development:
py_downloads/error.login the project root
| Problem | Solution |
|---|---|
| 🚫 No games showing | Verify your system configuration and network connectivity |
| ❌ Download failures | Check available disk space and directory permissions |
| 🖥️ Display issues | Ensure pygame dependencies are properly installed |
| 🖼️ Thumbnails not loading | Check that the boxarts URL is correct and accessible |
make format # 🖤 Format code with black
make lint # 🔍 Lint code with flake8
make test # 🧪 Run tests with pytest
make clean # 🧹 Clean build artifactsThis project is licensed under CC BY-NC 4.0. You're free to share, adapt, and build upon it for non-commercial purposes, with attribution. See the LICENSE file for details.
Pull requests are welcome — from humans and AI agents alike! 🤖 For major changes, please open an issue first to discuss proposed modifications.

