autoangel

AutoAngel is a general-purpose library designed to make it easy to work with angelica engine game files.

It supports the following file formats:

Quick Start

Working with elements.data

Let's start with importing autoangel:

import autoangel

Now you can load any elements.data you want using read_elements:

data = autoangel.read_elements('/path/to/elements.data')

By default read_elements will try to use one of the bundled config. This should work for you, if you don't use exotic game version (otherwise, you should load config with read_elements_config and pass it manually).

You can inspect data:

print(f'Version: {data.version}')
print(f'Number of lists: {len(data)}')

You can explore all the entries among all the lists. For example:

weapons_list = data[3]

# print list name
print(f'List: {weapons_list.config.caption}')

# print first 10 entries in list
for i in range(10):
  weapon = weapons_list[i]
  print(f'ID: {weapon.ID}, name: {weapon.Name}')

You can also modify anything you want like changing durability of all the weapons:

for weapon in weapons_list:
  weapon.durability_min = weapon.durability_max = 99999

All the modifications won't affect original elements.data until you save it:

data.save('elements2.data')

Working with pck/pkx

You can load a pck package using read_pck:

import autoangel

# Load a single pck file
package = autoangel.read_pck('/path/to/package.pck')

# Load a pck file with its corresponding pkx file
package = autoangel.read_pck('/path/to/package.pck', '/path/to/package.pkx')

Once you have a PckPackage object, you can explore its contents:

# Get a list of all files in the package
file_list = package.file_list()
print(f'Number of files: {len(file_list)}')

# Find files with a specific prefix
textures = package.find_prefix('textures/')
print(f'Number of texture files: {len(textures)}')

# Get the content of a specific file
file_content = package.get_file('path/to/file.txt')
if file_content is not None:
    print(f'File content: {file_content.decode("utf-8")}')
else:
    print('File not found')
1from .autoangel import *
2
3__doc__ = autoangel.__doc__
4if hasattr(autoangel, "__all__"):
5    __all__ = autoangel.__all__
class ElementsConfig:

Configuration for elements.data.

Attributes:
  • lists: elements.data lists configs
  • name: name of config specified when parsing it (i.e. file name)

List configs.

name: str | None

Config name.

class ElementsData:

Object describing parsed elements.data. It also works as immutable array of data lists (ElementsDataList).

Implements basic list interface such as len(..) and __getitem__.

Attributes:
  • version: elements.data version

See also: ElementsDataList

def find_entry( self, id: int, space_id: str | None = None, allow_unknown: bool = True) -> ElementsDataEntry | None:

Find entry by ID and space ID.

Parameters
  • id: Entry ID
  • space_id: Optional space ID (None by default). If None, find among all the lists, otherwise - only in lists with specified space_id.
  • allow_unknown: If set, include lists with "unknown" space_id in search, otherwise - ignore them (True by default).
Returns

Found entry or None

def save(self, path: str) -> None:

Saves elements.data to file at path.

Parameters
  • path: Path to output file
Raises
  • Exception: If any I/O error occurs
def save_bytes(self) -> bytes:

Saves elements.data to byte array.

Returns

Serialized elements.data

version: int

elements.data version.

class ElementsDataEntry:

Single data entry.

Implements basic dict[str, object]-like and object-like interface such as len(..), __getattr__, __setattr__, __getitem__, __setitem__ and __contains__.

Number of fields, its names and types depend on elements.data version and list config. Use entry.keys() to get field names.

One can access fields with either entry['name'] (by key) or entry.name (as an attribute) syntax.

Each field can be read and modified and has one of the following types:

  • int
  • float
  • str
  • bytes

See also: ElementsListConfig

def copy(self) -> ElementsDataEntry:

Get deep copy of self.

Returns

Deep copy of entry

def keys(self) -> List[str]:

Get entry field names

Returns

Field names

class ElementsDataList:

Contains data of specific list in elements.data such as list info (like ElementsListConfig) and all data entries (ElementsDataEntry). Implements basic list interface such as len(..), __getitem__ and __setitem__.

Attributes:
  • config: list config

See also: ElementsDataEntry.

def append(self, entry: ElementsDataEntry) -> None:

Append entry at the end of list.

Parameters
  • entry: New entry
Raises
  • ValueError: If entry comes from a list with a different config

List config.

class ElementsListConfig:

Configuration for a list in elements.data.

Attributes:
  • caption: list caption
  • data_type: list data type
  • fields: array of fields
  • offset: list offset
  • space_id: list space id (may be "unknown")
caption: str

List caption.

data_type: int

List data type.

Array of fields.

offset: int | Literal['AUTO']

List offset.

space_id: str

List space ID.

class ElementsListConfigArray:

Array of list configurations.

class ElementsMetaField:

Field metadata.

Attributes:
  • name: field name
  • type: field type
name: str

Field name.

type: str

Field type.

class ElementsMetaFieldArray:

Array of field metadata.

class PackageConfig:

Configuration for pck package encryption keys and guard values.

PackageConfig( key1: int = 2828235874, key2: int = 1496793649, guard1: int = 4261281518, guard2: int = 4027432687)

Create a new PackageConfig with optional custom values.

Parameters
  • key1: First key value.
  • key2: Second key value.
  • guard1: First guard value.
  • guard2: Second guard value.
guard1: int

First guard value.

guard2: int

Second guard value.

key1: int

First key value.

key2: int

Second key value.

class PckPackage:

Object describing parsed pck package.

def file_list(self) -> List[str]:

Returns list of file paths in package.

Returns

All paths in package.

def find_prefix(self, prefix: str) -> List[str]:

Finds all files in archive with path prefixed by prefix.

Parameters
  • prefix: Path prefix.
Returns

All paths in package prefixed by prefix or empty list if no files were found.

def get_file(self, path: str) -> bytes | None:

Get file content by its path.

Parameters
  • path: Path to file inside package.
Returns

None if file not found. Otherwise, returns file content.

def save(self, path: str, config: PackageConfig | None = None) -> None:

Save the package to a file.

This method saves the package to a file at the specified path. The saved package will be identical to the original when loaded back.

Parameters
  • path: Path where to save the package.
  • config: Custom package configuration. Defaults to None.
Raises
  • Exception: If any I/O error occurs during saving.
def read_elements( elements_path: str, config: ElementsConfig | None = None) -> ElementsData:

Parses elements.data from file elements_path and returns ElementsData. Doesn't load file content into memory, uses memory-mapped I/O - so file cannot be modified while ElementsData is alive.

Parameters
  • elements_path: Path to elements.data.
  • config: Optional config describing elements.data structure (None by default). If no config specified, one of predefined will be used.
Returns

Object describing parsed elements.data.

Raises
  • Exception: If any I/O error occurs, elements.data has invalid internal structure or config has incompatible version.
def read_elements_bytes( content: bytes, config: ElementsConfig | None = None) -> ElementsData:

Parses elements.data from byte array content and returns ElementsData.

Parameters
  • content: Content of elements.data.
  • config: Optional config describing elements.data structure (None by default). If no config specified, one of predefined will be used.
Returns

Object describing parsed elements.data.

Raises
  • Exception: If elements.data has invalid internal structure or config has incompatible version.
def read_elements_config(path: str) -> ElementsConfig:

Parses elements.data config from file at path path and returns ElementsConfig describing parsed file.

Parameters
  • path: Path to elements config.
Returns

Object describing parsed config.

Raises
  • Exception: If any I/O error occurs or config has invalid internal structure.
def read_elements_config_string(content: str) -> ElementsConfig:

Parses elements.data config from string content and returns ElementsConfig describing parsed file.

Parameters
  • content: String containing elements config.
Returns

Object describing parsed config.

Raises
  • Exception: If config has invalid internal structure.
def read_pck( pck_path: str, pkx_path: str | None = None, config: PackageConfig | None = None) -> PckPackage:

Parses pck package from file at path pck_path (and optionally pkx_path) and returns PckPackage describing parsed file(s). Doesn't load file content into memory, uses memory-mapped I/O - so file(s) cannot be modified while PckPackage is alive.

Parameters
  • pck_path: Path to pck package.
  • pkx_path: Optional path to pkx package (None by default).
  • config: Custom package configuration. Defaults to None.
Returns

Object describing parsed package.

Raises
  • Exception: If any I/O error occurs or package has invalid internal structure.
def read_pck_bytes( content: bytes, config: PackageConfig | None = None) -> PckPackage:

Parses package from byte array content and returns PckPackage.

Parameters
  • content: Content of package.
  • config: Custom package configuration. Defaults to None.
Returns

Object describing parsed package.

Raises
  • Exception: If package has invalid internal structure.