XP3 archives

An XP3 file begins with an 11-byte magic number, followed by an 8-byte offset from the start of the header (usually the start of the file), info_offset. All numbers are little-endian.

offset length contents notes
0x00 11 magic XP3\x0D\x0A\x20\x0A\x1A\x8B\x67\x01
0x0B 8 info_offset  
0x13 4 version 1 = XP3v2; absent in older files
0x17 8 table_size present only if version field is present
0x1F 1 flags present only if version field is present
0x?? ?? file_data  
info_offset 17 + compressed_size file_info  

At info_offset there are two possibilities. The first byte may be 0x80 (in case of a file generated by KiriKiri Z), in which case skip 8 bytes and read the true info offset. Jump there and continue.

info_offset + x length contents notes
0x00 1 flags 0x80 bit is set; marks KiriKiriZ compatibility
0x01 8 table_size size of the file table
0x09 8 info_offset true info offset

Otherwise, the first byte should be 0x01:

info_offset + x length contents notes
0x00 1 compressed whether the table is compressed
0x01 8 compressed_size  
0x09 8 original_size  
0x11 compressed_size file_table if compressed, decompress with zlib (LZ77 + Huffman coding)

The end of the file_table should be the end of the file.

File table structure

The decompressed file table is a sequence of tagged chunks. Each chunk begins
with a 4-byte magic tag and an 8-byte length field indicating the size of the
chunk's contents.

offset length contents notes
0x00 4 tag e.g. File, eliF, …
0x04 8 length  
0x0C length data  

The table consists of eliF ("File" in little-endian, 0x656C6946) entries
paired with File entries.

eliF chunk

Contains a UTF-16LE encoded filename and a key used to associate it with its
corresponding File entry.

File chunk

Contains sub-chunks: info, segm, adlr, and time.

  • info: a flags variable, compressed size, decompressed size, and what
    appears to be an MD5 hash of the file.
  • segm: one or more segments, each containing an offset to the file's data
    in the archive. Segment data is also compressed with LZ77 and Huffman coding
    (zlib).
  • adlr: contains the key used to match this File entry to its eliF
    entry. The key is also used in decryption.
  • time: a Unix timestamp for the file's creation date.

Encryption

Some XP3 archives (e.g. Nekopara) encrypt file contents. Encryption is
symmetric XOR. A base key is derived by XORing the game's master key with the
file key from the adlr chunk. A one-byte key is then derived by XORing each
byte of the base key together. For some games, the least-significant byte of
the base key is used to encrypt the first byte of the file, with the derived
one-byte key used for subsequent bytes. Games have default fallback values for
keys that are too simple.

Because most binary files begin with a known magic number, a known-plaintext
attack against the first byte is sufficient to recover the encryption key.