How ZIP Export Works in Apollo PS4 for Save Files and Trophies

The Apollo PS4 ZIP export functionality uses libzip to recursively compress save directories and trophy data into portable archives through the zip_directory core routine in source/zip_util.c.

The Apollo PS4 save management tool provides robust ZIP export functionality for backing up PlayStation 4 save files and trophy data to external USB storage. This feature leverages the libzip library to create standardized compressed archives while preserving directory structures and Unix file permissions critical for PS4 compatibility.

Core ZIP Export Implementation in zip_util.c

The foundation of the ZIP export functionality resides in source/zip_util.c, specifically within the zip_directory function. This routine orchestrates archive creation through a systematic three-step process.

First, it initializes a new zip archive using zip_open(output_filename, ZIP_CREATE | ZIP_TRUNCATE, NULL). The ZIP_CREATE flag ensures the file is created if it doesn't exist, while ZIP_TRUNCATE overwrites any existing archive to prevent data corruption.

Next, the function invokes walk_zip_directory(basedir, inputdir, archive) to recursively traverse the source directory tree and populate the archive with files and subdirectories.

Finally, zip_directory closes the archive with zip_close(archive) and verifies success by checking the existence of the output file on disk.

Recursive File Handling with walk_zip_directory

The walk_zip_directory function handles the actual file system traversal and zip entry creation. It opens the input directory using opendir and iterates through each entry.

When encountering a subdirectory, the function first adds the directory entry to the zip using zip_add_dir, then recursively calls itself to process the subdirectory's contents. This ensures the complete directory hierarchy is preserved within the archive.

For regular files, walk_zip_directory creates a zip source using zip_source_file, adds the file to the archive with zip_add, and preserves Unix file permissions by calling zip_file_set_external_attributes. This metadata preservation ensures that extracted saves maintain their original permission bits when restored to the PS4 file system.

Exporting Save Files with zipSave

The zipSave function in source/exec_cmd.c provides the high-level interface for exporting individual save games to ZIP format. This function constructs the archive filename, prepares directory paths, and invokes the core zip_directory routine.

First, zipSave generates a timestamped filename following the pattern <export_path>/<title_id>-<dir_name>_<timestamp>.zip. This naming convention prevents overwrites and maintains chronological organization of backups.

Next, the function performs path manipulation to identify the base directory. It duplicates the save's absolute path and uses strrchr to strip the mount point and trailing components:

tmp = strdup(entry->path);
*strrchr(tmp, '/') = 0;   // remove trailing part
*strrchr(tmp, '/') = 0;   // remove the mount point

Finally, zipSave calls zip_directory(tmp, entry->path, zip_file) where tmp represents the parent directory containing the save folder, entry->path is the actual save directory to compress, and zip_file is the destination archive path.

Upon successful completion, the function writes a log entry to <export_path>/<user_id>.txt and generates an XML ownership file to track the backup's origin.

Exporting Trophies with exportTrophiesZip

The exportTrophiesZip function, also located in source/exec_cmd.c, handles the bulk export of trophy data using the same underlying zip_directory mechanism.

This function constructs the output filename as trophies_<user_id>.zip and identifies the source trophy directory at TROPHY_PATH_HDD (typically /user/home/<user_id>/trophy/).

Similar to zipSave, it strips the trailing slash from the trophy path to obtain the base directory:

tmp = strdup(trp_path);
*strrchr(tmp, '/') = 0;   // remove trailing slash to get base dir

The function then invokes zip_directory(tmp, trp_path, export_file) to compress the entire trophy directory tree into the destination archive.

After successful compression, exportTrophiesZip writes the ownership XML file and displays a confirmation message to the user, completing the trophy backup process.

Practical Code Examples

The following examples demonstrate how to utilize the ZIP export functionality programmatically within the Apollo PS4 codebase.

Exporting a Specific Save Game

To export a single save entry to a USB device:

/* Assume 'save' points to a valid save_entry_t and EXPORT_PATH_USB0 is defined */
zipSave(save, EXPORT_PATH_USB0);

This creates a timestamped ZIP file in the USB export folder and updates the user's export log.

Exporting All Trophies

To export the current user's trophy data:

/* Export trophies for the logged-in user to the primary USB export path */
exportTrophiesZip(EXPORT_PATH_USB0);

The resulting archive is named trophies_<user_id>.zip and contains the complete trophy directory structure.

Direct Directory Compression

For custom export scenarios, use the core zip_directory function directly:

/* Zip an arbitrary folder */
const char *base   = "/user/home/12345678";          // Parent directory
const char *target = "/user/home/12345678/savegame1"; // Folder to compress
const char *output = "/mnt/usb/savegame1_backup.zip";

int success = zip_directory(base, target, output);
if (success) {
    printf("Archive created successfully\n");
}

This low-level approach allows compression of any directory while preserving the internal path structure relative to the base directory.

Summary

  • The ZIP export functionality in Apollo PS4 relies on the zip_directory function in source/zip_util.c to create archives using the libzip library.
  • Recursive traversal via walk_zip_directory preserves directory hierarchies and Unix file permissions during compression.
  • zipSave in source/exec_cmd.c handles save game exports by generating timestamped filenames and stripping mount points from absolute paths.
  • exportTrophiesZip uses the same core routine to compress trophy data from /user/home/<user_id>/trophy/ into trophies_<user_id>.zip.
  • Both high-level functions write XML ownership files and log entries to track backup provenance and ensure data integrity.

Frequently Asked Questions

How does Apollo PS4 handle file permissions when creating ZIP archives?

Apollo PS4 preserves Unix file permissions by calling zip_file_set_external_attributes after adding each file to the archive in walk_zip_directory. This ensures that when saves are extracted back to the PS4 file system, they maintain their original permission bits, which is critical for the system to recognize and load the save data correctly.

What is the difference between zipSave and exportTrophiesZip functions?

While both functions utilize the same underlying zip_directory routine, zipSave is designed for individual save game entries and generates timestamped filenames to prevent overwrites. In contrast, exportTrophiesZip exports the entire trophy directory for a user into a single archive named trophies_<user_id>.zip without timestamps, as trophies represent cumulative data rather than discrete snapshots.

Why does the ZIP export functionality strip mount points from file paths?

The path manipulation in zipSave and exportTrophiesZip removes mount points (such as /mnt/usb/ or /user/home/) to create a relative base directory for the archive. This ensures that the ZIP file contains only the relevant save or trophy folder structure without absolute system paths, making the archives portable and compatible with the extraction logic used during import operations.

Can the zip_directory function be used for custom export scenarios?

Yes, zip_directory is a generic utility that can compress any directory tree, not just saves or trophies. Developers can call it directly with a base directory, target folder, and output filename to create archives of arbitrary PS4 file system locations, provided the application has the necessary file system permissions to read the source directory and write to the destination.

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 →