Skip to content

Instantly share code, notes, and snippets.

@yifanlu

yifanlu/psv.h Secret

Last active September 18, 2022 15:37
Show Gist options
  • Save yifanlu/d546e687f751f951b1109ffc8dd8d903 to your computer and use it in GitHub Desktop.
Save yifanlu/d546e687f751f951b1109ffc8dd8d903 to your computer and use it in GitHub Desktop.
/**
* Motivation: One unified .psv format for archiving (preserving) Vita games.
* The goal is to preserve as much of the original game structure while ensuring
* the all the information needed to decrypt and extract data can be derived
* from just the file and a hacked Vita.
*
* We want something akin to .nds or .3ds/.cia or .iso but for Vita games. The
* unique challenge is that Vita cart games require a per-cart key to decrypt
* and digital games require a similar key from activation. With just the raw
* game image, it is not possible to extract the game data.
*
* What's wrong with using .vpk? VPK is designed for homebrew. The patches to
* enable homebrew strips out a lot of the game executable metadata as well as
* change the system state to be different than a Vita running an original game.
* This leads to many subtle as well as major bugs (saves not working, some
* games require additional patches to run, saves are not compatible with
* non-hacked Vitas, etc).
*
* Why not just ZIP the original files? Why not strip PFS as well to make data
* mining/emulation easy? Why not make a compressed format? One reason is that
* by stripping more than necessary (like, for example PFS), we might be losing
* information that we currently do not think is important. An example of this
* is when SNES games are first dumped and Earthbound was not dumped properly
* and people did not know about the anti-piracy checks until much later. There
* may be, for example, games that do timing checks or checks on the file
* modification time or something. Either explicitly for anti-piracy or
* implicitly due to bad programming (a lot of older consoles are infamous for
* the latter case). By preserving as much of the original structure as
* possible, we ensure that we can somehow play these games in a future where no
* more Vitas exist.
*
* Different tools (data extraction, backup loaders, archival storage, etc)
* might require different use cases. Someone might for example want to strip
* PFS and compress the game data for more efficient storage. We invite them to
* extend this format though flags BUT just as you shouldn't store all your
* photos in level-9 compressed JPEG, your code in executables, or any data you
* care about in a lossy format, you should archive your games in its original
* form. You can easily go from a RAW image to a JPEG but you cannot go back.
*/
typedef struct {
uint32_t magic; // 'PSV\0'
uint32_t version; // 0x00 = first version
uint32_t flags; // see below
uint8_t key1[0x10]; // for klicensee decryption
uint8_t key2[0x10]; // for klicensee decryption
uint8_t signature[0x14]; // same as in RIF
uint8_t hash[0x20]; // optional consistancy check. sha256 over complete data (including any trimmed bytes) if cart dump, sha256 over the pkg if digital dump.
uint64_t image_size; // if trimmed, this will be actual size
uint64_t image_offset_sector; // image (dump/pkg) offset in multiple of 512 bytes. must be > 0 if an actual image exists. == 0 if no image is included.
opt_header_t headers[]; // optional additional headers as defined by the flags
} psv_file_header_t;
#define PSV_MAGIC (0x00565350) // 'PSV\0'
#define FLAG_TRIMMED (1 << 0) // if set, the file is trimmed and 'image_size' is the actual size
#define FLAG_DIGITAL (1 << 1) // if set, RIF is present and an encrypted PKG file follows
#define FLAG_COMPRESSED (1 << 2) // undefined if set with `FLAG_TRIMMED` or `FLAG_DIGITAL`. if set, the data must start with a compression header (not currently defined)
#define FLAG_LICENSE_ONLY (FLAG_TRIMMED | FLAG_DIGITAL) // if set, the actual PKG is NOT stored and only RIF is present. 'image_size' will be size of actual package.
typedef struct {
uint32_t type; // 0x1 indicates header for digital content
uint32_t flags; // 1 == game, 2 == DLC, etc (not yet specified)
uint64_t license_size; // size of RIF that follows
uint8_t rif[]; // rif file
} digital_header_t;
typedef struct {
uint32_t type; // 0x2 indicates header for compression
uint32_t compression_algorithm; // not yet specified
uint64_t uncompressed_size;
} compression_header_t;
typedef union {
uint32_t type;
digital_header_t;
compression_header_t;
} opt_header_t;
/**
* Sample Usage 1: Game Cart Archival
* flag = 0, rif_size = 0, image_size = size of game dump, header is
* followed by raw dump of game cart
* Sample Usage 2: Save space of dump
* flag = FLAG_TRIMMED, rif_size = 0, image_size = size of game dump,
* header is followed by trimmed dump (trailing zeros are not included)
* Sample Usage 3: Digital content archival
* flag = FLAG_DIGITAL, rif_size = 0x200 (size of rif), image_size =
* size of PKG from PSN servers, header is followed by RIF followed
* by the game PKG
* Sample Usage 4: Backup of license for digital content
* flag = FLAG_DIGITAL | FLAG_TRIMMED, rif_size = 0x200, image_size =
* size of PKG from PSN servers, header is followed by RIF
**/
@devnoname120
Copy link

I've put a little tool together to help view the structure of .psv files: https://gist.github.com/devnoname120/47da5187b29863d09793a373718d421d

@yifanlu
Copy link
Author

yifanlu commented Sep 21, 2017

Proposal for an extension that allows backing up digital games in a pseudo-gamecard format (for compatibility).

What's needed is a fake exFAT image with the required files in the correct path. (If it's a digital game, it would be in app/titleid, if it's DLC, it would be in app/patch, if it's PSM it would be psm/titleid, and so on). A patched RIF shall reside in the correct license directory as well.

Header changes: key1/key2/signature shall be zero indicating pre-patched content. Maybe a new flag FLAG_PATCHED shall indicate that files have been changed. SHA hash shall still be used for integrity.

The main use-case is to have a common format for when emulators come in play.

All current NoNpDrm dumps can be converted to this "patched" format.

@frangarcj
Copy link

original psm games pkg can be retrieved from web so I prefer to store header + digital header + pkg

template:
http://zeus.dl.playstation.net/psm/np/NPPA/NPPA00010_00_D09A76B4/2.00/NPPA00010_00.pkg

NPPA = first 4 chars from titleid
D09A76B4 = FIRST 4 bytes from HMACSha1 of NPPA00010_00
2.00 = version (unknown)

@Anuskuss
Copy link

Anuskuss commented Aug 7, 2021

The Vita scene seems to be dead (just like the Vita itself), so this comment is probably worthless, but I'd like to suggest two things:

  1. CRC-32 hash in header (SHA* is overkill)
  2. FLAG_ENCRYPTED for when we get a tool (as if) that allows one to encrypt decrypted PSVs (store decrypted for compression potential but still have perfect backup)

@nitro322
Copy link

Yeah, also disappointed that PSV didn't take off. Seems like NoNpDrm solved the "I want to play all the games" problem, and support for PSV didn't mature enough with the appropriate tools to manage the files and reliably play the games, so people just stuck with the easier solution.

I found my way here trying to figure this out: is there any way to unpack or extract the contents of a PSV file on a PC? So far I've found nothing that can do that, but if anyone seeing this actually knows of a solution, I'd appreciate you leaving a comment. :-)

@LiEnby
Copy link

LiEnby commented Sep 18, 2022

D09A76B4 = FIRST 4 bytes from HMACSha1 of NPPA00010_00

? whats the HMAC Secret in this case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment