from mrcrowbar import models as mrc
from mrcrowbar.lib.images import base as img
from mrcrowbar.lib.audio import base as aud
from mrcrowbar.lib.containers import riff
from mrcrowbar import utils
from enum import IntEnum
DIRECTOR_PALETTE_RAW = '000000111111222222444444555555777777888888aaaaaa'\
'bbbbbbddddddeeeeee000011000022000044000055000077'\
'0000880000aa0000bb0000dd0000ee001100002200004400'\
'00550000770000880000aa0000bb0000dd0000ee00110000'\
'220000440000550000770000880000aa0000bb0000dd0000'\
'ee00000000330000660000990000cc0000ff003300003333'\
'0033660033990033cc0033ff006600006633006666006699'\
'0066cc0066ff0099000099330099660099990099cc0099ff'\
'00cc0000cc3300cc6600cc9900cccc00ccff00ff0000ff33'\
'00ff6600ff9900ffcc00ffff330000330033330066330099'\
'3300cc3300ff3333003333333333663333993333cc3333ff'\
'3366003366333366663366993366cc3366ff339900339933'\
'3399663399993399cc3399ff33cc0033cc3333cc6633cc99'\
'33cccc33ccff33ff0033ff3333ff6633ff9933ffcc33ffff'\
'6600006600336600666600996600cc6600ff663300663333'\
'6633666633996633cc6633ff666600666633666666666699'\
'6666cc6666ff6699006699336699666699996699cc6699ff'\
'66cc0066cc3366cc6666cc9966cccc66ccff66ff0066ff33'\
'66ff6666ff9966ffcc66ffff990000990033990066990099'\
'9900cc9900ff9933009933339933669933999933cc9933ff'\
'9966009966339966669966999966cc9966ff999900999933'\
'9999669999999999cc9999ff99cc0099cc3399cc6699cc99'\
'99cccc99ccff99ff0099ff3399ff6699ff9999ffcc99ffff'\
'cc0000cc0033cc0066cc0099cc00cccc00ffcc3300cc3333'\
'cc3366cc3399cc33cccc33ffcc6600cc6633cc6666cc6699'\
'cc66cccc66ffcc9900cc9933cc9966cc9999cc99cccc99ff'\
'cccc00cccc33cccc66cccc99ccccccccccffccff00ccff33'\
'ccff66ccff99ccffccccffffff0000ff0033ff0066ff0099'\
'ff00ccff00ffff3300ff3333ff3366ff3399ff33ccff33ff'\
'ff6600ff6633ff6666ff6699ff66ccff66ffff9900ff9933'\
'ff9966ff9999ff99ccff99ffffcc00ffcc33ffcc66ffcc99'\
'ffccccffccffffff00ffff33ffff66ffff99ffffccffffff'
DIRECTOR_PALETTE = [p for p in reversed( img.from_palette_bytes( bytes.fromhex( DIRECTOR_PALETTE_RAW ), stride=3, order=(0, 1, 2) ) )]
[docs]class ChannelCompressor( mrc.Transform ):
[docs] def import_data( self, buffer, parent=None ):
result = bytearray( 50*25 )
pointer = 0
while pointer < len( buffer ):
size = utils.from_uint16_be( buffer[pointer:pointer+2] )
offset = utils.from_uint16_be( buffer[pointer+2:pointer+4] )
result[offset:offset+size] = buffer[pointer+4:pointer+4+size]
return mrc.TransformResult( payload=result, end_offset=pointer )
[docs]class ChannelV4( mrc.Block ):
channel_size = mrc.UInt16_BE( 0x00 )
channel_offset = mrc.UInt16_BE( 0x02 )
data = mrc.Bytes( 0x04, length=mrc.Ref( 'channel_size' ) )
@property
def channel_row( self ):
return self.channel_offset // 0x14
@property
def channel_type( self ):
return self.channel_offset % 0x14
@property
def repr( self ):
return 'channel_size=0x{:02x}, channel_offset=0x{:04x}, channel_row={}, channel_type={}'.format( self.channel_size, self.channel_offset, self.channel_row, self.channel_type )
[docs]class FrameV4( mrc.Block ):
size = mrc.UInt16_BE( 0x00 )
channels = mrc.BlockField( ChannelV4, 0x02, stream=True, length=mrc.Ref( 'size_channels' ) )
@property
def size_channels( self ):
return self.size-0x02
@property
def repr( self ):
return 'num_channnels={}'.format( len( self.channels ) )
[docs]class ScoreV4( mrc.Block ):
size = mrc.UInt32_BE( 0x00 )
unk1 = mrc.UInt32_BE( 0x04 )
unk2 = mrc.UInt32_BE( 0x08 )
unk3 = mrc.UInt16_BE( 0x0c )
unk4 = mrc.UInt16_BE( 0x0e )
unk5 = mrc.UInt16_BE( 0x10 )
unk6 = mrc.UInt16_BE( 0x12 )
frames = mrc.BlockField( FrameV4, 0x14, stream=True, length=mrc.Ref( 'size_frames' ) )
extra = mrc.Bytes( mrc.EndOffset( 'frames' ) )
@property
def size_frames( self ):
return self.size-0x14
@property
def repr( self ):
return 'num_frames={}'.format( len( self.frames ) )
[docs]class SoundV4( mrc.Block ):
unk1 = mrc.UInt16_BE( 0x00 )
unk2 = mrc.UInt32_BE( 0x04 )
unk3 = mrc.UInt16_BE( 0x0c )
channels = mrc.UInt16_BE( 0x14 )
sample_rate = mrc.UInt16_BE( 0x16 )
unk4 = mrc.UInt16_BE( 0x18 )
length = mrc.UInt32_BE( 0x1e )
unk5 = mrc.UInt16_BE( 0x22 )
length_copy = mrc.UInt32_BE( 0x24 )
unk6 = mrc.UInt16_BE( 0x28 )
playback_rate = mrc.UInt16_BE( 0x2a )
unk7 = mrc.UInt16_BE( 0x2c )
sample_bits = mrc.UInt16_BE( 0x3e )
data = mrc.Bytes( 0x4e )
@property
def sample_width( self ):
return self.sample_bits // 8
@property
def sample_signedness( self ):
return 'unsigned' if self.sample_bits == 8 else 'signed'
def __init__( self, *argc, **argv ):
self.audio = aud.Wave( self, mrc.Ref( 'data' ), channels=mrc.Ref( 'channels' ), sample_rate=mrc.Ref( 'sample_rate' ), format_type=int, field_size=mrc.Ref( 'sample_width' ), signedness=mrc.Ref( 'sample_signedness' ), endian='big' )
super().__init__( *argc, **argv )
@property
def repr( self ):
return 'channels={}, sample_rate={}, length={}, sample_bits={}'.format( self.channels, self.sample_rate, self.length, self.sample_bits )
[docs]class SoundCastV4( mrc.Block ):
unk1 = mrc.Bytes( 0x00, length=0x20 )
extra_size = mrc.UInt8( 0x20 )
extra = mrc.BlockField( SoundCastV4Extra, 0x21, stream=True, length=mrc.Ref( 'extra_size' ) )
@property
def repr( self ):
return 'unk1={}{}'.format(
self.unk1, ', name={}'.format( self.extra[0].name ) if self.extra else '' )
[docs]class ScriptCastV4( mrc.Block ):
unk1 = mrc.Bytes( 0x00, length=0x14 )
script_id = mrc.UInt16_BE( 0x14 )
unk2 = mrc.UInt16_BE( 0x16 )
unk3 = mrc.UInt32_BE( 0x18 )
name_offset = mrc.UInt32_BE( 0x1c )
extra1_offset_raw = mrc.UInt32_BE( 0x20 )
unk5 = mrc.UInt32_BE( 0x24 )
unk6 = mrc.UInt32_BE( 0x28 )
unk7 = mrc.UInt32_BE( 0x2c )
extra2_offset_raw = mrc.UInt32_BE( 0x30 )
end_offset_raw = mrc.UInt32_BE( 0x34 )
code = mrc.Bytes( mrc.EndOffset( 'end_offset_raw' ), length=mrc.Ref( 'name_offset' ) )
name = mrc.PString( mrc.EndOffset( 'code' ) )
extra1 = mrc.Bytes( mrc.Ref( 'extra1_offset' ), length=mrc.Ref( 'extra1_size' ) )
extra2 = mrc.Bytes( mrc.Ref( 'extra2_offset' ), length=mrc.Ref( 'extra2_size' ) )
@property
def extra1_offset( self ):
return self.extra1_offset_raw + 0x38
@property
def extra1_size( self ):
return self.extra2_offset_raw-self.extra1_offset_raw
@extra1_size.setter
def extra1_size( self, value ):
self.extra2_offset_raw = value+self.extra1_offset_raw
@property
def extra2_offset( self ):
return self.extra2_offset_raw + 0x38
@property
def extra2_size( self ):
return self.end_offset_raw-self.extra2_offset_raw
@extra2_size.setter
def extra2_size( self, value ):
self.end_offset_raw = value+self.extra2_offset_raw
@property
def repr( self ):
return 'script_id={}'.format( self.script_id )
[docs]class BitmapCompressor( mrc.Transform ):
[docs] def import_data( self, buffer, parent=None ):
result = bytearray()
pointer = 0
while (pointer < len( buffer )):
test = buffer[pointer]
pointer += 1
length = test + 1
if test & 0x80:
length = ((test ^ 0xff) & 0xff) + 2
result.extend( (buffer[pointer] for i in range( length )) )
pointer += 1
else:
result.extend( buffer[pointer:pointer+length] )
pointer += length
return mrc.TransformResult( payload=result, end_offset=pointer )
[docs]class BitmapV4( mrc.Block ):
data = mrc.Bytes( 0x00 )
[docs]class Rect( mrc.Block ):
top = mrc.Int16_BE( 0x00 )
left = mrc.Int16_BE( 0x02 )
bottom = mrc.Int16_BE( 0x04 )
right = mrc.Int16_BE( 0x06 )
@property
def width( self ):
return self.right-self.left
@property
def height( self ):
return self.bottom-self.top
@property
def repr( self ):
return 'top={}, left={}, bottom={}, right={}, width={}, height={}'.format(
self.top, self.left, self.bottom, self.right, self.width, self.height )
[docs]class BitmapCastV4( mrc.Block ):
_data = None
bpp = mrc.Bits16( 0x00, 0b1111000000000000 )
pitch = mrc.Bits16( 0x00, 0b0000111111111111 )
initial_rect = mrc.BlockField( Rect, 0x02 )
bounding_rect = mrc.BlockField( Rect, 0x0a )
reg_x = mrc.UInt16_BE( 0x12 )
reg_y = mrc.UInt16_BE( 0x14 )
#bpp = mrc.UInt16_BE( 0x16 )
#unk4 = mrc.Bytes( 0x18, length=0x24 )
#name = mrc.Bytes( 0x3e )
unk4 = mrc.Bytes( 0x16 )
@property
def repr( self ):
#return 'name={}, pitch={}, bpp={}, reg_x={}, reg_y={}, unk1={}, unk2={}'.format( self.name, self.pitch, self.bpp, self.reg_x, self.reg_y, self.unk1, self.unk2 )
return 'bpp={}, pitch={}, reg_x={}, reg_y={}, initial_rect={}, bounding_rect={}'.format( self.bpp, self.pitch, self.reg_x, self.reg_y, self.initial_rect, self.bounding_rect )
def __init__( self, *argc, **argv ):
self.image = img.IndexedImage( self, mrc.Ref( '_data.data' ), mrc.Ref( 'pitch' ), mrc.Ref( 'initial_rect.height' ), palette=DIRECTOR_PALETTE )
super().__init__( *argc, **argv )
[docs]class CastType( IntEnum ):
NULL = 0x00
BITMAP = 0x01
FILM_LOOP = 0x02
TEXT = 0x03
PALETTE = 0x04
PICTURE = 0x05
SOUND = 0x06
BUTTON = 0x07
SHAPE = 0x08
MOVIE = 0x09
VIDEO = 0x0a
SCRIPT = 0x0b
RTE = 0x0c
[docs]class CastV4( mrc.Block ):
CAST_MAP = {
CastType.BITMAP: BitmapCastV4,
CastType.SOUND: SoundCastV4,
CastType.SCRIPT: ScriptCastV4,
}
size1 = mrc.UInt16_BE( 0x00 )
size2 = mrc.UInt32_BE( 0x02 )
cast_type = mrc.UInt8( 0x06, enum=CastType )
unk1 = mrc.UInt8( 0x07 )
detail = mrc.BlockField( CAST_MAP, 0x08, block_type=mrc.Ref( 'cast_type' ), default_klass=mrc.Unknown )
garbage = mrc.Bytes( mrc.EndOffset( 'detail' ) )
@property
def repr( self ):
return 'size1: {}, size2: {}, cast_type: {}'.format( self.size1, self.size2, str( self.cast_type ) )
[docs]class KeyEntry( mrc.Block ):
section_index = mrc.UInt32_P( 0x00 )
cast_index = mrc.UInt32_P( 0x04 )
chunk_id = mrc.UInt32_P( 0x08 )
@property
def repr( self ):
return 'chunk_id: {}, section_index: {}, cast_index: {}'.format( riff.TagB( self.chunk_id ), self.section_index, self.cast_index )
[docs]class KeyV4( mrc.Block ):
unk1 = mrc.UInt16_P( 0x00 )
unk2 = mrc.UInt16_P( 0x02 )
slot_count = mrc.UInt32_P( 0x04 )
entry_count = mrc.UInt32_P( 0x08 )
entries = mrc.BlockField( KeyEntry, 0x0c, count=mrc.Ref( 'slot_count' ) )
# garbage = mrc.Bytes( mrc.EndOffset( 'entries' ) )
[docs]class MMapEntry( mrc.Block ):
chunk_id = mrc.UInt32_P( 0x00 )
length = mrc.UInt32_P( 0x04 )
offset = mrc.UInt32_P( 0x08 )
flags = mrc.UInt16_P( 0x0c )
unk1 = mrc.UInt16_P( 0x0e )
memsize = mrc.UInt32_P( 0x10 )
[docs] def import_data( self, *args, **kwargs ):
return super().import_data( *args, **kwargs )
@property
def repr( self ):
return 'chunk_id: {}, length: 0x{:08x}, offset: 0x{:08x}, flags: {}'.format( riff.TagB( self.chunk_id ), self.length, self.offset, self.flags )
# rough idea of the layout of a Director file
# imap chunk
# - dunno what this does
# mmap chunk
# - this is a list of all of the chunks in the director file, including lengths and offsets
# - main bodge mechanism used to allow append-only editing of director files!
# - when another bit of the file is referring to a chunk with an index, it's usually against this thing
# KEY* chunk
# - slightly different; this is a list containing mappings between CASt chunks and the referenced data
# - index is for the mmap list
# CAS* chunk
# - ordered list of CASt objects
# - matches the ordering in the Director UI
# - index is for the mmap list
# Sord chunk
# - sets some sort of order for cast members???
# - index appears to be for the CAS* list
[docs]class IMapV4( mrc.Block ):
unk1 = mrc.UInt32_BE( 0x00 )
mmap_offset = mrc.UInt32_BE( 0x04 )
version = mrc.UInt32_BE( 0x08 )
unk2 = mrc.Bytes( 0x0c )
[docs]class MMapV4( mrc.Block ):
unk1 = mrc.Bytes( 0x00, length=8 )
entries_max = mrc.UInt32_P( 0x04 )
entries_used = mrc.UInt32_P( 0x08 )
unk2 = mrc.Const( mrc.Bytes( 0x0c, length=8 ), b'\xff'*8 )
unk3 = mrc.UInt32_P( 0x14 )
entries = mrc.BlockField( MMapEntry, 0x18, count=mrc.Ref( 'entries_max' ), fill=b'\xaa'*0x14 )
@property
def repr( self ):
return 'entries_max: {}, entries_used: {}'.format( self.entries_max, self.entries_used )
[docs]class SordV4( mrc.Block ):
unk1 = mrc.Bytes( 0x00, length=0xc )
count = mrc.UInt32_BE( 0x0c )
unk2 = mrc.UInt16_BE( 0x10 )
unk3 = mrc.UInt16_BE( 0x12 )
index = mrc.UInt16_BE( 0x14, count=mrc.Ref( 'count' ) )
[docs]class CastListV4( mrc.Block ):
index = mrc.UInt32_BE( 0x00, stream=True )
[docs]class TextV4( mrc.Block ):
unk1 = mrc.UInt32_BE( 0x00 )
length = mrc.UInt32_BE( 0x04 )
unk2 = mrc.UInt32_BE( 0x08 )
data = mrc.Bytes( 0xc, length=mrc.Ref( 'length' ) )
unk3 = mrc.Bytes( mrc.EndOffset( 'data' ) )
[docs]class Sprite( mrc.Block ):
script_id = mrc.UInt8( 0x00 )
sprite_type = mrc.UInt8( 0x01 )
x2 = mrc.UInt16_BE( 0x02 )
flags = mrc.UInt16_BE( 0x04 )
cast_id = mrc.UInt16_BE( 0x06 )
start_x = mrc.UInt16_BE( 0x08 )
start_y = mrc.UInt16_BE( 0x0a )
height = mrc.UInt16_BE( 0x0c )
width = mrc.UInt16_BE( 0x0e )
[docs]class ScriptNamesV4( mrc.Block ):
# used to store the names of functions invoked with CALL_EXTERNAL
unk1 = mrc.UInt16_BE( 0x00 )
unk2 = mrc.UInt16_BE( 0x02 )
unk3 = mrc.UInt16_BE( 0x04 )
unk4 = mrc.UInt16_BE( 0x06 )
length_1 = mrc.UInt16_BE( 0x08 )
unk5 = mrc.UInt16_BE( 0x0a )
length_2 = mrc.UInt16_BE( 0x0c )
unk6 = mrc.UInt16_BE( 0x0e )
offset = mrc.UInt16_BE( 0x10 )
count = mrc.UInt16_BE( 0x12 )
names = mrc.StringField( mrc.Ref( 'offset' ), count=mrc.Ref( 'count' ), length_field=mrc.UInt8, encoding='latin1' )
[docs]class ScriptContextEntry( mrc.Block ):
unk1 = mrc.UInt16_BE( 0x00 )
unk2 = mrc.UInt16_BE( 0x02 )
unk3 = mrc.UInt16_BE( 0x04 )
index = mrc.UInt16_BE( 0x06 ) # for mmap.entries
unk4 = mrc.Bits16( 0x08, 0b1111111111111011 )
active = mrc.Bits16( 0x08, 0b0000000000000100 )
link = mrc.Int16_BE( 0x0a )
@property
def repr( self ):
return 'index: {}, active: {}'.format( self.index, self.active )
[docs]class ScriptContextV4( mrc.Block ):
#test = mrc.Bytes()
unk1 = mrc.Bytes( 0x00, length=0x8 )
list_count = mrc.UInt32_BE( 0x08 )
list_count_2 = mrc.UInt32_BE( 0x0c )
list_offset = mrc.UInt16_BE( 0x10 )
unk2 = mrc.UInt16_BE( 0x12 )
unk3 = mrc.Bytes( 0x14, length=22 )
entries = mrc.BlockField( ScriptContextEntry, mrc.Ref( 'list_offset' ), count=mrc.Ref( 'list_count' ) )
# source: http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Header
[docs]class ScriptConstantType( IntEnum ):
STRING = 0x0001
UINT32 = 0x0004
FLOAT = 0x0009
[docs]class Blank( mrc.Block ):
value = None
@property
def repr( self ):
return ''
[docs]class Write8( mrc.Block ):
value = mrc.UInt8( 0x00 )
@property
def repr( self ):
return '0x{:02x}'.format(self.value)
[docs]class Write16( mrc.Block ):
value = mrc.UInt16_BE( 0x00 )
@property
def repr( self ):
return '0x{:04x}'.format(self.value)
LINGO_V4_LIST = [
('EXIT', 0x01, Blank),
('PUSH_0', 0x03, Blank),
('MULT', 0x04, Blank),
('ADD', 0x05, Blank),
('SUB', 0x06, Blank),
('DIV', 0x07, Blank),
('MOD', 0x08, Blank),
('NEGATE', 0x09, Blank),
('AMPERSAND', 0x0a, Blank),
('CONCAT', 0x0b, Blank),
('LT', 0x0c, Blank),
('LE', 0x0d, Blank),
('NEQ', 0x0e, Blank),
('EQ', 0x0f, Blank),
('GT', 0x10, Blank),
('GE', 0x11, Blank),
('AND', 0x12, Blank),
('OR', 0x13, Blank),
('NOT', 0x14, Blank),
('CONTAINS', 0x15, Blank),
('STARTS', 0x16, Blank),
('OF', 0x17, Blank),
('HILITE', 0x18, Blank),
('INTERSECTS', 0x19, Blank),
('WITHIN', 0x1a, Blank),
('FIELD', 0x1b, Blank),
('EXEC', 0x1c, Blank),
('EXEC_END', 0x1d, Blank),
('LIST_UNK', 0x1e, Blank),
('DICT', 0x1f, Blank),
# 1 byte payload
('PUSH', 0x41, Write8),
('ARGLIST', 0x42, Write8),
('LIST', 0x43, Write8),
('PUSH_CONST', 0x44, Write8),
('PUSH_SYMBOL', 0x45, Write8),
('PUSH_OBJECT', 0x46, Write8),
('PUSH_GLOBAL', 0x49, Write8),
('PUSH_PROPERTY', 0x4a, Write8),
('PUSH_PARAM', 0x4b, Write8),
('PUSH_LOCAL', 0x4c, Write8),
('POP_GLOBAL', 0x4f, Write8),
('POP_PROPERTY', 0x50, Write8),
('POP_PARAM', 0x51, Write8),
('POP_LOCAL', 0x52, Write8),
('JUMP_BACK', 0x54, Write8),
('CALL', 0x56, Write8),
('CALL_EXTERNAL', 0x57, Write8),
('CALL_METHOD', 0x58, Write8),
('PUT_TEXTVAR', 0x59, Write8),
('PUT', 0x5a, Write8),
('SLICE_DEL', 0x5b, Write8),
('THE_ENTITY_GET', 0x5c, Write8),
('THE_ENTITY_SET', 0x5d, Write8),
('PUSH_PROPERTY_CTX', 0x5f, Write8),
('POP_PROPERTY_CTX', 0x60, Write8),
('PUSH_PROPERTY_OBJ', 0x61, Write8),
('POP_PROPERTY_OBJ', 0x62, Write8),
('CALL_EXTERNAL_OBJ', 0x63, Write8),
('PUSH_FROM_STACK', 0x64, Write8),
('POP_FROM_STACK', 0x65, Write8),
('PUSH_PROPERTY_RO', 0x66, Write8),
# 2 byte payload
('PUSH_U16', 0x81, Write16),
('ARGLIST_U16', 0x82, Write16),
('LIST_U16', 0x83, Write16),
('PUSH_CONST_U16', 0x84, Write16),
('PUSH_GLOBAL_U16', 0x89, Write16),
('POP_GLOBAL_U16', 0x8f, Write16),
('POP_PROPERTY_U16', 0x90, Write16),
('JUMP', 0x93, Write16),
('JUMP_IF', 0x95, Write16),
('POP_PROPERTY_CTX_U16', 0xa0, Write16),
('PUSH_PROPERTY_OBJ_U16', 0xa1, Write16),
('POP_PROPERTY_OBJ_U16', 0xa2, Write16),
('PUSH_PATH_U16', 0xa6, Write16),
]
# add stubs for missing instructions
LINGO_COVERAGE = set( (x[1] for x in LINGO_V4_LIST) )
for i in range( 0x00, 0x40 ):
if i not in LINGO_COVERAGE:
LINGO_V4_LIST.append( ('UNK_{:02X}'.format( i ), i, Blank ) )
for i in range( 0x40, 0x80 ):
if i not in LINGO_COVERAGE:
LINGO_V4_LIST.append( ('UNK_{:02X}'.format( i ), i, Write8 ) )
for i in range( 0x80, 0x100 ):
if i not in LINGO_COVERAGE:
LINGO_V4_LIST.append( ('UNK_{:02X}'.format( i ), i, Write16 ) )
LingoV4 = IntEnum( 'LingoV4', [(x[0], x[1]) for x in LINGO_V4_LIST] )
LINGO_V4_MAP = {LingoV4( x[1] ): x[2] for x in LINGO_V4_LIST}
[docs]class ScriptString( mrc.Block ):
length = mrc.UInt32_BE( 0x00 )
value = mrc.CString( 0x04, length=mrc.Ref( 'length' ), encoding='latin1' )
@property
def repr( self ):
return self.value
[docs]class ScriptConstantString( mrc.Block ):
offset = mrc.UInt32_BE( 0x00 )
value = mrc.StoreRef( ScriptString, mrc.Ref( '_parent._parent.consts_store' ), offset=mrc.Ref( 'offset' ), size=None )
@property
def repr( self ):
return self.value.repr
[docs]class ScriptConstantUInt32( mrc.Block ):
value = mrc.UInt32_BE( 0x00 )
@property
def repr( self ):
return '{}'.format( self.value )
[docs]class ScriptFloat( mrc.Block ):
length = mrc.UInt32_BE( 0x00 )
data = mrc.Bytes( 0x04, length=mrc.Ref( 'length' ) )
@property
def repr( self ):
return self.data.hex()
[docs]class ScriptConstantFloat( mrc.Block ):
offset = mrc.UInt32_BE( 0x00 )
value = mrc.StoreRef( ScriptFloat, mrc.Ref( '_parent._parent.consts_store' ), offset=mrc.Ref( 'offset' ), size=None )
@property
def repr( self ):
return self.value.repr
[docs]class ScriptConstantV4( mrc.Block ):
SCRIPT_CONSTANT_TYPES = {
0x0001: ScriptConstantString,
0x0004: ScriptConstantUInt32,
0x0009: ScriptConstantFloat
}
const_type = mrc.UInt16_BE( 0x00, enum=ScriptConstantType )
const = mrc.BlockField( SCRIPT_CONSTANT_TYPES, 0x02, block_type=mrc.Ref( 'const_type' ) )
@property
def repr( self ):
return '{}: {}'.format( self.const_type, self.const.repr )
[docs]class ScriptConstantV5( mrc.Block ):
SCRIPT_CONSTANT_TYPES = {
0x0001: ScriptConstantString,
0x0004: ScriptConstantUInt32,
0x0009: ScriptConstantFloat
}
const_type = mrc.UInt32_BE( 0x00, enum=ScriptConstantType )
const = mrc.BlockField( SCRIPT_CONSTANT_TYPES, 0x04, block_type=mrc.Ref( 'const_type' ) )
@property
def repr( self ):
return '{}: {}'.format( self.const_type, self.const.repr )
[docs]class ScriptGlobal( mrc.Block ):
name_index = mrc.UInt16_BE( 0x00 )
[docs]class ScriptCode( mrc.Block ):
instructions = mrc.ChunkField( LINGO_V4_MAP, 0x00, id_field=mrc.UInt8, id_enum=LingoV4, default_klass=Blank )
[docs]class ScriptArguments( mrc.Block ):
name_index = mrc.UInt16_BE( 0x00, count=mrc.Ref( '_parent.args_count' ) )
[docs]class ScriptVariables( mrc.Block ):
name_index = mrc.UInt16_BE( 0x00, count=mrc.Ref( '_parent.vars_count' ) )
[docs]class ScriptFunction( mrc.Block ):
name_index = mrc.UInt16_BE( 0x00 )
unk1 = mrc.UInt16_BE( 0x02 )
length = mrc.UInt32_BE( 0x04 )
offset = mrc.UInt32_BE( 0x08 )
args_count = mrc.UInt16_BE( 0x0c )
args_offset = mrc.UInt32_BE( 0x0e )
vars_count = mrc.UInt16_BE( 0x12 )
vars_offset = mrc.UInt32_BE( 0x14 )
unk2 = mrc.UInt16_BE( 0x18 )
unk8 = mrc.UInt16_BE( 0x1a )
unk9 = mrc.UInt16_BE( 0x1c )
unk10 = mrc.UInt16_BE( 0x1e )
unk11 = mrc.UInt16_BE( 0x20 )
unk12 = mrc.UInt16_BE( 0x22 )
unk13 = mrc.UInt16_BE( 0x24 )
unk14 = mrc.UInt16_BE( 0x26 )
unk15 = mrc.UInt16_BE( 0x28 )
code = mrc.StoreRef( ScriptCode, mrc.Ref( '_parent.code_store' ), offset=mrc.Ref( 'offset' ), size=mrc.Ref( 'length' ) )
args = mrc.StoreRef( ScriptArguments, mrc.Ref( '_parent.code_store' ), offset=mrc.Ref( 'args_offset' ) )
vars = mrc.StoreRef( ScriptVariables, mrc.Ref( '_parent.code_store' ), offset=mrc.Ref( 'vars_offset' ) )
@property
def args_size( self ):
return self.args_count * 2
@property
def vars_size( self ):
return self.vars_count * 2
@property
def repr( self ):
return '{}'.format( self.name )
[docs]class ScriptV4( mrc.Block ):
unk1 = mrc.Bytes( 0x00, length=0x10 )
code_store_offset = mrc.UInt16_BE( 0x10 )
unk2 = mrc.Bytes( 0x12, length=0x2e )
globals_offset = mrc.UInt16_BE( 0x40 )
globals_count = mrc.UInt16_BE( 0x42 )
unk3 = mrc.Bytes( 0x44, length=4 )
functions_count = mrc.UInt16_BE( 0x48 )
unk4 = mrc.UInt16_BE( 0x4a )
functions_offset = mrc.UInt16_BE( 0x4c )
consts_count = mrc.UInt16_BE( 0x4e )
unk6 = mrc.UInt16_BE( 0x50 )
consts_offset = mrc.UInt16_BE( 0x52 )
unk7 = mrc.UInt16_BE( 0x54 )
consts_unk = mrc.UInt16_BE( 0x56 )
unk8 = mrc.UInt16_BE( 0x58 )
consts_base = mrc.UInt16_BE( 0x5a )
@property
def code_store_size( self ):
return self.functions_offset - self.code_store_offset
@code_store_size.setter
def code_store_size( self, value ):
self.functions_offset = value + self.code_store_offset
@property
def code_store_base( self ):
return -self.code_store_offset
code_store_raw = mrc.Bytes( mrc.Ref( 'code_store_offset' ), length=mrc.Ref( 'code_store_size' ) )
globals = mrc.BlockField( ScriptGlobal, mrc.Ref( 'globals_offset' ), count=mrc.Ref( 'globals_count' ) )
functions = mrc.BlockField( ScriptFunction, mrc.Ref( 'functions_offset' ), count=mrc.Ref( 'functions_count' ) )
consts = mrc.BlockField( ScriptConstantV4, mrc.Ref( 'consts_offset' ), count=mrc.Ref( 'consts_count' ) )
consts_raw = mrc.Bytes( mrc.EndOffset( 'consts' ) )
@property
def consts_store_offset( self ):
return self.consts_base-self.get_field_end_offset( 'consts' )
#test = mrc.Bytes( 0x00 )
def __init__( self, *args, **kwargs ):
self.consts_store = mrc.Store( self, mrc.Ref( 'consts_raw' ),
base_offset=mrc.Ref( 'consts_store_offset' ) )
self.code_store = mrc.Store( self, mrc.Ref( 'code_store_raw' ),
base_offset=mrc.Ref( 'code_store_base' ) )
super().__init__( *args, **kwargs )
[docs]class ScriptV5( ScriptV4 ):
consts = mrc.BlockField( ScriptConstantV5, mrc.Ref( 'consts_offset' ), count=mrc.Ref( 'consts_count' ) )
[docs]class DirectorV4Map( riff.RIFXMap ):
CHUNK_MAP = {
riff.Tag( b'imap' ): IMapV4,
riff.Tag( b'mmap' ): MMapV4,
riff.Tag( b'KEY*' ): KeyV4,
riff.Tag( b'Sord' ): SordV4,
riff.Tag( b'CAS*' ): CastListV4,
riff.Tag( b'CASt' ): CastV4,
riff.Tag( b'snd ' ): SoundV4,
riff.Tag( b'BITD' ): BitmapV4,
riff.Tag( b'STXT' ): TextV4,
riff.Tag( b'Lscr' ): ScriptV4,
riff.Tag( b'Lnam' ): ScriptNamesV4,
riff.Tag( b'Lctx' ): ScriptContextV4,
riff.Tag( b'VWSC' ): ScoreV4,
}
DirectorV4Map.CHUNK_MAP[riff.Tag( b'RIFX' )] = DirectorV4Map
[docs]class DirectorV4( riff.RIFX ):
CHUNK_MAP_CLASS = DirectorV4Map
[docs]class DirectorV5Map( riff.RIFXMap ):
CHUNK_MAP = {
riff.Tag( b'imap' ): IMapV4,
riff.Tag( b'mmap' ): MMapV4,
riff.Tag( b'KEY*' ): KeyV4,
riff.Tag( b'Sord' ): SordV4,
riff.Tag( b'CAS*' ): CastListV4,
riff.Tag( b'CASt' ): CastV4,
riff.Tag( b'snd ' ): SoundV4,
riff.Tag( b'BITD' ): BitmapV4,
riff.Tag( b'STXT' ): TextV4,
riff.Tag( b'Lscr' ): ScriptV5,
riff.Tag( b'Lnam' ): ScriptNamesV4,
riff.Tag( b'Lctx' ): ScriptContextV4,
riff.Tag( b'VWSC' ): ScoreV4,
}
DirectorV4Map.CHUNK_MAP[riff.Tag( b'RIFX' )] = DirectorV4Map
[docs]class PJ93( mrc.Block ):
_endian = 'little'
magic = mrc.Const( mrc.Bytes( 0x00, length=4 ), b'PJ93' )
rifx_offset = mrc.UInt32_P( 0x04 )
fontmap_offset = mrc.UInt32_P( 0x08 )
resfork1_offset = mrc.UInt32_P( 0x0c )
resfork2_offset = mrc.UInt32_P( 0x10 )
dirdib_drv_offset = mrc.UInt32_P( 0x14 )
macromix_dll_offset = mrc.UInt32_P( 0x18 )
rifx_offset_dup = mrc.UInt32_P( 0x1c )
unk1 = mrc.Bytes( 0x20, length=0xc )
[docs]class MV93( mrc.Block ):
_endian = 'little'
CHUNK_MAP_CLASS = DirectorV4Map.CHUNK_MAP
magic = mrc.Const( mrc.Bytes( 0x00, length=4 ), b'XFIR' )
data_length = mrc.UInt32_P( 0x04 )
magic2 = mrc.Const( mrc.Bytes( 0x08, length=4 ), b'39VM' )
stream = mrc.ChunkField( CHUNK_MAP_CLASS, 0x0c, stream=True, id_field=mrc.UInt32_P, length_field=mrc.UInt32_P, default_klass=mrc.Unknown, alignment=0x2, fill=b'' )
[docs]class MV93_BE( mrc.Block ):
_endian = 'big'
CHUNK_MAP_CLASS = DirectorV4Map.CHUNK_MAP
magic = mrc.Const( mrc.Bytes( 0x00, length=4 ), b'RIFX' )
data_length = mrc.UInt32_P( 0x04 )
magic2 = mrc.Const( mrc.Bytes( 0x08, length=4 ), b'MV93' )
stream = mrc.ChunkField( CHUNK_MAP_CLASS, 0x0c, stream=True, id_field=mrc.UInt32_P, length_field=mrc.UInt32_P, default_klass=mrc.Unknown, alignment=0x2, fill=b'' )
[docs]class MV93_BE_V5( mrc.Block ):
_endian = 'big'
CHUNK_MAP_CLASS = DirectorV5Map.CHUNK_MAP
magic = mrc.Const( mrc.Bytes( 0x00, length=4 ), b'RIFX' )
data_length = mrc.UInt32_P( 0x04 )
magic2 = mrc.Const( mrc.Bytes( 0x08, length=4 ), b'MV93' )
stream = mrc.ChunkField( CHUNK_MAP_CLASS, 0x0c, stream=True, id_field=mrc.UInt32_P, length_field=mrc.UInt32_P, default_klass=mrc.Unknown, alignment=0x2, fill=b'' )