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

> Explore the Apollo PS4 owner.xml file structure. Learn how Apollo parses this XML to manage PlayStation 4 account credentials for backup and restore.

- Repository: [Damián Parrino/apollo-ps4](https://github.com/bucanero/apollo-ps4)
- Tags: deep-dive
- Published: 2026-02-26

---

**Apollo uses a hierarchical XML file called [`owner.xml`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/source/owner_xml.c) file of the [bucanero/apollo-ps4](https://github.com/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:

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

```

This root node, generated at lines 95-99 of [`source/owner_xml.c`](https://github.com/bucanero/apollo-ps4/blob/main/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:

```xml
<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`](https://github.com/bucanero/apollo-ps4/blob/main/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:

```c
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:

```c
#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`](https://github.com/bucanero/apollo-ps4/blob/main/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:

```c
#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`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/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.