"""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 mrcrowbar import models as mrc
from mrcrowbar import bits
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