cpk

Support for CRI CPK archives, used by many CRI Middleware based games.

A CPK archive is a container indexed by one or more tables (TOC, ETOC, ITOC, GTOC) serialized in the CRI @UTF table format. Files can optionally be compressed with the CRILAYLA scheme and the @UTF packets themselves can be encrypted with a simple XOR scheme. Based on CriPakTools.

class hacktools.cpk.CPK[source]

Structure of a CPK archive.

filetable

List of file and header entries in the archive.

data

Extra data associated with the archive, not used by this module.

align

Alignment of file data in the archive.

getFileEntry(filename, filetype='', tocname='')[source]

Search the file table for an entry with the given filename.

Parameters:
  • filename (str) – Name of the entry to search for.

  • filetype (str) – If not empty, only match entries with this filetype.

  • tocname (str) – If not empty, only match entries with this tocname.

Returns:

The first matching entry, or None if not found.

Return type:

CPKFileEntry | None

getIDEntry(id, filetype='', tocname='')[source]

Search the file table for an entry with the given ID.

Parameters:
  • id (int) – ID of the entry to search for.

  • filetype (str) – If not empty, only match entries with this filetype.

  • tocname (str) – If not empty, only match entries with this tocname.

Returns:

The first matching entry, or None if not found.

Return type:

CPKFileEntry | None

getEntries(filetype)[source]

Get all the file table entries with the given filetype.

Parameters:

filetype (str) – Filetype to search for, usually “FILE”.

Returns:

The list of matching entries.

Return type:

list[CPKFileEntry]

class hacktools.cpk.CPKFileEntry[source]

Structure of a single entry in a CPK file table.

Both actual files and internal headers (CPK, TOC, ETOC, ITOC, GTOC) are represented as entries, distinguished by their filetype.

dirname

Directory name of the file.

filename

Name of the file.

filesize

Size of the file as stored in the archive.

filesizepos

Position of the filesize value in the @UTF packet.

filesizetype

Data type of the filesize value, as a UTFStructTypes.

fileoffset

Absolute offset of the file data in the archive.

fileoffsetpos

Position of the fileoffset value in the @UTF packet.

fileoffsettype

Data type of the fileoffset value, as a UTFStructTypes.

extractsize

Size of the file after decompression, equal to filesize if the file is not compressed.

extractsizepos

Position of the extractsize value in the @UTF packet.

extractsizetype

Data type of the extractsize value, as a UTFStructTypes.

offset

Base offset that was added to fileoffset when reading the entry.

id

Numeric ID of the file.

userstring

Optional user string attached to the file.

updatetime

Update timestamp read from the ETOC.

localdir

Local directory read from the ETOC.

tocname

Name of the TOC the entry belongs to (“TOC”, “ITOC” or “CPK”).

encrypted

Whether the @UTF packet of this entry is encrypted.

filetype

Type of the entry (“FILE”, “CPK”, “HDR” or “CONTENT”).

utf

Parsed @UTF table for header entries, None otherwise.

classmethod createEntry(filename, fileoffset, fileoffsettype, fileoffsetpos, tocname, filetype, encrypted)[source]

Create an entry with the given attributes.

Parameters:
  • filename (str) – Name of the file.

  • fileoffset (int) – Absolute offset of the file data in the archive.

  • fileoffsettype (int) – Data type of the fileoffset value, as a UTFStructTypes.

  • fileoffsetpos (int) – Position of the fileoffset value in the @UTF packet.

  • tocname (str) – Name of the TOC the entry belongs to.

  • filetype (str) – Type of the entry.

  • encrypted (bool) – Whether the @UTF packet of this entry is encrypted.

Returns:

The new entry.

Return type:

CPKFileEntry

getFolderFile(basefolder)[source]

Get the extraction folder and filename for this entry.

The folder is basefolder plus the entry dirname, if any. If the entry has no filename, an “ID” name is generated from the entry ID.

Parameters:

basefolder (str) – Base folder the path should start with.

Returns:

A (folder, filename) tuple.

Return type:

tuple[str, str]

class hacktools.cpk.UTFStructTypes(*values)[source]

Data types of the values stored in a @UTF table.

class hacktools.cpk.UTF[source]

Structure of a @UTF table, a set of typed columns and rows.

columns

List of columns in the table.

rows

List of rows, each row being a list of one UTFRow per column.

tablesize

Size of the table.

rowsoffset

Absolute offset of the rows data.

stringsoffset

Absolute offset of the strings data.

dataoffset

Absolute offset of the binary data.

tablename

Offset of the table name in the strings data.

numcolumns

Number of columns in the table.

rowlength

Length in bytes of a single row.

numrows

Number of rows in the table.

columnlookup

Lookup dictionary from column name to column index.

baseoffset

Base offset passed to readUTF().

rawpacket

Stream holding the decrypted @UTF packet, only set when requested with storeraw.

datalpos

Position of the DataL value in the packet, used by ITOC tables.

datahpos

Position of the DataH value in the packet, used by ITOC tables.

utfdatal

Nested table parsed from the DataL value, used by ITOC tables.

utfdatah

Nested table parsed from the DataH value, used by ITOC tables.

getColumnData(row, name, type)[source]

Get the value of a column, with a default if it’s missing.

Like getColumnDataType() but if the column is missing or empty, a default is returned instead of None: the maximum unsigned value for integer types, or 0 for the other types.

Parameters:
  • row (int) – Index of the row to read from.

  • name (str) – Name of the column.

  • type (int) – Expected data type, as a UTFStructTypes, used to pick the default value.

Returns:

A (data, position) tuple.

Return type:

tuple[int | float | str | bytes, int]

getColumnDataType(row, name)[source]

Get the value, position and type of a column.

Constant column values are shared by all rows, otherwise the value is read from the given row.

Parameters:
  • row (int) – Index of the row to read from.

  • name (str) – Name of the column.

Returns:

A (data, position, type) tuple with the type as a UTFStructTypes, or (None, 0, DATA_TYPE_NONE) if the column is missing or holds no data.

Return type:

tuple[int | float | str | bytes | None, int, int]

updateColumnDataType(data, pos, type)[source]

Write a new integer value in the raw packet of this table.

The position and type are usually the ones previously returned by getColumnDataType(). Only integer types are supported.

Parameters:
  • data (int) – New value to write.

  • pos (int) – Position to write the value at.

  • type (int) – Data type of the value, as a UTFStructTypes.

Return type:

None

class hacktools.cpk.UTFColumnFlags(*values)[source]

Flags of a @UTF column, holding the storage type and data type.

class hacktools.cpk.UTFColumn[source]

Structure of a single column in a @UTF table.

flags

Raw flags of the column, as a UTFColumnFlags mask.

name

Name of the column.

data

Value of the column if it’s a constant one, None otherwise.

storagetype

Storage type of the column, as a UTFColumnFlags.

type

Data type of the column if it’s a constant one, as a UTFStructTypes, DATA_TYPE_NONE otherwise.

position

Position of the column value if it’s a constant one.

class hacktools.cpk.UTFRow[source]

Structure of a single row value in a @UTF table.

data

Value of the row, None if the column stores no data.

type

Data type of the value, as a UTFStructTypes.

position

Position of the value in the packet, for per-row storage.

hacktools.cpk.extract(file, outfolder, guessextension=None)[source]

Extract all the files of a CPK archive to a folder.

Files compressed with CRILAYLA are decompressed. Files without a name are extracted with a generated “ID” name.

Parameters:
  • file (str) – Path of the CPK archive.

  • outfolder (str) – Path of the folder to extract to.

  • guessextension (Callable[[bytes, CPKFileEntry, str], str] | None) – Optional function receiving the file data, the CPKFileEntry and the current filename, returning the filename to use.

Return type:

None

hacktools.cpk.repack(file, outfile, infolder, outfolder, nocmp=False)[source]

Repack a CPK archive, replacing the files found in a folder.

Files that exist in outfolder are read from there and compressed if they were compressed in the original archive, while the others are copied from the original archive. Since CRILAYLA compression is slow, compressed data is cached next to the input file in a .cache file tagged with the input CRC, and reused as long as the input doesn’t change. The TOC and ITOC tables are updated with the new offsets and sizes.

Parameters:
  • file (str) – Path of the original CPK archive.

  • outfile (str) – Path of the output CPK archive.

  • infolder (str) – Path of the folder the archive was extracted to, used to look up the original filenames and extensions.

  • outfolder (str) – Path of the folder containing the replacement files.

  • nocmp (bool) – Whether to store the replacement files uncompressed.

Return type:

None

hacktools.cpk.readCPK(file)[source]

Read the header and file table of a CPK archive.

All the TOC tables present in the archive (TOC, ETOC, ITOC, GTOC) are parsed, adding their header and file entries to the archive filetable.

Parameters:

file (str) – Path of the CPK archive.

Returns:

The parsed archive structure, or None if the file is not a valid CPK archive.

Return type:

CPK | None

hacktools.cpk.readTOC(f, cpk, tocoffset, contentoffset)[source]

Read a TOC table, adding a file entry for each row.

Parameters:
  • f (Stream) – Stream opened on the CPK archive.

  • cpk (CPK) – Archive structure holding a TOC_HDR entry, updated in place.

  • tocoffset (int) – Offset of the TOC table.

  • contentoffset (int) – Offset of the archive content, used to compute the base offset of the file entries.

Return type:

None

hacktools.cpk.readETOC(f, cpk, tocoffset)[source]

Read an ETOC table, updating the file entries with their local directory and update time.

Parameters:
  • f (Stream) – Stream opened on the CPK archive.

  • cpk (CPK) – Archive structure holding an ETOC_HDR entry, updated in place.

  • tocoffset (int) – Offset of the ETOC table.

Return type:

None

hacktools.cpk.readITOC(f, cpk, tocoffset, contentoffset, align)[source]

Read an ITOC table, holding files indexed by ID instead of name.

File sizes are read from the nested DataL and DataH tables, and a file entry is added for each ID found. If the table has no nested data, the IDs are assigned to the file entries already in the archive filetable.

Parameters:
  • f (Stream) – Stream opened on the CPK archive.

  • cpk (CPK) – Archive structure holding an ITOC_HDR entry, updated in place.

  • tocoffset (int) – Offset of the ITOC table.

  • contentoffset (int) – Offset of the archive content, where file data starts.

  • align (int) – Alignment of the file data, used to compute the file offsets.

Return type:

None

hacktools.cpk.readGTOC(f, cpk, tocoffset)[source]

Read a GTOC table, only checking the header and updating the GTOC_HDR entry, since the table content is currently ignored.

Parameters:
  • f (Stream) – Stream opened on the CPK archive.

  • cpk (CPK) – Archive structure holding a GTOC_HDR entry, updated in place.

  • tocoffset (int) – Offset of the GTOC table.

Return type:

None

hacktools.cpk.readUTFData(f)[source]

Read a @UTF packet from the current stream position.

The packet is decrypted if needed and returned as a new big endian stream. The stream endianness is restored to little endian afterwards.

Parameters:

f (Stream) – Stream opened on the CPK archive, positioned before a @UTF packet.

Returns:

A (packet, size, encrypted) tuple with the packet as a new stream, its size, and whether it was encrypted in the archive.

Return type:

tuple[Stream, int, bool]

hacktools.cpk.decryptUTF(input)[source]

Decrypt a @UTF packet with the standard XOR scheme.

Since this is a simple XOR, calling it on a decrypted packet encrypts it again.

Parameters:

input (bytes) – Encrypted packet data.

Returns:

The decrypted packet data.

Return type:

bytes

hacktools.cpk.readUTF(f, baseoffset, storeraw=False)[source]

Read a @UTF table with all its columns and rows.

Parameters:
  • f (Stream) – Stream holding the decrypted @UTF packet, usually returned by readUTFData().

  • baseoffset (int) – Offset of the packet in the archive, stored in the table for later use.

  • storeraw (bool) – Whether to store the packet stream in the rawpacket attribute of the table, so it can be edited later with UTF.updateColumnDataType().

Returns:

The parsed table, or None if the packet has a wrong header.

Return type:

UTF | None

hacktools.cpk.readUTFTypedData(f, utf, flags)[source]

Read a single value from a @UTF table.

String and bytearray values are read from the strings and data sections of the table, following the offset at the current position.

Parameters:
  • f (Stream) – Stream holding the decrypted @UTF packet, positioned on the value.

  • utf (UTF) – Table the value belongs to.

  • flags (int) – Column flags, masked with TYPE_MASK to get the data type.

Returns:

A (data, type) tuple with the type as a UTFStructTypes.

Return type:

tuple[int | float | str | bytes, int]