Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ notify.txt
src/notify_icon_asset.c
refs/*
docs/*
.DS_Store
/zfs_implementation*
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
PS5_PAYLOAD_SDK ?= /opt/ps5-payload-sdk

# Allow utility-only targets to run without the PS5 SDK toolchain present.
ifneq ($(filter-out clean makezfs,$(MAKECMDGOALS)),)
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
endif

VERSION_TAG := $(shell git describe --abbrev=6 --dirty --always --tags 2>/dev/null || echo unknown)

Expand All @@ -21,6 +25,15 @@ HEADERS := $(wildcard include/*.h)
# Targets
all: shadowmountplus.elf

.PHONY: makezfs
makezfs:
@if [ -z "$(SOURCE)" ]; then \
echo "Usage: make makezfs SOURCE=<game_root_dir> [OUTPUT=<image.ffzfs>] [ZFS_COMPRESSION=lz4]"; \
exit 1; \
fi
ZFS_COMPRESSION="$(if $(ZFS_COMPRESSION),$(ZFS_COMPRESSION),lz4)" \
./makezfs.sh "$(SOURCE)" "$(if $(OUTPUT),$(OUTPUT),download0.ffzfs)"

# Build Daemon
shadowmountplus.elf: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
Expand Down
44 changes: 40 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@

## Current image support

`PFS support is experimental.`
`PFS and ZFS support are experimental.`

| Extension | Mounted FS | Attach backend | Status |
| --- | --- | --- | --- |
| `.ffpkg` | `ufs` | `LVD` or `MD` (configurable) | Recommended |
| `.exfat` | `exfatfs` | `LVD` or `MD` (configurable) | Compatibility / external-drive-only titles |
| `.ffpfs` | `pfs` | `LVD` | Experimental |
| `.ffzfs` | `zfs` | `LVD` or `MD` (configurable) | Experimental - File compression support |

Notes:
- Backend, read-only mode, and sector size can be configured via `/data/shadowmount/config.ini`.
- Debug logging is enabled by default (`debug=1`) and writes to console plus `/data/shadowmount/debug.log` (set `debug=0` to disable).
- **UFS (`.ffpkg`) is the recommended image format for normal use.**
- **Use exFAT (`.exfat`) only for titles that need external-drive-style compatibility.**
- **ZFS (`.ffzfs`) is experimental; use only if you wanto so benefit from the file compression from ZFS.**
- **When building exFAT images manually, keep the cluster size at `64 KB`; smaller clusters can reduce performance.**

## Recommended FS choice
Expand All @@ -52,6 +54,7 @@ Supported keys (all optional):
- `stability_wait_seconds=<0..3600>` (minimum source age before processing; default: `10`)
- `exfat_backend=lvd|md` (default: `lvd`)
- `ufs_backend=lvd|md` (default: `lvd`)
- `zfs_backend=lvd|md` (default: `lvd`)
- `backport_fakelib=1|0` (`1` mounts sandbox `fakelib` overlays for running games; default: `1`)
- `kstuff_game_auto_toggle=1|0` (`1` pauses kstuff after tracked game launches and resumes it on stop; default: `1`)
- `kstuff_crash_detection=1|0` (`1` enables the early post-auto-pause crash heuristic and autotune updates; default: `1`)
Expand All @@ -66,9 +69,11 @@ Supported keys (all optional):
- `scanpath=<absolute_path>` (can be repeated on multiple lines; default: built-in scan path list below)
- `lvd_exfat_sector_size=<value>` (default: `512`)
- `lvd_ufs_sector_size=<value>` (default: `4096`)
- `lvd_zfs_sector_size=<value>` (default: `4096`)
- `lvd_pfs_sector_size=<value>` (default: `32768`)
- `md_exfat_sector_size=<value>` (default: `512`)
- `md_ufs_sector_size=<value>` (default: `512`)
- `md_zfs_sector_size=<value>` (default: `512`)

Per-image mode override behavior:
- Match is done by image file name (without path).
Expand Down Expand Up @@ -137,7 +142,7 @@ Image mountpoints are created under:

`/mnt/shadowmnt/<image_name>_<hash>`

Image layout requirement (`.ffpkg`, `.exfat`, `.ffpfs`):
Image layout requirement (`.ffpkg`, `.exfat`, `.ffpfs`, `.ffzfs`):
- Game files must be placed at the image root.
- Do not add an extra top-level folder inside the image.
- Valid example: `/sce_sys/param.json` exists directly from image root.
Expand Down Expand Up @@ -237,6 +242,37 @@ Windows:
- `UFS2Tool.exe newfs -O 2 -b 65536 -f 65536 -m 0 -S 4096 -i 262144 -D ./APPXXXX ./PPSA12345.ffpkg`
- For manual builds, use `-i 262144` as the baseline and lower it for images with many small files.

## Creating a ZFS image (`.ffzfs`)

ZFS support is experimental and intended for advanced users who need ZFS tooling/compression.

Linux:
- Script: `makezfs.sh`
- Usage: `sudo ./makezfs.sh <game_root_dir> [output_file]`
- Example:
- `chmod +x makezfs.sh`
- `sudo ./makezfs.sh ./APPXXXX ./PPSA12345.ffzfs`
- Requirements:
- `zpool`, `zfs`, `losetup`, `truncate`, `rsync`
- Notes:
- Source folder must be the game root and contain `eboot.bin`.
- Recommended/default compression is `lz4` for speed and compatibility.
- You can override compression via env var, for example:
- `sudo ZFS_COMPRESSION=zstd ./makezfs.sh ./APPXXXX ./PPSA12345.ffzfs`

macOS:
- Script: `makezfs.sh`
- Usage: `sudo ./makezfs.sh <game_root_dir> [output_file]`
- Requirements:
- OpenZFS tools installed and available in PATH (`zpool`, `zfs`)
- `hdiutil`, `mkfile`, `rsync`
- Notes:
- The script uses `hdiutil` raw attachment for image-backed pool creation.

Windows:
- Recommended path is WSL with OpenZFS tooling and then run `makezfs.sh` from WSL.
- Native Windows ZFS/image workflow is not bundled yet.


## Installation and usage

Expand Down Expand Up @@ -274,9 +310,9 @@ If a game is not mounted:
- If logs show `source not stable yet`, adjust `stability_wait_seconds` (or wait for source copy/write to finish).
- Verify game structure:
- folder game: `<GAME_DIR>/sce_sys/param.json`;
- image game (`.ffpkg` / `.exfat` / `.ffpfs`): `sce_sys/param.json` must be at image root (no extra top-level folder).
- image game (`.ffpkg` / `.exfat` / `.ffpfs` / `.ffzfs`): `sce_sys/param.json` must be at image root (no extra top-level folder).
- If you see `missing/invalid param.json` for an image, check via FTP that files are present under `/mnt/shadowmnt/<image_name>_<hash>/` and include `sce_sys/param.json`.
- If you see image mount failure, check image integrity and filesystem type (`.ffpkg`=UFS, `.exfat`=exFAT, `.ffpfs`=PFS).
- If you see image mount failure, check image integrity and filesystem type (`.ffpkg`=UFS, `.exfat`=exFAT, `.ffpfs`=PFS, `.ffzfs`=ZFS).
- If you see duplicate titleId notification, keep only one source per `<TITLE_ID>`.

If a game is mounted but does not start:
Expand Down
3 changes: 3 additions & 0 deletions config.ini.example
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ stability_wait_seconds=10
# md -> /dev/mdctl -> /dev/mdN
exfat_backend=lvd
ufs_backend=lvd
zfs_backend=lvd

# Sandbox fakelib backport watcher:
# 1/true/yes/on -> mount /mnt/sandbox/<TITLE_ID>_XXX/app0/fakelib into common/lib
Expand Down Expand Up @@ -85,9 +86,11 @@ kstuff_pause_delay_direct_seconds=10
# Backend-specific sector size overrides:
lvd_exfat_sector_size=512
lvd_ufs_sector_size=4096
lvd_zfs_sector_size=4096
lvd_pfs_sector_size=32768
md_exfat_sector_size=512
md_ufs_sector_size=512
md_zfs_sector_size=512

# Optional custom scan roots (can be repeated):
# If at least one scanpath=... is present, only these paths are used.
Expand Down
5 changes: 5 additions & 0 deletions include/sm_mount_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#define EXFAT_ATTACH_USE_MDCTL 0
// 1 = allow mounting .ffpkg images via /dev/mdctl, 0 = keep UFS on LVD.
#define UFS_ATTACH_USE_MDCTL 0
// 1 = allow mounting .ffzfs images via /dev/mdctl, 0 = keep ZFS on LVD.
#define ZFS_ATTACH_USE_MDCTL 0

// --- LVD Definitions ---
// Kernel exposes base attach (V0), extended attach (V1/Attach2) and detach.
Expand Down Expand Up @@ -35,16 +37,19 @@
#
#define LVD_SECTOR_SIZE_EXFAT 512u
#define LVD_SECTOR_SIZE_UFS 4096u
#define LVD_SECTOR_SIZE_ZFS 4096u
#define LVD_SECTOR_SIZE_PFS 4096u
#define LVD_SECONDARY_UNIT_SINGLE_IMAGE 0x10000u
#define MD_SECTOR_SIZE_EXFAT 512u
#define MD_SECTOR_SIZE_UFS 512u
#define MD_SECTOR_SIZE_ZFS 512u
// Raw option bits are normalized by sceFsLvdAttachCommon before validation:
// raw:0x1->norm:0x08, raw:0x2->norm:0x80, raw:0x4->norm:0x02, raw:0x8->norm:0x10.
// The normalized masks are then checked against validator constraints (0x82/0x92).
#define LVD_ATTACH_IMAGE_TYPE_SINGLE 0
#define LVD_ATTACH_IMAGE_TYPE_UFS_DOWNLOAD_DATA 7
#define LVD_ATTACH_IMAGE_TYPE_PFS_SAVE_DATA 5
#define LVD_ATTACH_IMAGE_TYPE_ZFS LVD_ATTACH_IMAGE_TYPE_SINGLE
#define LVD_ATTACH_LAYER_COUNT 1
#define LVD_ATTACH_LAYER_ARRAY_SIZE 3
#define LVD_ENTRY_TYPE_FILE 1
Expand Down
4 changes: 4 additions & 0 deletions include/sm_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,22 @@ typedef struct runtime_config {
uint32_t kstuff_pause_delay_direct_seconds;
attach_backend_t exfat_backend;
attach_backend_t ufs_backend;
attach_backend_t zfs_backend;
uint32_t lvd_sector_exfat;
uint32_t lvd_sector_ufs;
uint32_t lvd_sector_zfs;
uint32_t lvd_sector_pfs;
uint32_t md_sector_exfat;
uint32_t md_sector_ufs;
uint32_t md_sector_zfs;
} runtime_config_t;

typedef enum {
IMAGE_FS_UNKNOWN = 0,
IMAGE_FS_UFS,
IMAGE_FS_EXFAT,
IMAGE_FS_PFS,
IMAGE_FS_ZFS,
} image_fs_type_t;

typedef struct sm_error {
Expand Down
Loading
Loading