#!/usr/bin/python3
"""File format classes for the Secret Agent engine (DOS, 1992)
Sources:
ProGraphx tileset format
http://www.shikadi.net/moddingwiki/ProGraphx_Toolbox_tileset_format
Secret Agent encryption
http://www.shikadi.net/moddingwiki/Secret_Agent_encryption
"""
from __future__ import annotations
from mrcrowbar import bits
from mrcrowbar import models as mrc
from mrcrowbar.lib.hardware import ibm_pc
from mrcrowbar.lib.images import base as img
[docs]class SAMEncryption( mrc.Transform ):
KEY = b"Copyright 1991 Peder Jungck\x00"
def __init__( self, length=None ):
self.length = length
[docs] def import_data( self, buffer, parent=None ):
limit = len( buffer ) if not self.length else min( len( buffer ), self.length )
payload = bytes(
[
bits.reverse_bits( c ) ^ self.KEY[i % len( self.KEY )]
for i, c in enumerate( buffer[:limit] )
]
)
return mrc.TransformResult( payload=payload, end_offset=limit )
[docs] def export_data( self, buffer, parent=None ):
payload = bytes(
[
bits.reverse_bits( c ^ self.KEY[i % len( self.KEY )] )
for i, c in enumerate( buffer )
]
)
return mrc.TransformResult( payload=payload )
[docs]class SAMTileset16( mrc.Block ):
count = mrc.UInt8( 0x00 )
width_raw = mrc.UInt8( 0x01 )
height = mrc.UInt8( 0x02 )
mask_data = mrc.Bytes(
0x03,
length=0x1f7d,
transform=img.Planarizer(
bpp=5,
plane_size=mrc.Ref( "plane_size" ),
row_planar_size=1,
plane_order=(0,),
),
)
image_data = mrc.Bytes(
0x03,
length=0x1f7d,
transform=img.Planarizer(
bpp=5,
plane_size=mrc.Ref( "plane_size" ),
row_planar_size=1,
plane_order=(1, 2, 3, 4),
),
)
def __init__( self, *args, **kwargs ):
super().__init__( *args, **kwargs )
self.image = img.IndexedImage(
self,
width=mrc.Ref( "width" ),
height=mrc.Ref( "height" ),
source=mrc.Ref( "image_data" ),
frame_count=mrc.Ref( "count" ),
palette=ibm_pc.EGA_DEFAULT_PALETTE,
mask=mrc.Ref( "mask_data" ),
)
@property
def plane_size( self ):
return self.count * self.width_raw * self.height
@property
def width( self ):
return self.width_raw * 8
[docs]class SAMGfx16( mrc.Block ):
tilesets = mrc.BlockField(
SAMTileset16, 0x00, transform=SAMEncryption( length=0x1f80 )
)
[docs]class SAMTileset8( mrc.Block ):
count = mrc.UInt8( 0x00 )
width_raw = mrc.UInt8( 0x01 )
height = mrc.UInt8( 0x02 )
mask_data = mrc.Bytes(
0x03,
length=0x7fd,
transform=img.Planarizer(
bpp=5,
plane_size=mrc.Ref( "plane_size" ),
row_planar_size=1,
plane_order=(0,),
),
)
image_data = mrc.Bytes(
0x03,
length=0x7fd,
transform=img.Planarizer(
bpp=5,
plane_size=mrc.Ref( "plane_size" ),
row_planar_size=1,
plane_order=(1, 2, 3, 4),
),
)
def __init__( self, *args, **kwargs ):
super().__init__( *args, **kwargs )
self.image = img.IndexedImage(
self,
width=mrc.Ref( "width" ),
height=mrc.Ref( "height" ),
source=mrc.Ref( "image_data" ),
frame_count=mrc.Ref( "count" ),
palette=ibm_pc.EGA_DEFAULT_PALETTE,
)
self.mask = img.IndexedImage(
self,
width=mrc.Ref( "width" ),
height=mrc.Ref( "height" ),
source=mrc.Ref( "mask_data" ),
frame_count=mrc.Ref( "count" ),
palette=ibm_pc.EGA_DEFAULT_PALETTE,
)
@property
def plane_size( self ):
return self.count * self.width_raw * self.height
@property
def width( self ):
return self.width_raw * 8
[docs]class SAMGfx8( mrc.Block ):
tilesets = mrc.BlockField(
SAMTileset8, 0x00, transform=SAMEncryption( length=0x800 )
)
[docs]class Loader( mrc.Loader ):
_SEP = mrc.Loader._SEP
_SAM_FILE_CLASS_MAP = {
_SEP + "(SAM)([1-3]).(APO|CRD|END|TTL)$": None,
_SEP + "(SAM)([1-3])0(1).(GFX)$": SAMGfx16,
_SEP + "(SAM)([1-3])0(2).(GFX)$": SAMGfx8,
_SEP + "(SAM)([1-3])0(3).(GFX)$": None,
_SEP + "(SAM)([1-3])0([1-3])E.(SND)$": None,
}
def __init__( self ):
super().__init__( self._SAM_FILE_CLASS_MAP )
[docs] def post_load( self ):
# for key, obj in file_map.items():
# pass
pass