Skip to content
Back to top
View this page

# `pack`

Module containing `Pack` and all its subclasses.

It contains three submodules, and a sub-submodule:

1. `lihzahrd.terraria.utils.pack.pack`, with the abstract base class;

2. `lihzahrd.terraria.utils.pack.primitive`, for single units of data;

3. `lihzahrd.terraria.utils.pack.primitive.op`, for adding operators to those single units of data;

4. `lihzahrd.terraria.utils.pack.composite`, for composite data.

## `pack`

Submodule for the base `Pack` abstract class.

class lihzahrd.terraria.utils.pack.pack.Pack

  Block of adjacent data in a Terraria save file.

  Instances of classes inheriting from this one always have:

  - `read()` and `write()` methods to interact with a `FileProcessor`;

  - a `validate()` method to check that the data currently represented makes sense in the context of a Terraria save file.

  To better support future Terraria updates, or just to allow more tinkering with the save files, inheritors of `Pack` should try to allow as much leeway as it makes sense to in reading and writing values that do not pass validation.

  abstractmethod classmethod read(fp: FileProcessor, *, strict: bool = True, **kwargs: Any) → PackRead[Self]

    Read a value from the given `FileProcessor`, validate it, then use it to create an instance of this class.

    Parameters:

      - fp – The `FileProcessor` to read from.

      - strict – Whether any `ValidationError` raised should be raised again, or ignored.

      - kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Returns:

      A `PackRead` instance with the created instance and validation information.

    Raises:

      - ValidationError – If the read value is invalid and validation is `strict`.

      - ReadError – If the value could not be read.

  exception ReadError

    A failure of some kind in `read()`, preventing the object from being read.

  abstractmethod write(fp: FileProcessor, *, strict: bool = True, **kwargs: Any) → PackWrite[Self]

    Validate the value represented by this instance, then write it to the given `FileProcessor`.

    Parameters:

      - fp – The `FileProcessor` to write to.

      - strict – Whether any `ValidationError` raised should be raised again, or ignored.

      - kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Returns:

      A `PackWrite` instance with validation information.

    Raises:

      - ValidationError – If the value to write is invalid and validation is `strict`.

      - WriteError – If the value could not be written.

  exception WriteError

    A failure of some kind in `write()`, preventing the object from being written.

  abstractmethod validate(*, strict: bool = True, **kwargs: Any) → bool

    Validate the value represented by this instance.

    Note

    This method is separate from `read()` and `write()`, but can be called by them at their own discretion.

    Parameters:

      - strict – Whether any `ValidationError` encountered should be re-raised (`True`) or ignored (`False`).

      - kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Returns:

      `True` if the value is valid, `False` if the value is invalid and validation isn’t `strict`.

    Raises:

      ValidationError – If the value is invalid and validation is `strict`.

  exception ValidationError

    A failure in validation of some kind; if this occurs, it means that the data represented by this instance would not be valid in a Terraria save file.

    Tip

    You can ignore errors of this type in `read()`, `write()`, and `validate()` by specifying `strict=False` as a keyword argument!

class lihzahrd.terraria.utils.pack.pack.PackRead(instance: Pck, valid: bool)

  The result of a call to `Pack.read()`.

  The generic type `Pck` is the `Pack` instance returning this.

  Used to bubble up validity information.

  Attributes on this class are frozen.

  instance: Pck

    The created `Pack` instance representing the read value.

  valid: bool

    Whether the read value passed validation.

  __bool__() → bool
    Returns:

      Whether the read value is `valid`.

  __str__() → str
    Returns:

      The `repr()` of the `instance`, followed by either `" (ok)"` or `" (invalid)"` depending on `valid`.

class lihzahrd.terraria.utils.pack.pack.PackWrite(valid: bool)

  The result of a call to `Pack.write()`.

  The generic type `Pck` is the `Pack` instance returning this.

  Used to bubble up validity information.

  Attributes on this class are frozen.

  valid: bool

    Whether the written value passed validation.

  __bool__() → bool
    Returns:

      Whether the written value is `valid`.

  __str__() → str
    Returns:

      Either `"ok"` or `"invalid"` depending on the value of `valid`.

## `primitive`

Submodule for subclasses of `Pack` directly representing single units of data.

### `primitive`

Submodule containing the abstract base class of `Pack` primitives.

class lihzahrd.terraria.utils.pack.primitive.primitive.PackPrimitive(value: Value)

  A `Pack` representing a single unit of data of generic type `InternalValue`.

  Automatically implements logging, validation, and structured results in `__init__()`, `read()`, `write()`, and `validate()`, but still requires the inheritor to specify what should be performed in each of those methods by implementing `_read()`, `_write()`, and `_validate()`.

  Tip

  To alter the value of PackPrimitives, you can either use the methods specific to the inheriting class, like `set_from_variant()`, or directly operate on the `value` attribute, like this:

      p.value = 67

  Via this approach, you can set the value of the PackPrimitive to anything you want, including invalid values!

  value: Value

    The raw `InternalValue` represented by this instance.

  _LOG: Logger

    The `logging.Logger` to use to emit messages about the packing process.

    Note

    `logging.Logger` is not created as a global variable because in this way, logging can be filtered by section being processed instead of by class doing the processing, which arguably is more useful in debugging.

  classmethod _validate(value: Value, **kwargs: Any) → None

    Validate the given `InternalValue`, raising `ValidationError` if it’s invalid.

    Parameters:

      - value – The `InternalValue` to validate.

      - kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Raises:

      ValidationError – If `value` is not valid.

  abstractmethod classmethod _read(fp: FileProcessor, **kwargs: Any) → Value

    Read a `InternalValue` from the given `Packer`.

    Example

    The usual implementation of this method will have a body like:

        def _read(cls, fp: FileProcessor, **kwargs: Any) -> int:
            return fp.read_int()

    Parameters:

      - fp – The `FilePacker` to read from.

      - kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Returns:

      The read `InternalValue`.

    Raises:

      ReadError – If the value could not be read.

  abstractmethod classmethod _write(fp: FileProcessor, value: Value, **kwargs: Any) → None

    Write the given `InternalValue` to the given `Packer`.

    Example

    The usual implementation of this method will have a body like:

        def _write(cls, fp: FileProcessor, value: int, **kwargs: Any) -> None:
            fp.write_int(value)

    Parameters:

      - fp – The `FilePacker` to write to.

      - value – The `InternalValue` to write.

      - kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Raises:

      WriteError – If the value could not be written.

  exception ValidationError(value: Value)

    A `ValidationError` in which `InternalValue` can be stored.

    `_validate()` is expected to raise this kind of error if the value represented by this class is invalid.

    value: Value

      The value which failed validation.

### `bool`

Submodule containing processors for boolean types.

class lihzahrd.terraria.utils.pack.primitive.bool.PackBool(value: Value)

  A `PackPrimitive` processing a `BOOL` as `bool`.

class lihzahrd.terraria.utils.pack.primitive.bool.PackTrue(value: Value)

  A `PackBool` where only `True` values pass validation, and `False` values raise `IsFalseError`.

  exception IsFalseError(value: Value)

    The value is `False`.

### `int`

Submodule containing processors for integer types.

class lihzahrd.terraria.utils.pack.primitive.int.PackInteger(value: Value)

  A `PackPrimitive` processing integers.

  On validation, it ensures that the represented value is between `VALUE_MIN` and `VALUE_MAX`, and raises `OverflowError` otherwise.

  VALUE_MIN: int = NotImplemented

    The minimum value that this type of integer can take.

  VALUE_MAX: int = NotImplemented

    The maximum value that this type of integer can take.

  exception OverflowError(value: Value)

    The processed value is out of representable range.

class lihzahrd.terraria.utils.pack.primitive.int.PackSByte(value: Value)

  A `PackInteger` processing a `SBYTE` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackByte(value: Value)

  A `PackInteger` processing a `BYTE` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackShort(value: Value)

  A `PackInteger` processing a `SHORT` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackUShort(value: Value)

  A `PackInteger` processing a `USHORT` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackInt(value: Value)

  A `PackInteger` processing a `INT` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackUInt(value: Value)

  A `PackInteger` processing a `UINT` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackLong(value: Value)

  A `PackInteger` processing a `LONG` as `int`.

class lihzahrd.terraria.utils.pack.primitive.int.PackULong(value: Value)

  A `PackInteger` processing a `ULONG` as `int`.

### `float`

Submodule containing processors for floating-point numeric types.

class lihzahrd.terraria.utils.pack.primitive.float.PackFloating(value: Value)

  A `PackPrimitive` processing floating-point numeric types.

class lihzahrd.terraria.utils.pack.primitive.float.PackFloat(value: Value)

  A `PackFloating` processing a `FLOAT` as `float`.

class lihzahrd.terraria.utils.pack.primitive.float.PackDouble(value: Value)

  A `PackDouble` processing a `FLOAT` as `float`.

### `coordinates`

Submodule containing processors for `Coordinates`.

class lihzahrd.terraria.utils.pack.primitive.coordinates.PackCoordinatesInt(value: Value)

  A `PackPrimitive` processing a pair of `int` as `Coordinates`.

  exception OverflowError(value: Value)

    The bounds are not within the representable range.

### `rectangle`

Submodule containing processors for `Rectangle`.

class lihzahrd.terraria.utils.pack.primitive.rectangle.PackRectangleInt(value: Value)

  A `PackPrimitive` processing four `int` as a `Rectangle`.

  On validation, it ensures that the represented value is between `INT_MIN` and `INT_MAX`.

  exception OverflowError(value: Value)

    One of the `Rectangle` edges is not within representable range.

### `date`

Submodule containing processors for `datetime`.

class lihzahrd.terraria.utils.pack.primitive.date.PackDatetime(value: Value)

  A `PackPrimitive` processing a `datetime`.

### `bytes`

Submodule containing processors for raw bytes.

class lihzahrd.terraria.utils.pack.primitive.bytes.PackUnknown(value: Value)

  A `PackPrimitive` processing blocks of raw `bytes`.

  When this class starts a `read()`, it keeps on reading until it reaches the address specified by the `until` keyword argument.

  If instance of this class contain a non-zero amount of bytes, it will fail validation with `UnknonwnBytesError` (as that probably means that something went wrong during the parsing of the corresponding save file section), unless the `acceptable` keyword argument is `True`.

  Tip

  This behavior can be useful when you want to deliberately skip processing some sections of a save file, because for example they were loaded from a cache. This behavior can be useful when you want to deliberately skip processing some sections of a save file, because for example they were loaded from a cache.

  Parameters:

    - until – The byte offset at which to stop reading.

    - acceptable – If `True`, no `UnknownBytesError` will be raised on validation.

  exception UnknownBytesError(value: Value)

    Unknown bytes are present.

### `str`

Submodule containing processors for strings.

class lihzahrd.terraria.utils.pack.primitive.str.PackStrFixed(value: Value)

  A `PackPrimitive` processing fixed-length strings.

  On validation, it ensures that the represented value is exactly `DATA_LEN` bytes long, and raises `InvalidLengthError` otherwise.

  DATA_LEN: int = NotImplemented

    The expected length of the string.

  exception InvalidLengthError(value: Value)

    The string is not of the required length.

class lihzahrd.terraria.utils.pack.primitive.str.PackStrVariable(value: Value)

  A `PackPrimitive` processing variable-length strings.

### `uuid_`

Submodule containing processors for `UUID`.

class lihzahrd.terraria.utils.pack.primitive.uuid_.PackUUID(value: Value)

  A `PackPrimitive` processing a `UUID`.

### `enumeration`

Submodule containing processors for classes in `enum`.

class lihzahrd.terraria.utils.pack.primitive.enumeration.PackEnum(value: Value)

  A `PackPrimitive` where all the valid values are known at class definition time, and are represented by the `Enum` `ValidValue`, whose value is of the generic type `InternalValue`.

  On validation, it checks that the represented value is a valid value of `ENUM`, and raises `UnknownVariantError` otherwise.

  ENUM: type[ValidValue] = NotImplemented

    The `type` containing all the known values.

  classmethod from_variant(variant: ValidValue) → Self

    Create an instance of this class from an `Enum` variant.

    Parameters:

      variant – The variant to use to create the instance.

    Returns:

      The created instance.

  classmethod from_name(key) → Self

    Create an instance of this class from an `Enum` variant’s `name`.

    Parameters:

      key – The `name` to use to create the instance.

    Returns:

      The created instance.

  variant() → ValidValue

    Convert this instance to the corresponding `Enum` variant.

    Returns:

      The created variant.

    Raises:

      ValueError – If the value does not correspond to any enum variant.

  name() → str

    Convert this instance to the corresponding `Enum` variant `name`.

    Returns:

      The enum `name`.

  set_from_variant(variant: ValidValue) → None

    Set the value of this class instance to the value of the given variant.

    Parameters:

      variant – The variant to use to set the value.

  set_from_name(name: str) → None

    Set the value of this class instance to the value of the variant with the specified `name`.

    Parameters:

      name – The `name` to use to set the value.

  exception UnknownVariantError(value: Value)

    The specified value is not among the known values `ENUM` can take.

class lihzahrd.terraria.utils.pack.primitive.enumeration.PackFlags(value: Value)

  A `PackPrimitive` where all the valid values are known at class definition time, and are represented by the `Flag` `ValidValue`, whose value is of the generic type `InternalValue`.

  On validation, it checks that the represented value is a valid value of `FLAGS`, and raises `UnknownVariantError` otherwise.

  FLAGS: type[ValidValue] = NotImplemented

    The `type` containing all the known values.

  exception UnknownFlagsError(value: Value)

    The specified value is not among the known values `FLAGS` can take.

  classmethod from_variant(variant: ValidValue) → Self

    Create an instance of this class from a `Flag` variant.

    Parameters:

      variant – The variant to use to create the instance.

    Returns:

      The created instance.

  variant() → ValidValue

    Convert this instance to the corresponding `Flag` variant.

    Returns:

      The created variant.

    Raises:

      ValueError – If the value does not correspond to any enum variant.

  set_from_variant(variant: ValidValue) → None

    Set the value of this class instance to the value of the given variant.

    Parameters:

      variant – The variant to use to set the value.

### `op`

Module containing mixins to add various operators to classes inheriting from `PackPrimitive`.

Example

To allow instances of a class inheriting from `PackInt` to behave like regular `int` would, you can mixin `OpInteger`:

    class WorldRevision(OpInteger, PackInt):
        ...

Allowing for code like the following:

    p1: WorldRevision = WorldRevision.read(fp).instance
    p2: WorldRevision = WorldRevision.read(fp).instance
    p3: WorldRevision = p1 + p2
    assert p3.value == p1.value + p2.value

#### `equality`

Submodule for `OpEquality`.

class lihzahrd.terraria.utils.pack.primitive.op.equality.OpEquality(value: Value)

  Extension of `PackPrimitive` which adds equality operators based on `value`.

  __eq__(other) → Self
  __ne__(other) → Self

#### `boolean`

Submodule for `OpBoolean`.

class lihzahrd.terraria.utils.pack.primitive.op.boolean.OpBoolean(value: Value)

  Extension of `OpEquality` which adds boolean operators based on `value`.

  __bool__() → bool
  __and__(other: Any) → Self
  __iand__(other: Any) → None
  __or__(other: Any) → Self
  __ior__(other: Any) → None
  __xor__(other: Any) → Self
  __ixor__(other: Any) → None
  __invert__() → Self

#### `collection`

Submodule for `OpCollection`.

class lihzahrd.terraria.utils.pack.primitive.op.collection.OpCollection(value: Value)

  Extension of `OpEquality` which adds collection operators based on `value`.

  __len__() → int
  __length_hint__() → int
  __getitem__(item) → Any
  __setitem__(key, value) → None
  __delitem__(key) → None
  __iter__() → Iterator[Any]
  __reversed__() → Iterator[Any]
  __contains__(item) → bool

#### `comparison`

Submodule for `OpComparison`.

class lihzahrd.terraria.utils.pack.primitive.op.comparison.OpComparison(value: Value)

  Extension of `OpEquality` which adds comparison operators based on `value`.

  __gt__(other) → Self
  __ge__(other) → Self
  __lt__(other) → Self
  __le__(other) → Self

#### `number`

Submodule for `OpNumber`.

class lihzahrd.terraria.utils.pack.primitive.op.number.OpNumber(value: Value)

  Extension of `OpComparison` which adds number operators based on `value`.

  Note

  `__pow__()` and `__ipow__()` are not implemented due to ambiguity regarding how to handle the `modulo` parameter.

  __neg__() → Self
  __pos__() → Self
  __abs__() → Self
  __add__(other) → Self
  __iadd__(other) → None
  __sub__(other) → Self
  __isub__(other) → None
  __mul__(other) → Self
  __imul__(other) → None
  __truediv__(other) → Self
  __itruediv__(other) → None

#### `integer`

Submodule for `OpInteger`.

class lihzahrd.terraria.utils.pack.primitive.op.integer.OpInteger(value: Value)

  Extension of both `OpNumber` and `OpBoolean` which adds integer operators based on `value`.

  __int__() → int
  __index__() → int
  __floordiv__(other) → Self
  __ifloordiv__(other) → None
  __mod__(other) → Self
  __imod__(other) → None
  __lshift__(other) → Self
  __ilshift__(other) → None
  __rshift__(other) → Self
  __irshift__(other) → None

#### `floating`

Submodule for `OpFloating`.

class lihzahrd.terraria.utils.pack.primitive.op.floating.OpFloating(value: Value)

  Extension of `OpNumber` which adds floating-point operators based on `value`.

  __float__() → float
  __complex__() → complex
  __round__(ndigits=None) → Self
  __trunc__() → Self
  __floor__() → Self
  __ceil__() → Self

## `composite`

Submodule for subclasses of `Pack` representing composite sections of data, composed of multiple underlying sub- `Pack`.

### `composite`

Submodule for `PackComposite`.

class lihzahrd.terraria.utils.pack.composite.composite.PackComposite(*args: Pack, **kwargs)

  A `Pack` representing an ordered and fixed-length set of heterogenous `Pack` items.

  Automatically implements `read()`, `write()`, and `validate()` with logging, validation, and structured results, but requires the inheritor to define the fields that instances should contain, and specify the order they should be processed to via `_fields()`.

  abstractmethod __init__(*args: Pack, **kwargs)

    Create a new instance of the `PackComposite` by determining the fields it will have with the given kwargs, then by setting the value of each to the arg with the same index.

    For instances to have correct attribute suggestions in IDEs, the `__init__()` method of each `PackComposite` must be overridden.

    The override should define a new instance-level attribute for each field, annotating it with the type of the field value, and assigning to it the value `Ellipsis` (which will be ignored by `Field`).

    Warning

    Not setting the value to `Ellipsis` will result in the type annotation becoming `Any` for IDEs!

    Example

    From `FileMetadata`:

        @override
        def __init__(self, *args: Pack, **kwargs):
            self.version: PackFileVersion = ...
            self.signature: PackFileSignature = ...
            self.kind: PackFileKind = ...
            self.revision: PackFileRevision = ...
            self.flags: PackFileFlags = ...

            super().__init__(*args, **kwargs)

    Parameters:

      - kwargs – The arguments to pass to `_fields()` to determine which fields will exist on the instance.

      - args – The values to use for each field returned by `_fields()`.

  class Field(ty: type[Value])

    A descriptor for one of the `Pack` values making up the composite object.

    Necessary to allow `read()` to determine the class to call `read()` on.

    If a field is attempted to be set to `Ellipsis`, the action is ignored; this is used to make instance-level type hinting prevail over the class-level descriptor access on IDEs.

    __init__(ty: type[Value]) → None

      Create a new field.

      You should call this as if defining a class-level variable.

      The name of the field is determined via `__set_name__()`.

      Parameters:

        ty – The type of the value to store in it.

    container_name() → str
      Returns:

        The name of the variable to use to store the actual value in.

  abstractmethod classmethod _fields(**kwargs) → list[Field[Self, Pack]]

    Get a `list` of the fields that should be processed, in the order they should be processed.

    Warning

    Getters of the included properties must have a correct type annotation!

    Parameters:

      kwargs – Additional keyword arguments that may be used by implementations to do conditional processing.

    Returns:

      The `property()` that should be validated, read, or written, in the order these operations should be done.

  _LOG = <Logger lihzahrd.terraria.utils.pack.composite.composite (WARNING)>

    The `logging.Logger` to use to emit messages about the packing process.

### `count_array`

Submodule for `PackCountArray`.

class lihzahrd.terraria.utils.pack.composite.count_array.PackCountArray(items: list[Item])

  A `Pack` representing an ordered and variable-length set of homogeneous `Pack` items.

  The serialization it expects is:

  1. Item count

  2. Item #1

  3. Item #2

  4. …

  Automatically implements `read()`, `write()`, and `validate()` with logging, validation, and structured results, but it requires the inheritor to define the way to read or write the item count by implementing `_read_count()` and `_write_count()`.

  On validation, if `MAX_COUNT` is not `None`, it ensures that the collection represented by this object is not larger than `MAX_COUNT`, and raises `LimitExceededError` otherwise.

  __iter__() → Iterator
    Returns:

      An iterator of the items in the collection.

  ITEM: type[Pack]

    The `type` of items to read or write.

  abstractmethod classmethod _read_count(fp: FileProcessor) → int

    Read the amount of items to subsequently read to the collection.

    Parameters:

      fp – The `lihzahrd.terraria.utils.file_processor.FileProcessor` to read from.

    Returns:

      The number of items to read to the collection.

  abstractmethod classmethod _write_count(fp: FileProcessor, count: int) → None

    Write the amount of items to subsequently write to the collection.

    Parameters:

      - fp – The `lihzahrd.terraria.utils.file_processor.FileProcessor` to write to.à

      - count – The count of items in the collection to write.

  MAX_COUNT: int | None = None

    The maximum number of items that this collection can contain.

    Usually specified because of hardcoded limits in the Terraria save file format.

  exception LimitExceededError

    The collection has more items than how many are allowed by `MAX_COUNT`.

### `flag_array`

Submodule for `PackFlagArray`.

class lihzahrd.terraria.utils.pack.composite.flag_array.PackFlagArray(items: list[Item])

  A `Pack` representing an ordered and variable-length set of homogeneous `Pack` items.

  The serialization it expects is:

  1. Boolean `True` (“there is an item afterward”)

  2. Item #1

  3. Boolean `True` (“there is an item afterward”)

  4. Item #2

  5. Boolean `False` (“items have ended”)

  Automatically implements `read()`, `write()`, and `validate()` with logging, validation, and structured results.

  __iter__() → Iterator
    Returns:

      An iterator of the items in the collection.

  ITEM: type[Pack]

    The `type` of items to read or write.