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

> Learn how Apollo PS4 ZIP export uses libzip and zip_directory to compress save files and trophies into portable archives. Understand the zip_util.c routine.

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

---

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

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

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

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

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

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