Skip to content
RetrogamingHow-To Published Updated 6 min readViews unavailable

ScummVM Save Migration: Target Identity, Engine Formats, and Round-Trip Tests

Migrate ScummVM saves across devices with explicit target identity, version boundaries, file manifests, isolated save paths, and functional round-trip checks.

Moving a ScummVM save is more than copying a file to a familiar folder. The destination must find that file, associate it with the intended game, understand its engine-specific format, and restore a playable state. These are separate checks. A file can arrive with an identical hash and still remain absent from the load menu because the configured target or save path differs.

ScummVM reimplements many game engines. It does not provide one universal snapshot format for all titles. The procedure below combines documented launcher and path behavior with an isolated validation workflow. It avoids assuming that every save can move between editions, every original game’s save is importable, or a save written by a newer build is readable by an older one.

Inventory the game before touching its saves

Record the installed ScummVM version, game edition, language, original platform, data-file location, and configured target ID. The official command-line guide distinguishes a supported game ID from a target, which is the configuration entry created when a game is added. Adding the same game twice produces separate targets that can carry different settings.

On a desktop installation with ScummVM on the executable search path, these read-only commands help establish that inventory:

scummvm --version
scummvm --list-targets

Use the executable inside the macOS application bundle or the installed Windows path when the command is not available directly. Do not mistake a shell’s missing-command error for a ScummVM configuration problem.

Keep a copy of the relevant configuration entry alongside the backup, but redact unrelated credentials or paths before sharing it. A target’s identity can matter to save naming in an engine. That is an implementation-specific relationship to verify, not a promise that renaming every save prefix converts it to another game’s edition.

Resolve the effective save path

The Paths settings expose a savepath value globally and for individual games. Inspect the game-specific setting as well as the global setting; a custom value may explain why files copied to the default directory do not appear. Keep game data paths and save paths separate in the inventory.

The documented default on current macOS builds is ~/Library/Application Support/ScummVM/Savegames/; the guide notes that versions before 2.9 used ~/Documents/ScummVM Savegames/. Linux follows XDG data-directory rules, and a Snap installation has a package-specific location. Windows uses %APPDATA%\ScummVM\Saved games. These defaults are discovery aids, not a substitute for reading the destination’s actual configuration.

Avoid making the first migration attempt into a live folder containing newer progress. Create an empty validation directory and configure the destination game to use it through the Paths tab. Confirm that a new disposable save made by that game appears in the expected directory. This proves the path selection and write access before historical saves are introduced.

Make a stable backup and a manifest

Quit ScummVM on the source device before copying the save directory. This minimizes the chance of reading a file while an autosave or manual save is being written. If cloud synchronization is configured, account for it before testing: a background sync can change files independently of the copy operation.

The following standard-library Python example creates a read-only manifest of regular files under a selected save directory. Replace the path explicitly. It skips symlinks so the inventory does not accidentally traverse an unrelated tree:

from pathlib import Path
import hashlib
import json

root = Path('/absolute/path/to/copied-save-directory')
records = []
for path in sorted(root.rglob('*')):
    if path.is_symlink() or not path.is_file():
        continue
    digest = hashlib.sha256()
    with path.open('rb') as stream:
        for block in iter(lambda: stream.read(1024 * 1024), b''):
            digest.update(block)
    records.append({
        'path': path.relative_to(root).as_posix(),
        'bytes': path.stat().st_size,
        'sha256': digest.hexdigest(),
    })
print(json.dumps(records, indent=2))

Capture the output outside the save directory so the manifest does not inventory itself on a second run. Compare source-backup and destination manifests by relative path, length, and hash. Modification timestamps alone are inadequate transfer checks: an archive extractor, cloud service, or filesystem can change them without changing the bytes.

Treat compatibility as an engine contract

The SCUMM save/load implementation illustrates why format compatibility needs code or release evidence. It contains a save version, conditional deserialization paths, and conversions for historical state layouts. Those conversions demonstrate intentional compatibility work inside that engine; they do not establish an unlimited guarantee for all game engines or all future versions.

Some games must use their original in-game save/load interface instead of the Global Main Menu. The official guide explicitly notes this distinction. An unavailable GMM load button is therefore not enough to diagnose a missing or invalid save. First verify the supported load interface for the game and the location where that interface expects its files.

Keep the source game’s exact data variant when possible. A floppy edition, voiced release, localized edition, demo, and platform port can have different resources or script state. If changing editions is part of the migration, require explicit documentation or a controlled test for that pair. Do not infer cross-edition compatibility because both entries have the same visible title.

Test a complete restore cycle

Place copied saves in the isolated destination directory, launch the matching game, and attempt the documented load operation. Record whether the save is listed, whether metadata can be read, whether the load succeeds, and whether the game continues normally. These observations identify different failure layers.

After loading, perform a short but meaningful sequence: inspect inventory, enter another room if appropriate, trigger an interaction, and verify dialogue or audio relevant to that scene. Then save to a new slot, quit ScummVM completely, relaunch, and load the new slot. The fresh-process reload matters because an in-session load can accidentally benefit from resources or state already held in memory.

A useful acceptance record includes the original save’s hash, the producing build if known, the destination build, game variant, target, path, and the actions tested. Label results as a test of that save and environment. A single successful slot cannot prove that every game, slot, puzzle branch, or later scene is compatible.

Diagnose failures in layers

If a save is absent from the list, inspect path selection, target naming, permissions, and the game’s supported load interface first. If the file is listed but rejected, preserve the error message and investigate the engine format or game-variant boundary. If it loads but fails later, compare the same actions on the original environment before attributing the result to the transfer.

When a new build writes a new save, retain it separately from the historical copy. Do not overwrite the only old slot while experimenting with a downgrade. Successful forward loading does not prove reverse compatibility: the older program may never have implemented the newer layout. Rollback should restore the old executable, configuration, and untouched save set together.

ScummVM’s cloud integration can synchronize saves on launch, after saving, and when loading, and it is not supported on every platform. Treat it as a transport mechanism with its own timing, not as compatibility validation. Re-enable it only after the isolated local test succeeds and after preserving both devices’ pre-migration progress.

The migration is complete when byte identity, game identity, effective paths, functional restoration, and a fresh-process round trip have all been recorded. Keeping those checks separate makes a missing save diagnosable and prevents a successful file copy from being mistaken for a verified continuation of the game.

Related:

Sources:

Comments