How Apollo Applies Save Wizard and Bruteforce Save Data Patches to PS4 Game Saves

Apollo’s patch system loads .savepatch files containing BSD or Save Wizard syntax, converts them into structured cheat entries, and applies binary modifications through a three-stage pipeline before re-signing the save data.

Apollo is an open-source save game editor for PS4 that supports cheat formats from Save Wizard and Bruteforce Save Data (BSD). According to the bucanero/apollo-ps4 source code, the application implements a modular patch engine that parses plain-text instructions and applies them to encrypted save files while handling file wildcards, type differentiation, and cryptographic resigning.

Loading and Parsing the Patch File

When you open a save entry in Apollo, the system initiates the patch discovery phase in ReadCodes() at source/saves.c:777-792. This function constructs the path to the patch file using the save’s title_id and the APOLLO_DATA_PATH constant:

snprintf(filePath, sizeof(filePath), APOLLO_DATA_PATH "%s.savepatch", save->title_id);
if ((buffer = readTextFile(filePath)) == NULL) goto skip_end;
load_patch_code_list(buffer, save->codes, &get_file_entries, save->path);

If the file exists, load_patch_code_list() parses the text line-by-line according to BSD/Save-Wizard syntax (e.g., 80 123456 1234). For each instruction, it creates a code_entry_t structure and sets the type field to either PATCH_BSD or PATCH_GAMEGENIE as defined in include/types.h. This type flag determines which interpreter engine processes the patch during the application phase.

User Selection and Activation

After parsing, the UI layer in menu_cheats.c and menu_main.c renders each code_entry_t as a toggleable menu item. When you select a cheat, the system sets the code_entry_t.activated flag to 1. Unselected patches remain in the list but retain an activated value of 0, ensuring only user-approved modifications reach the binary stage.

Applying Patches to Save Files

When you trigger Apply changes & resign, Apollo executes apply_cheat_patches() located at source/exec_cmd.c:1524-1529. This function filters the code list for activated entries of type PATCH_GAMEGENIE, PATCH_BSD, or PATCH_PYTHON, resolves target file paths (including wildcards), and dispatches the patch:

for (node = list_head(entry->codes); (code = list_get(node)); node = list_next(node)) {
    if (!code->activated ||
       (code->type != PATCH_GAMEGENIE && code->type != PATCH_BSD && code->type != PATCH_PYTHON))
        continue;

    LOG("Active code: [%s]", code->name);
    /* Resolve filename, honouring optional wild‑cards */
    if (strchr(code->file, '*')) {
        option_value_t *opt = list_get_item(code->options[0].opts, code->options[0].sel);
        filename = opt->name;
    }
    snprintf(tmpfile, sizeof(tmpfile), "%s%s", entry->path, filename);
    if (!apply_cheat_patch_code(tmpfile, code, &orbis_host_callback)) {
        LOG("Error: failed to apply (%s)", code->name);
    }
    code->activated = 0;
}

The apply_cheat_patch_code() function (linked through the orbis_host_callback interface) performs the actual binary manipulation. It reads the target save file, interprets the address and value/mask specifications from the BSD or Save Wizard instruction, writes the modified bytes, and returns. After all active patches process, Apollo calls resignSave() to regenerate cryptographic signatures, ensuring the PS4 recognizes the edited data as valid.

SFO Parameter Patching

Before resigning, apply_sfo_patches() (referenced around exec_cmd.c:1434-1450) updates the param.sfo metadata file. This step handles account ID transfers and title ID corrections, ensuring the save ownership matches the target console profile.

Summary

  • Apollo searches for <title-id>.savepatch files in APOLLO_DATA_PATH via ReadCodes() in source/saves.c.
  • The parser load_patch_code_list() tokenizes BSD and Save Wizard syntax into code_entry_t structures with types PATCH_BSD or PATCH_GAMEGENIE.
  • The UI toggles the activated flag to mark selected patches.
  • The executor apply_cheat_patches() in source/exec_cmd.c filters by type, resolves wildcards, and invokes apply_cheat_patch_code().
  • Binary changes are applied through the cheat engine callback, followed by SFO patching and cryptographic resigning.

Frequently Asked Questions

What is the difference between PATCH_BSD and PATCH_GAMEGENIE types?

PATCH_BSD indicates a Bruteforce Save Data format instruction (hex address followed by value), while PATCH_GAMEGENIE represents Save Wizard’s encrypted code pattern. The apply_cheat_patch_code() function uses these type flags from include/types.h to select the appropriate decoder ring when interpreting the patch bytes.

How does Apollo handle patch files with wildcards in the filename?

If code->file contains an asterisk (*), Apollo treats it as a wildcard selector. It extracts the selected option from code->options[0].sel, retrieves the concrete filename from the option_value_t list, and concatenates it with entry->path before calling apply_cheat_patch_code().

Does Apollo modify the param.sfo file when applying patches?

Yes. Before apply_cheat_patches() runs, Apollo invokes apply_sfo_patches() to modify param.sfo metadata. This updates account IDs and title information to match the destination user profile, which is required for the PS4 to recognize the save as belonging to the signed-in account.

Where does Apollo look for .savepatch files?

Apollo constructs the path using snprintf() with the format APOLLO_DATA_PATH "%s.savepatch" where %s is the save’s title_id. This typically resolves to a directory like /data/apollo/ or similar configured path, followed by the game ID and .savepatch extension.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →