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
**/
@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