Packers
Overview
Packers are components that typically mirror unpackers, taking constituent children resources (and sometimes descendants) and reassembling them to produce a new resource.
Packers target specific resource tags against which they can run, and are typically paired with unpackers. For example, consider the ZipPacker
:
from io import BytesIO
from ofrak_type.range import Range
from ofrak.resource import Resource
from ofrak.component.packer import Packer
...
class ZipPacker(Packer[None]):
targets = (ZipArchive,)
async def pack(self, resource: Resource, config=None):
zip_view: ZipArchive = await resource.view_as(ZipArchive)
zip_entries = await zip_view.get_entries()
result = BytesIO()
with ZipFile(result, mode="w", compression=ZIP_DEFLATED) as zip_file:
for zip_entry_view in zip_entries:
zip_path_in_archive = zip_entry_view.path_in_archive
zip_entry_data = await zip_entry_view.resource.get_data()
zip_file.writestr(zip_path_in_archive, zip_entry_data)
original_zip_size = await zip_view.resource.get_data_length()
resource.queue_patch(Range(0, original_zip_size), result.getvalue())
This packer targets a ZipArchive
and performs the opposite operation of ZipUnpacker
.
Discerning OFRAK Users will notice that there are generally more unpackers than packers. The reason for this is that packers are often not needed in OFRAK when a child resource's data is mapped directly into its parent's data. For example, while there is an IElfUnpacker
in OFRAK, an ELF packer is not needed: because all of an ELF's children are mapped directly into the ELF file, modifications to them are automatically reflected in the parent object. Note, however, that packers are needed in OFRAK when there are interdependencies between the data in children resources (a common example of this is when modifying the data of a child requires updating a checksum in another part of that resource).
Usage
There are several ways in which a packer can be invoked in OFRAK.
Run Explicitly
OFRAK packers can be run directly against a resource. For example:
from ofrak.resource import Resource
from ofrak.core.zip import ZipPacker
...
resource: Resource
await resource.run(ZipPacker)
Run Automatically
Packers can also be run automatically against resources with valid resource tags. For example, consider the following code:
from ofrak.resource import Resource
from ofrak.core.zip import ZipArchive
...
resource: Resource
assert resource.has_tag(ZipArchive)
await resource.pack()
Since the resource has the ZipArchive
tag, OFRAK will run the ZipPacker
.
Recursive Packing
It is also possible to chain pack
calls together recursively using the Resource.pack_recursively
method. See Resource for more details.