The easiest way to play a Pokemon Ruby emulator setup in 2026 is mGBA, a free, open-source Game Boy Advance emulator that runs on Windows, macOS and Linux. The current stable build is mGBA 0.10.5, and it handles Ruby’s save chip, real-time clock and sound without a BIOS file. This guide walks through 13 steps in about 55 minutes, from installing the emulator to a working launcher that backs up your save every time you play.
Ruby is a Game Boy Advance game from the Hoenn generation, and the hardware that ran it is long gone. According to AVForums, the Game Boy Advance family was discontinued globally by the end of 2010 and the last hardware revision, the Game Boy Micro, dates to September 2005, so a PC emulator is how most players revisit Hoenn today. The catch is that Ruby has a few quirks (a flash save, a clock-driven berry system and picky save-state habits) that trip up first-timers who just drag a file onto a window.
This tutorial focuses on those quirks. If you want the broader picture across every Hoenn and Kanto cartridge, our mGBA setup for Pokémon GBA games covers the shared basics. Here, everything is tuned for Ruby: header verification, save handling, the internal clock, controls and a small Python project you can keep using for years. You need your own legally obtained copy of the game, and we explain exactly what that means below.
Pokemon Ruby Emulator Basics: What Is Current in November 2026
Before you install anything, it helps to know what is actually current. Emulator guides age fast, and plenty of pages still point at old builds or at platforms that no longer matter. The table below pulls together the facts that shape this tutorial as of November 4, 2026.
| Item | Current state (Nov. 4, 2026) | What it means for you |
|---|---|---|
| mGBA stable release | 0.10.5, a bugfix release from March 2025 | Download this build, not a nightly |
| mGBA 0.11 series | Still in development, no confirmed release date | Skip development builds for your main save |
| mGBA price and license | Free, open source, Mozilla Public License 2.0 | Never pay for the emulator itself |
| Supported systems | Windows, macOS, Linux (plus Game Boy and Game Boy Color) | One emulator covers the whole handheld line |
| Newest Nintendo console | Nintendo Switch 2 | Nintendo’s modern hardware, not a GBA device |
| Ruby in GBA Nintendo Classics | Not listed in the catalog Nintendo shows | No official subscription route for Ruby today |
| Reported Ruby, Sapphire, Emerald collection | Reports claim February 2027, no official Nintendo confirmation found | Treat as a rumor until Nintendo announces it |
The version detail matters most. According to the Freetins GBA emulator roundup, mGBA 0.10.5 from March 8, 2025 is still the latest numbered stable release, and talk of 0.11 refers to development builds only. The official release notes describe 0.10.5 as a bugfix release and say it may be the last build to support Windows 7 through 8.1 and macOS 10.9 through 10.12. If you run anything that old, this is your final supported version.
Third-party packages sometimes carry labels such as 0.10.5-5. Those are repackaged builds, not new upstream releases, and they tell you nothing about 0.11. We compared mGBA against other handheld emulators in our mGBA vs WatermelonDS vs Azahar comparison, and for Ruby the verdict stays the same: mGBA is the accurate, low-friction pick.
Ruby deserves its own guide because it behaves differently from the Kanto remakes people usually start with. It stores progress on a flash chip instead of plain battery-backed memory, it ships a real-time clock that drives berries and tides, and it has no wireless adapter support to worry about. Each of those traits maps to one step below, so you fix them once instead of discovering them hours into a playthrough.
The sources we checked for this article agree on one more point. The mGBA project reads as maintained rather than abandoned: its GitHub organization was still active on November 3, 2026, and a retro-gaming profile page refreshed on November 4, 2026 still lists the emulator as active. That matters because an unmaintained emulator is a poor home for a save you plan to nurse for sixty hours.
Prerequisites: Versions, Hardware and Time Budget
You do not need a powerful machine. The Game Boy Advance was a modest handheld from the early 2000s, and mGBA emulates it at full speed on almost any laptop from the last decade. What you do need is a clean ROM, a few folders and a bit of patience for the dump step if you have never read a cartridge before.
| Requirement | Version or spec | Notes |
|---|---|---|
| mGBA | 0.10.5 stable | Free and open source, per Retro Replay’s mGBA profile |
| Operating system | Windows 10 or 11 (64-bit), a current macOS, or any Linux with Flatpak | Windows 7 to 8.1 and macOS 10.9 to 10.12 are at the end of support |
| Python | 3.9 or newer | Runs the verification and backup project |
| Pokémon Ruby cartridge | Your own copy, any region | Needed for the legal dump |
| Cartridge dumper | A GBA reader such as the GBxCart RW | Or a friend’s or library’s reader, if you own the game |
| Controller (optional) | Any XInput, DualSense or Switch Pro pad | Keyboard works fine |
| Free disk space | A few hundred MB | The ROM is 16 MiB, saves are tiny |
Plan for roughly 55 minutes if you already have a dumped ROM. The timeline below shows where the time goes, so you can pause at a natural break.
| Phase | Steps | Minutes |
|---|---|---|
| Install mGBA and create the folder layout | 1 to 3 | 10 |
| Dump and verify the ROM | 4 to 5 | 10 |
| Load the game, tune video, audio and controls | 6 to 8 | 12 |
| Saves, states and backups | 9 to 10 | 10 |
| Clock, speed tools and a final test | 11 to 13 | 13 |
Legal Ground Rules for Pokémon Ruby ROMs and Saves
The emulator is legal software. The game file is the part that needs care. mGBA’s own site states that the project is not affiliated with Nintendo, and Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Nothing in our research shows that Nintendo endorses running a Ruby ROM on a PC, and no source we checked confirms an official PC release of the game.
The safe, widely recommended route is to dump the cartridge you own. Downloading a ROM from a website is a copyright question that depends on your country, and we do not link to or recommend any ROM download site. That includes the search results that clutter the first page for this keyword. This guide assumes a personal dump and stops there.
Dumping also gives you something a download never will: the original save from your cartridge. If your old Ruby file still holds a finished Hall of Fame, a reader can pull that save off the chip and into mGBA. For players who want to keep using real hardware instead, a flash cartridge is the other legitimate path, and we cover it in our EverDrive GBA Pro Pokémon setup.
One more point on currency. Ruby is a legacy title. Nintendo’s latest hardware is Nintendo Switch 2, and its retro library for the Game Boy Advance sits inside Game Boy Advance – Nintendo Classics for Nintendo Switch Online + Expansion Pack members. Ruby is not in the catalog Nintendo lists, which is why emulation of your own copy remains the practical option. We return to the subscription route near the end.
Phase 1: Install mGBA and Build the Project Folder (Steps 1 to 3)
Step 1: Download mGBA 0.10.5 from the official site
Open mgba.io and use its downloads page. Pick the 64-bit Windows installer or portable archive, the macOS disk image, or the Linux AppImage. Linux users on a distribution with an older packaged version should prefer Flatpak, because distro repositories often lag behind upstream by a release or more. Our general mGBA Pokemon setup shows the same install choices side by side.
# Linux: install the Flatpak build
flatpak install flathub io.mgba.mGBA
# Let the sandbox see your project folder (created in Step 2)
flatpak override --user --filesystem=$HOME/ruby-kit io.mgba.mGBA
# Debian or Ubuntu alternative (version may be older)
sudo apt install mgba-qt
After installing, open Help and then About inside mGBA to confirm you see 0.10.5. If it shows a development string with a long build number, you grabbed a nightly. Replace it before you start a save you care about.
Windows users get a choice between the installer and a portable archive. The installer adds Start menu entries and file associations, while the archive runs from any folder and keeps its configuration beside the program once you add the portable.ini file mentioned in Step 3. Pick portable if you plan to carry the setup on a USB stick or keep it inside the ruby-kit folder. On macOS, drag the app into Applications and expect Gatekeeper to ask for confirmation on first launch.
Step 2: Create the ruby-kit folder layout
Keep the ROM, saves and backups in one tree. A single folder makes backups trivial and keeps the launcher script simple. Create it wherever you keep games, but avoid folders that a cloud-sync client watches (more on that in the pitfalls section).
# macOS or Linux
mkdir -p ~/ruby-kit/roms ~/ruby-kit/saves ~/ruby-kit/backups
cd ~/ruby-kit
python3 --version
# Windows PowerShell
mkdir $HOMEruby-kitroms, $HOMEruby-kitsaves, $HOMEruby-kitbackups
cd $HOMEruby-kit
py --version
You should see Python 3.9 or newer printed. Save the finished ruby_kit.py from the project section into this folder when you reach Step 5. Until then, an empty tree is fine.
Step 3: Point mGBA at your save and state folders
Open Tools, then Settings, then the paths section. Set the save game folder to ruby-kit/saves and the save state folder to a sibling you create, such as ruby-kit/states. This one change decides whether your progress sits beside the ROM, buried in an app-data directory, or somewhere you can actually back up.
Advanced users can edit the configuration file instead. Close mGBA first, because it rewrites the file on exit and will overwrite hand edits made while it runs. On Windows, a file named portable.ini placed next to the executable switches mGBA into portable mode, so the config lives beside the program.
[ports.qt]
savegamePath=/home/you/ruby-kit/saves
savestatePath=/home/you/ruby-kit/states
screenshotPath=/home/you/ruby-kit/screenshots
lockAspectRatio=1
lockIntegerScaling=1
interframeBlending=0
Forward slashes work on Windows too. If mGBA ignores a key, set the same option through the Settings window instead, since the GUI always writes the right spelling for your version.
Phase 2: Dump and Verify Your Ruby ROM (Steps 4 and 5)
Step 4: Dump the cartridge you own
A cartridge dumper is a small USB board with a GBA slot. The GBxCart RW is a common one, and the open-source FlashGBX program drives it on Windows, macOS and Linux. Plug in the reader, seat the Ruby cartridge with the power off, then use the software’s read ROM and read save functions. Name the files Pokemon - Ruby Version.gba and Pokemon - Ruby Version.sav.
Do both reads. The ROM is the game, and the save is your actual playthrough from the cartridge. Put the ROM in ruby-kit/roms and the save in ruby-kit/saves with the matching file name. mGBA looks for a save whose name matches the ROM, so the spelling must be identical apart from the extension.
Ruby came out in several regions, and the region code stored in the ROM header tells you which one you hold. Keep this table handy for Step 5.
| Header game code | Region | Language |
|---|---|---|
| AXVE | USA | English |
| AXVP | Europe | English (PAL release) |
| AXVJ | Japan | Japanese |
| AXVD | Germany | German |
| AXVF | France | French |
| AXVI | Italy | Italian |
| AXVS | Spain | Spanish |
Step 5: Verify the ROM header with a script
A bad dump is the number one reason Ruby crashes in the first hour. Every GBA ROM carries a header with the game title, a four-letter code, a fixed marker byte and a one-byte checksum. A script can check all of that in a second. The core of the check is short.
def header_info(rom):
if len(rom) < 0xC0:
raise ValueError("file is too small to be a GBA ROM")
stored = rom[0xBD]
calc = (-(sum(rom[0xA0:0xBD]) + 0x19)) & 0xFF
return {
"title": rom[0xA0:0xAC].rstrip(b"").decode("ascii", "replace"),
"code": rom[0xAC:0xB0].decode("ascii", "replace"),
"fixed_ok": rom[0xB2] == 0x96,
"revision": rom[0xBC],
"checksum_ok": stored == calc,
}
Save the full ruby_kit.py from the project section, then run the verify command from the ruby-kit folder. A healthy dump prints something like this.
$ python3 ruby_kit.py init --mgba mgba-qt
Wrote ruby_kit.json. Put your own Ruby dump at roms/Pokemon - Ruby Version.gba.
$ python3 ruby_kit.py verify
File : Pokemon - Ruby Version.gba
Title : POKEMON RUBY
Code : AXVE (USA / English)
Revision : v1.0
Size : 16777216 bytes
SHA-1 : (40 hex characters, unique to your dump)
OK: header and size look like an unmodified Pokemon Ruby dump.
If the title reads something other than POKEMON RUBY, you have a ROM hack or a different game. If the checksum fails, re-dump, because a flaky cartridge contact or a loose USB cable is the usual cause. To go further, compare the SHA-1 against a preservation database such as No-Intro using a DAT manager, and pass the known hash with --expect-sha1. The script never ships a hash list of its own, because you should trust your dump and a public database, not our file.
Phase 3: Load Ruby and Tune Video, Audio and Controls (Steps 6 to 8)
Step 6: Load the game and confirm the save type
Use File, then Load ROM, and pick your verified file. The title screen should appear within a second, with Groudon on the title screen and the music intact. mGBA ships with a built-in replacement BIOS, so Ruby runs without any BIOS file. You can add a real BIOS in settings later, but the game does not require one.
Start a throwaway new game, walk into Littleroot Town, and save through the in-game menu. Then look in your saves folder. A file named Pokemon - Ruby Version.sav should appear and its size should stay constant between saves. That behavior confirms mGBA picked the right save hardware for Ruby. If no file appears, jump to the troubleshooting list.
Pay attention to how the game starts. The opening scenes of Ruby exercise audio, input and text rendering all at once. If any of them stutters or sounds off, fix it now rather than after you have invested an evening in the game.
Step 7: Set video and audio for a crisp picture
The GBA screen is tiny, and scaling decides whether Hoenn looks sharp or smeared. Integer scaling keeps pixels square. The table lists settings that work well for Ruby on a modern monitor.
| Setting | Recommended value | Why |
|---|---|---|
| Lock integer scaling | On | Prevents uneven pixel widths |
| Lock aspect ratio | On | Keeps the original 3:2 shape |
| Interframe blending | Off | Ruby does not need it and it softens text |
| Sync to audio | On | Stops crackle and keeps speed steady |
| Renderer | OpenGL (software if you see a black window) | Software mode rescues problem drivers |
| Window scale | 4x or larger | Readable on a 1080p or 1440p display |
Optional shaders live under Tools, then Shaders. A mild LCD-grid shader mimics the original screen, while a heavy scanline preset is overkill for a game with this much bright color. Try one, play five minutes, and decide.
Step 8: Bind the controls
The GBA has two face buttons, two shoulder buttons, Start, Select and a D-pad. mGBA’s default keyboard map at the time of writing is below. Confirm it in Tools, then Settings, then Keyboard, because defaults can shift between releases.
| GBA button | Default key | Use in Ruby |
|---|---|---|
| D-pad | Arrow keys | Move and menu |
| A | X | Confirm and talk |
| B | Z | Cancel and run (hold) |
| L | A | Alternate controls option |
| R | S | Alternate controls option |
| Start | Enter | Open the main menu |
| Select | Backspace | Register an item shortcut |
For a gamepad, open the Controllers panel, click each GBA button, and press the matching control. Connect the pad before launching mGBA so it appears in the list. Hold B to run in Ruby, and consider mapping a spare trigger to fast-forward for long walks through Route 119.
Phase 4: Saves, Save States and Automatic Backups (Steps 9 and 10)
Step 9: Understand in-game saves versus save states
Ruby has two kinds of saves in an emulator, and mixing them up is how people lose hours. The in-game save writes to the cartridge’s flash chip, which mGBA stores in the .sav file. A save state snapshots the entire emulated machine, memory and all, into a separate state file. States are a convenience. The .sav file is your real record.
The default shortcuts use the function keys. Pressing a function key loads a state slot, and holding Shift with the same key saves to that slot. Verify them in the Shortcuts panel. A good habit for Ruby is to make a state before every gym leader and every Safari Zone run, but always write an in-game save at Pokémon Centers too.
Why both? A state captures a moment the game itself considers unsaved. If you load an old state after a later in-game save, the emulator restores the old memory and the next in-game save overwrites your newer file with older progress. Our Pokémon Red save check walkthrough shows the same trap on Game Boy hardware, and the rule carries over: treat states as bookmarks, not as your save.
Four file types matter in this setup. Knowing which is which makes every later decision easier.
| File | Created by | Holds | Back it up? |
|---|---|---|---|
| Pokemon – Ruby Version.gba | Your cartridge dump | The game itself | Yes, once |
| Pokemon – Ruby Version.sav | The in-game save command | Your real progress | Yes, constantly |
| Save state files | The save state shortcut | A full machine snapshot | Optional |
| ruby-YYYYMMDD-HHMMSS.sav | ruby_kit.py | A dated copy of the save | Already a backup |
Step 10: Add a one-command backup
A backup strategy for a single 128 KiB-class file should be boring and automatic. The script copies your save into a backups folder with a timestamp and keeps the newest 20 copies. The launcher runs it before and after every session, so a crash or a bad restore never costs you more than one play session.
def cmd_backup(args, cfg=None, quiet=False):
cfg = cfg or load_config()
sav = save_path(cfg)
if not sav.exists():
if not quiet:
print(f"No save yet at {sav}. Nothing to back up.")
return
dest_dir = resolve(cfg["backup_dir"])
dest_dir.mkdir(parents=True, exist_ok=True)
dest = dest_dir / f"ruby-{time.strftime('%Y%m%d-%H%M%S')}.sav"
shutil.copy2(sav, dest)
old = sorted(dest_dir.glob("ruby-*.sav"))[: -int(cfg["keep"])]
for f in old:
f.unlink()
print(f"Backed up to {dest.name} ({len(old)} old copies pruned).")
Run it by hand any time, or let the launcher do it. Expected output after your first session looks like this.
$ python3 ruby_kit.py backup
Backed up to ruby-20261104-214502.sav (0 old copies pruned).
$ python3 ruby_kit.py restore
Available backups (newest last):
ruby-20261104-214502.sav
ruby-20261104-221017.sav
$ python3 ruby_kit.py restore ruby-20261104-214502.sav
Backed up to ruby-20261104-221030.sav (0 old copies pruned).
Restored ruby-20261104-214502.sav. Close mGBA before restoring next time.
Notice that restore makes a fresh backup of the current save before it overwrites anything. That safety copy has saved more than one player from restoring the wrong file.
Phase 5: Ruby’s Clock, Speed Tools and Cheats (Steps 11 and 12)
Step 11: Check the real-time clock
Ruby is one of the Game Boy Advance games with a battery-backed real-time clock inside the cartridge. The clock drives berry growth, the tides in Shoal Cave and the day and night text. On real cartridges the clock battery eventually dies, and the game then shows a message that the internal battery has run dry. In an emulator the same message can appear if the clock hardware is not enabled for your ROM.
mGBA recognizes Ruby by its game code and turns the clock on automatically, following your computer’s time. To double-check, open Tools, then Game overrides, and confirm the real-time clock option is ticked for the loaded game. If you are playing a patched or hacked build with an unfamiliar code, set the override yourself. Plant a berry, leave the game for a few real hours, and return to see whether it grew.
Step 12: Use fast-forward, rewind and cheats with care
Holding Tab, by default, speeds up the game, which is ideal for Ruby’s long text boxes and slow overworld. Rewind, if you enable it in Settings, lets you step back a few seconds after a misclick, which costs a little memory but pays off during long training sessions. Both tools are optional, so judge them by how you like to play.
Cheats live under Tools, then Cheats, and accept GameShark and Action Replay style codes. Back up your save before you try any code. Bad codes can corrupt a Pokémon’s data in ways that only show up hours later. Our guides on Pokémon Emerald cheats in mGBA and Pokémon Fire Red cheats explain how to load and test codes safely, and the same workflow applies to Ruby with its own code lists.
Step 13: Launch Through the Wrapper and Run a Final Test
The last step ties everything together. Launch Ruby through the script so the backups run automatically. The wrapper backs up the save, starts mGBA, waits for you to quit and backs up again. Add -f for fullscreen.
$ python3 ruby_kit.py launch -f
Launching: mgba-qt -f /home/you/ruby-kit/roms/Pokemon - Ruby Version.gba
Backed up to ruby-20261104-214502.sav (0 old copies pruned).
Backed up to ruby-20261104-221017.sav (0 old copies pruned).
Run this final checklist before you trust the setup with a long playthrough.
- Start a new game and play past the truck scene into your house.
- Save in-game, then quit mGBA fully (not just minimize it).
- Check that
savesholds one.savfile andbackupsholds a timestamped copy. - Relaunch through the script and choose Continue on the title screen. Your character should stand where you saved.
- Make a save state, walk a few steps, load the state and confirm the character snaps back.
- Restart the whole process once more to be sure the backup count grows by one or two.
If every item passes, your Pokemon Ruby emulator setup is production-ready. Delete the throwaway save, or keep it as a known-good test file, and begin your real run.
Complete Working Project: ruby_kit.py
Here is the full script. It uses only the Python standard library, so there is nothing to install. Save it as ruby_kit.py inside your ruby-kit folder. Commands are init, verify, backup, restore and launch. The script was tested end to end with a synthetic ROM header and a stand-in emulator, covering a clean verify, a tampered header, repeated backups and a restore.
#!/usr/bin/env python3
"""ruby_kit.py - verify, back up, restore and launch Pokemon Ruby in mGBA."""
import argparse
import hashlib
import json
import shutil
import subprocess
import sys
import time
from pathlib import Path
KIT_DIR = Path(__file__).resolve().parent
CONFIG_FILE = KIT_DIR / "ruby_kit.json"
ROM_SIZE = 16 * 1024 * 1024
SAVE_SIZE = 128 * 1024
REGIONS = {
"AXVE": "USA / English",
"AXVP": "Europe",
"AXVJ": "Japan",
"AXVD": "Germany",
"AXVF": "France",
"AXVI": "Italy",
"AXVS": "Spain",
}
DEFAULTS = {
"mgba": "mgba-qt",
"rom": "roms/Pokemon - Ruby Version.gba",
"save_dir": "saves",
"backup_dir": "backups",
"keep": 20,
}
def load_config():
if not CONFIG_FILE.exists():
sys.exit("No ruby_kit.json yet. Run: python3 ruby_kit.py init")
cfg = dict(DEFAULTS)
cfg.update(json.loads(CONFIG_FILE.read_text()))
return cfg
def resolve(path):
p = Path(path).expanduser()
return p if p.is_absolute() else KIT_DIR / p
def header_info(rom):
if len(rom) < 0xC0:
raise ValueError("file is too small to be a GBA ROM")
stored = rom[0xBD]
calc = (-(sum(rom[0xA0:0xBD]) + 0x19)) & 0xFF
return {
"title": rom[0xA0:0xAC].rstrip(b"").decode("ascii", "replace"),
"code": rom[0xAC:0xB0].decode("ascii", "replace"),
"maker": rom[0xB0:0xB2].decode("ascii", "replace"),
"fixed_ok": rom[0xB2] == 0x96,
"revision": rom[0xBC],
"checksum_ok": stored == calc,
}
def cmd_init(args):
if CONFIG_FILE.exists() and not args.force:
sys.exit("ruby_kit.json already exists. Use --force to overwrite.")
cfg = dict(DEFAULTS)
if args.mgba:
cfg["mgba"] = args.mgba
CONFIG_FILE.write_text(json.dumps(cfg, indent=2) + "n")
for key in ("save_dir", "backup_dir"):
resolve(cfg[key]).mkdir(parents=True, exist_ok=True)
resolve(cfg["rom"]).parent.mkdir(parents=True, exist_ok=True)
print(f"Wrote {CONFIG_FILE.name}. Put your own Ruby dump at {cfg['rom']}.")
def cmd_verify(args):
cfg = load_config()
path = resolve(args.rom or cfg["rom"])
if not path.exists():
sys.exit(f"ROM not found: {path}")
data = path.read_bytes()
info = header_info(data)
problems = []
if info["title"] != "POKEMON RUBY":
problems.append(f"title is {info['title']!r}, not 'POKEMON RUBY' (hack or wrong game?)")
if info["code"] not in REGIONS:
problems.append(f"game code {info['code']!r} is not a known Ruby code")
if not info["checksum_ok"]:
problems.append("header checksum is wrong (bad dump or patched header)")
if not info["fixed_ok"]:
problems.append("fixed byte 0x96 is missing (not a valid GBA header)")
if len(data) != ROM_SIZE:
problems.append(f"size is {len(data)} bytes, expected {ROM_SIZE}")
sha1 = hashlib.sha1(data).hexdigest()
print(f"File : {path.name}")
print(f"Title : {info['title']}")
print(f"Code : {info['code']} ({REGIONS.get(info['code'], 'unknown')})")
print(f"Revision : v1.{info['revision']}")
print(f"Size : {len(data)} bytes")
print(f"SHA-1 : {sha1}")
if args.expect_sha1 and sha1 != args.expect_sha1.lower():
problems.append("SHA-1 does not match the value you passed in --expect-sha1")
if problems:
print("nProblems:")
for p in problems:
print(f" - {p}")
sys.exit(1)
print("nOK: header and size look like an unmodified Pokemon Ruby dump.")
def save_path(cfg):
return resolve(cfg["save_dir"]) / (resolve(cfg["rom"]).stem + ".sav")
def cmd_backup(args, cfg=None, quiet=False):
cfg = cfg or load_config()
sav = save_path(cfg)
if not sav.exists():
if not quiet:
print(f"No save yet at {sav}. Nothing to back up.")
return
if sav.stat().st_size != SAVE_SIZE:
print(f"Warning: save is {sav.stat().st_size} bytes, Ruby normally writes {SAVE_SIZE}.")
dest_dir = resolve(cfg["backup_dir"])
dest_dir.mkdir(parents=True, exist_ok=True)
dest = dest_dir / f"ruby-{time.strftime('%Y%m%d-%H%M%S')}.sav"
shutil.copy2(sav, dest)
old = sorted(dest_dir.glob("ruby-*.sav"))[: -int(cfg["keep"])]
for f in old:
f.unlink()
print(f"Backed up to {dest.name} ({len(old)} old copies pruned).")
def cmd_restore(args):
cfg = load_config()
backups = sorted(resolve(cfg["backup_dir"]).glob("ruby-*.sav"))
if not backups:
sys.exit("No backups found.")
if args.name is None:
print("Available backups (newest last):")
for b in backups:
print(f" {b.name}")
return
chosen = resolve(cfg["backup_dir"]) / args.name
if not chosen.exists():
sys.exit(f"Backup not found: {args.name}")
cmd_backup(args, cfg, quiet=True)
shutil.copy2(chosen, save_path(cfg))
print(f"Restored {chosen.name}. Close mGBA before restoring next time.")
def cmd_launch(args):
cfg = load_config()
cmd_backup(args, cfg, quiet=True)
exe = cfg["mgba"]
cmd = list(exe) if isinstance(exe, list) else [exe]
if args.fullscreen:
cmd.append("-f")
cmd.append(str(resolve(cfg["rom"])))
print("Launching:", " ".join(cmd))
code = subprocess.call(cmd)
cmd_backup(args, cfg, quiet=True)
sys.exit(code)
def main():
ap = argparse.ArgumentParser(description=__doc__)
sub = ap.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("init", help="create ruby_kit.json and folders")
p.add_argument("--mgba", help="path to the mGBA executable")
p.add_argument("--force", action="store_true")
p.set_defaults(fn=cmd_init)
p = sub.add_parser("verify", help="check the ROM header, size and SHA-1")
p.add_argument("--rom")
p.add_argument("--expect-sha1")
p.set_defaults(fn=cmd_verify)
p = sub.add_parser("backup", help="copy the current .sav into backups/")
p.set_defaults(fn=cmd_backup)
p = sub.add_parser("restore", help="list backups, or restore one by name")
p.add_argument("name", nargs="?")
p.set_defaults(fn=cmd_restore)
p = sub.add_parser("launch", help="back up, run mGBA, back up again on exit")
p.add_argument("-f", "--fullscreen", action="store_true")
p.set_defaults(fn=cmd_launch)
args = ap.parse_args()
args.fn(args)
if __name__ == "__main__":
main()
The folder should end up looking like the listing below. If you use Flatpak, replace the mgba value in ruby_kit.json with a list such as ["flatpak", "run", "io.mgba.mGBA"], and remember that the Flatpak build keeps its own settings under your home directory’s app folder.
ruby-kit/
ruby_kit.py
ruby_kit.json
roms/
Pokemon - Ruby Version.gba
saves/
Pokemon - Ruby Version.sav
states/
backups/
ruby-20261104-214502.sav
ruby-20261104-221017.sav
The generated ruby_kit.json is plain text, so you can edit it. Change keep to hold more backups, point save_dir at another drive, or set mgba to a full path such as the Windows executable inside your install folder.
How the script behaves, in plain terms
initwrites the config file and creates the saves, backups and roms folders, so a fresh machine needs one command.verifyreads the header, checks the title, the region code, the fixed marker byte, the checksum and the 16 MiB size, then prints the SHA-1 for comparison against a public database.backupcopies the live save into the backups folder and prunes anything beyond the newest 20 copies.restorelists backups, and with a file name it first backs up the current save, then swaps in the chosen copy.launchbacks up, starts mGBA, waits until you quit and backs up again, passing along the exit code.
Common Pitfalls to Avoid
These mistakes cause most of the lost saves and weird bugs we see in Pokemon Ruby emulator setups.
- Playing on a nightly build. Development builds can change save handling between versions. Keep your real run on 0.10.5 and test newer builds with a copy of your save.
- Trusting repackaged versions. A package labeled 0.10.5-5 is not an official release and says nothing about 0.11. Download from the project’s own site.
- Relying on save states alone. A state is a snapshot, not a save. Always write in-game saves and keep the
.savbacked up. - Putting the folder inside cloud sync. Sync clients can lock or half-upload a file while mGBA writes it. Sync the backups folder if you like, never the live saves folder.
- Loading an old state after a newer in-game save. The next in-game save writes older progress over the newer one. Back up first.
- Mixing up ROM hacks and Ruby. A hack with a different title will fail the verify step. That is a feature, not a bug, but know which game you are loading before you blame the emulator.
- Forgetting the clock. If you disable the real-time clock, berries and tides stop. Check the game overrides when something time-based breaks.
- Renaming the ROM mid-run. mGBA matches saves by file name. Rename the ROM and it will look for a new, empty save.
Troubleshooting: 10 Fixes for Pokemon Ruby on mGBA
- Black window after loading. Switch the renderer from OpenGL to the software renderer in Settings, restart mGBA and reload the ROM. This rescues many outdated graphics drivers.
- Game freezes on the intro. Re-run
ruby_kit.py verify. A failed checksum or wrong file size points to a bad dump. - No .sav file appears. Confirm the save path in Settings, save from the in-game menu (not just a state), then fully close mGBA. Some files are flushed only on exit.
- Progress vanished after relaunch. The ROM name changed, so mGBA created a new empty save. Rename the ROM back or copy the old
.savto match the new name. - Save from another emulator will not load. A file of the wrong size can confuse save detection. Use the original 128 KiB-class dump, and check Tools then Game overrides for the save type.
- Message that the internal battery has run dry. The clock hardware is not enabled. Open Game overrides and tick the real-time clock, then restart the game.
- Audio crackles or drifts. Turn on sync to audio, raise the audio buffer in Settings and close heavy background apps.
- Controller not detected on Linux. An mGBA issue filed on October 12, 2026 describes broken gamepad detection on Linux. Connect the pad before launch, try the keyboard in the meantime, and check the issue tracker for a fix.
- Blurry or uneven pixels. Enable lock integer scaling and choose a window scale that divides evenly into your screen height.
- Windows or macOS blocks the download. Confirm you downloaded from the official site. On macOS, right-click the app and choose Open the first time. Never disable security software to run an unverified file.
Advanced Tips for Power Users
Once the basics work, a few upgrades are worth your time. First, mGBA is also available as a libretro core, so you can run Ruby inside RetroArch if you want a controller-first interface, shaders in one menu and the same core on a handheld or a Steam Deck. The save format stays compatible because both front ends wrap the same emulation code.
Second, treat patches carefully. If you later want to try a fan-made hack, keep it in a separate folder with its own save directory so it cannot touch your Ruby file. Our Pokémon Unbound mGBA patching guide shows the clean way to apply a patch without overwriting your original ROM.
Third, Hoenn has side games too. The pinball spin-off includes a built-in rumble feature, and mGBA can pass it to a controller that supports vibration, as we explain in our Pokémon Pinball Ruby and Sapphire setup. Link-cable features work between two mGBA windows too, though the 0.11 series is where upstream has been reworking link and netplay behavior, and that work has no confirmed release date.
Fourth, tinkerers can script the emulator. mGBA exposes a scripting interface, and we walked through one creative use in mGBA MCP setup for an AI playing Pokémon. For ordinary players, a simpler automation is to schedule ruby_kit.py backup every hour with cron or Task Scheduler while you play.
Fifth, automate the hourly backup if you play in long sittings. The commands below register a schedule on each platform. Adjust the path to wherever you placed the folder.
# Linux or macOS: add this line with crontab -e
0 * * * * cd $HOME/ruby-kit && python3 ruby_kit.py backup
# Windows PowerShell: hourly task
schtasks /Create /SC HOURLY /TN "RubyBackup" /TR "python C:Usersyouruby-kitruby_kit.py backup"
Sixth, play on a handheld. mGBA runs on Linux, so a Steam Deck in desktop mode can install the Flatpak build and read the same folder over a network share or a memory card. Phones and tablets need a different app, and RetroArch with the mGBA core is the usual route on Android. Copy your verified ROM and your .sav file across, and keep the PC folder as the master copy.
Finally, a word on experiments. A community project called RubySapphireRecomp is reportedly an unofficial alternative to emulation. Treat it as an unverified, early-stage experiment, not a replacement for a clean mGBA setup, and do not trade your real save for it.
Official Routes and What Comes Next
Emulation is not the only way back to Hoenn. If you own a Nintendo Switch 2, Nintendo’s retro catalog for the Game Boy Advance is part of Nintendo Switch Online + Expansion Pack. Recent additions include Monster Rancher Advance, Metal Slug Advance and Kururin Paradise. Ruby is not in the list Nintendo displays, so the subscription does not solve this particular problem yet.
Reports dated October 31, 2026 say Ruby, Sapphire and Emerald could return in a new collection in February 2027. We found no official Nintendo announcement with a platform, a price or a date, so treat that as a rumor. If Nintendo confirms a rerelease, the emulator route still works for anyone who prefers their own dump, a PC and full control over the save file. Browse more retro setups in our gaming hub.
FAQ: Pokemon Ruby Emulator Questions
What is the best Pokemon Ruby emulator for PC?
mGBA is the best pick for most players. It is free, open source, supports Windows, macOS and Linux, and its current stable release is 0.10.5. It also emulates Ruby’s save chip and clock correctly without needing a BIOS.
Do I need a BIOS file to play Pokémon Ruby?
No. mGBA includes a built-in replacement BIOS that runs Ruby fine. A real BIOS is optional and only matters for a handful of edge cases, none of which affect a normal playthrough.
Is it legal to play Pokemon Ruby on an emulator?
The emulator is legal software, but the game file raises copyright issues that vary by country. The safest approach is dumping a cartridge you own. Nintendo has not endorsed ROM use on PC, and we do not link to ROM download sites.
Can I transfer my real Ruby save to mGBA?
Yes. A cartridge reader can export the save as a file. Name it to match your ROM, drop it in your saves folder and mGBA loads it as Continue on the title screen. Keep a backup of the original export.
Why does Ruby say the internal battery has run dry?
That message means the game cannot read its real-time clock. On a real cartridge the clock battery has died. In mGBA, open Tools, then Game overrides, and enable the real-time clock for the loaded game.
Is Pokémon Ruby on Nintendo Switch 2 or Nintendo Switch Online?
Not as of November 4, 2026. Ruby is not in the Game Boy Advance – Nintendo Classics catalog Nintendo lists. Reports of a February 2027 collection exist, but Nintendo has not confirmed them in anything we found.
Will mGBA 0.11 break my Ruby save?
There is no stable 0.11 release yet, and no confirmed date. When one ships, copy your backups folder first and test the new build with a duplicate save before you switch your main run.
How long does the setup take?
About 55 minutes if you already have a verified dump, including install, controls, saves, the clock check and the final test. Dumping a cartridge for the first time adds extra time depending on your reader.