NPK Format - niemasd/PyFF7 GitHub Wiki
NPK is an archive file format and is used in some file packages in Disc 4 of Final Fantasy VII's International Edition. Files are always LZSS-compressed, but they are broken into chunks . You must first decompress an LZSS-compressed NPK archive (e.g. using lzss_decompress.py) before you can play with it. This blog post might be a useful reference for the general structure of the NPK archive (and for a fun story).
Data Blocks
The NPK file is broken into multiple blocks of exactly 1,024 bytes. Each block has the following components:
4 bytes: Number of Sub-Blocks
- This is a 4-byte unsigned integer denoting the number of remaining blocks (including this one) that are a part of the current output file
- If this is 1, this block is the last data block of the current output file
- If this is 0, the current output file is finished, and this block has no data
2 bytes: Compressed Size
- This is a 2-byte unsigned integer denoting the size (in bytes) of this block's compressed data
2 bytes: Decompressed Size
- This is a 2-byte unsigned integer denoting the size (in bytes) of this block's compressed data when it's decompressed
LZSS-Compressed Data
- All the remaining "Compressed Size" bytes are the actual compressed data
Unpacking NPK Files
Given that I've already implemented an LZSS file decompressor (lzss_decompress.py), unpacking NPK files is easy. Below is an algorithm to do so:
current_file = empty list of bytes
for each 1024 block in the NPK file:
num_subblocks = first 4 bytes of block (unsigned integer)
size_compressed = next 2 bytes of block (unsigned integer)
size_decompressed = next 2 bytes of block (unsigned integer)
if num_subblocks != 0:
block_data = the size_compressed blocks starting with the 9th byte (1-based counting)
append block_data to the end of current_file
if num_subblocks <= 1 and current_file isn't empty:
current_file = (length of current_file as a 4-byte unsigned integer) + current_file # create and add LZSS header
current_file_decompressed = lzss_decompress(current_file)