Apollo PS4 owner.xml File Structure and Parsing: A Complete Guide

Apollo uses a hierarchical XML file called owner.xml to store PlayStation 4 account credentials, parsing it into parallel arrays to populate the UI for backup and restore operations.

The owner.xml file serves as the central registry for user account data in the Apollo save tool for PS4. Located in the source/owner_xml.c file of the bucanero/apollo-ps4 repository, this XML format encapsulates console identifiers, user IDs, and PSN account IDs that enable Apollo to associate save data with specific users.

owner.xml File Structure and Schema

The XML schema follows a strict three-level hierarchy defined in save_xml_owner(). The file supports multiple <owner> blocks to accommodate consoles with several registered users.

Root Element and Metadata

The document root is an <apollo> element carrying platform and version metadata:

<apollo platform="PS4" version="1.0.0">
    <!-- owner entries -->
</apollo>

This root node, generated at lines 95-99 of source/owner_xml.c, identifies the file as a PS4 Apollo configuration and enables future versioning if the schema evolves.

Owner, Console, and User Nodes

Each registered user appears as an <owner> element containing nested <console> and <user> tags:

<owner name="PlayerOne">
    <console idps="" psid="1234567890ABCDEF 1122334455667788"/>
    <user id="00000001" account_id="0123456789ABCDEF"/>
</owner>
  • <owner name="..."> (lines 100-103): Stores the console owner’s display name as a text attribute.
  • <console idps="" psid="..."/> (lines 104-108): Contains the idps attribute (reserved, currently empty) and the psid attribute formatted as two concatenated 64-bit hexadecimal values representing the console’s unique identifier.
  • <user id="..." account_id="..."/> (lines 109-115): Holds the numeric local user ID (id) and the 64-bit hexadecimal PSN account_id that uniquely identifies the PlayStation Network account.

How Apollo Parses owner.xml for Account Management

Apollo reads owner.xml through the get_xml_owners() function (lines 14-73), which transforms the XML hierarchy into C arrays suitable for menu rendering.

The get_xml_owners() Function

The parser uses the Mini-XML (mxml) library to traverse the document:

int get_xml_owners(const char *xmlfile, int cmd, char*** nam, char*** val)

Parameters:

  • xmlfile: Path to the XML file (typically "owner.xml").
  • cmd: A command character prefix (e.g., 'B' for backup, 'R' for restore) prepended to account IDs.
  • nam: Output pointer for the display names array.
  • val: Output pointer for the command values array.

Return value: The total count of entries including a default "Auto-generated" option.

Building the Account Selection Arrays

The function constructs two parallel arrays that power Apollo’s account selection menus:

  1. First pass (lines 39-45): Walks all <owner> nodes using mxmlFindElement and counts entries containing a valid <user> with an account_id attribute.

  2. Allocation (lines 47-51): Allocates *nam and *val arrays, seeding index 0 with a fallback entry: "Auto-generated Account ID" with value "%c0" (where %c expands to the command prefix).

  3. Second pass (lines 57-63): For each valid owner, extracts:

    • name attribute via mxmlElementGetAttr(node, "name")
    • account_id via mxmlElementGetAttr(value, "account_id")

    Formats them using asprintf:

    • Display name: "%s (%s)""PlayerOne (0123456789ABCDEF)"
    • Command token: "%c%s""B0123456789ABCDEF"

If mxmlLoadFile fails (lines 21-35), the function returns only the default entry to ensure the UI remains functional even with missing configuration.

Writing owner.xml with save_xml_owner()

The save_xml_owner() function (lines 84-124) generates the XML structure programmatically:

#include <mxml.h>

void create_owner_xml(const char* filename, const char* owner_name, 
                      const char* psid, const char* user_id, 
                      const char* account_id) {
    FILE *fp = fopen(filename, "w");
    mxml_node_t *xml = mxmlNewXML("1.0");
    
    // Root element with platform metadata
    mxml_node_t *root = mxmlNewElement(xml, "apollo");
    mxmlElementSetAttr(root, "platform", "PS4");
    mxmlElementSetAttr(root, "version", "1.0.0");
    
    // Owner container
    mxml_node_t *owner = mxmlNewElement(root, "owner");
    mxmlElementSetAttr(owner, "name", owner_name);
    
    // Hardware identifiers
    mxml_node_t *console = mxmlNewElement(owner, "console");
    mxmlElementSetAttr(console, "idps", "");
    mxmlElementSetAttr(console, "psid", psid);
    
    // Account identifiers
    mxml_node_t *user = mxmlNewElement(owner, "user");
    mxmlElementSetAttr(user, "id", user_id);
    mxmlElementSetAttr(user, "account_id", account_id);
    
    mxmlSaveFile(xml, fp, &xml_whitespace_cb);
    fclose(fp);
    mxmlDelete(xml);
}

This implementation mirrors the actual serialization logic in source/owner_xml.c, using mxmlElementSetAttr to populate attributes and mxmlSaveFile to emit formatted XML with proper indentation via the xml_whitespace_cb callback.

Practical Usage Example

To retrieve account lists for populating a menu:

#include "owner_xml.h"

char **display_names = NULL;
char **command_values = NULL;

// Load accounts with 'B' prefix for backup operations
int count = get_xml_owners("/data/apollo/owner.xml", 'B', 
                           &display_names, &command_values);

for (int i = 0; i < count; i++) {
    printf("Option %d: %s -> %s\n", i, 
           display_names[i], command_values[i]);
    // Output: "Option 0: Auto-generated Account ID -> B0"
    // Output: "Option 1: PlayerOne (0123456789ABCDEF) -> B0123456789ABCDEF"
}

// Cleanup
for (int i = 0; i < count; i++) {
    free(display_names[i]);
    free(command_values[i]);
}
free(display_names);
free(command_values);

The command_values array entries are passed directly to Apollo’s underlying PS4 SDK functions to target specific account IDs during save data operations.

Summary

  • Schema: owner.xml uses a root <apollo> element containing <owner> nodes with nested <console> (PSID) and <user> (account ID) elements.
  • Parsing: get_xml_owners() in source/owner_xml.c uses Mini-XML to extract data into parallel nam (display) and val (command) arrays.
  • Resilience: The parser always returns at least one default entry to prevent UI failures when the XML file is missing.
  • Command Prefix: The cmd parameter allows the same parsing logic to generate tokens for different operations (backup, restore, export) by prefixing the raw account ID.
  • Multi-user Support: The file structure supports multiple <owner> blocks, enabling consoles with several registered PSN accounts to manage saves for each user independently.

Frequently Asked Questions

What happens if owner.xml is missing or corrupted?

If mxmlLoadFile fails to read the file, get_xml_owners() creates a single default entry labeled "Auto-generated Account ID" with the value "%c0" (where %c is the command prefix). This ensures Apollo remains operational and can generate account IDs on-the-fly rather than crashing or presenting an empty menu.

Can owner.xml contain multiple PlayStation accounts?

Yes. The XML schema supports multiple <owner> blocks, each with its own <user> child containing a distinct account_id. During parsing, Apollo counts all valid owner/user combinations and allocates arrays accordingly, allowing users to select between different PSN accounts when managing save data.

What is the command prefix (cmd) parameter used for?

The cmd character (typically 'B' for backup, 'R' for restore, or 'E' for export) prefixes the raw hexadecimal account ID in the val array. This creates distinct command tokens like "B0123456789ABCDEF" that Apollo’s menu system passes to handler functions, enabling the same parsing logic to serve different operational modes without replicating code.

Where does Apollo store the owner.xml file?

While the get_xml_owners() function accepts any path via its xmlfile parameter, Apollo typically stores owner.xml in the application data directory (/data/apollo/ on the PS4 filesystem). The file is generated automatically when users configure their account settings through the Apollo UI or when save_xml_owner() is invoked during initial setup.

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 →