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
import math
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 BuiltinPalette( IntEnum ):
SYSTEM_MAC = 0xff
SYSTEM_WIN = 0x9b
RAINBOW = 0xfe
GRAYSCALE = 0xfd
PASTELS = 0xfc
VIVID = 0xfb
NTSC = 0xfa
METALLIC = 0xf9
[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 EmptyChannelV4( mrc.Block ):
pass
[docs]
class SpriteType( IntEnum ):
INACTIVE = 0x00
BITMAP = 0x01
RECTANGLE = 0x02
ROUNDED_RECTANGLE = 0x03
OVAL = 0x04
LINE_TOP_BOTTOM = 0x05
LINE_BOTTOM_TOP = 0x06
TEXT = 0x07
BUTTON = 0x08
CHECKBOX = 0x09
RADIO_BUTTON = 0x0a
PICT = 0x0b
OUTLINED_RECTANGLE = 0x0c
OUTLINED_ROUNDED_RECTANGLE = 0x0d
OUTLINED_OVAL = 0x0e
THINK_LINE = 0x0f
CAST_MEMBER = 0x10
FILM_LOOP = 0x11
DIR_MOVIE = 0x12
UNUSED = 0xff
[docs]
class SpriteChannelV4( mrc.Block ):
script_id = mrc.UInt8( 0x00 )
type = mrc.UInt8( 0x01, enum=SpriteType )
fg_colour = mrc.UInt8( 0x02 )
bg_colour = mrc.UInt8( 0x03 )
line_size = mrc.Bits( 0x04, 0b00000011 )
unk4 = mrc.Bits( 0x04, 0b11111100 )
ink = mrc.Bits( 0x05, 0b00111111 )
trails = mrc.Bits( 0x05, 0b01000000 )
unk5 = mrc.Bits( 0x05, 0b10000000 )
cast_id = mrc.UInt16_BE( 0x06 )
y_pos = mrc.UInt16_BE( 0x08 )
x_pos = mrc.UInt16_BE( 0x0a )
height = mrc.UInt16_BE( 0x0c )
width = mrc.UInt16_BE( 0x0e )
[docs]
class ScriptChannelV4( mrc.Block ):
index = mrc.UInt16_BE( 0x00 )
[docs]
class ChannelType( IntEnum ):
SPRITE = 0x00
FRAME_SCRIPT = 0x10
PALETTE = 0x20
[docs]
class ChannelV4( mrc.Block ):
CHANNEL_MAP = {
None: EmptyChannelV4,
# ChannelType.SPRITE: SpriteChannelV4,
ChannelType.FRAME_SCRIPT: ScriptChannelV4,
# ChannelType.PALETTE: mrc.Unknown,
}
channel_size = mrc.UInt16_BE( 0x00 )
channel_offset = mrc.UInt16_BE( 0x02 )
@property
def channel_row( self ):
return self.channel_offset // 0x14
@property
def channel_type( self ):
return self.channel_offset % 0x14
@property
def channel_type_wrap( self ):
return (ChannelType.PALETTE if self.channel_offset == 0x14 else self.channel_type) if self.channel_size else None
data = mrc.BlockField( CHANNEL_MAP, 0x04, block_type=mrc.Ref( 'channel_type_wrap' ), default_klass=mrc.Unknown, length=mrc.Ref( 'channel_size' ) )
@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.Bytes( 0x00, length=0x14 )
channels = mrc.UInt16_BE( 0x14 )
sample_rate = mrc.UInt16_BE( 0x16 )
unk2 = mrc.Bytes( 0x18, length=0x06 )
length = mrc.UInt32_BE( 0x1e )
unk3 = mrc.UInt16_BE( 0x22 )
length_copy = mrc.UInt32_BE( 0x24 )
unk4 = mrc.UInt16_BE( 0x28 )
unk5 = mrc.UInt16_BE( 0x2a )
unk6 = mrc.UInt16_BE( 0x2c )
unk7 = mrc.Bytes( 0x2e, length=0x12 )
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 )
var_count = mrc.UInt16_BE( 0x16 )
unk2 = mrc.UInt32_BE( 0x18 )
vars = mrc.UInt32_BE( 0x1c, count=mrc.Ref( 'var_count' ) )
code = mrc.Bytes( mrc.EndOffset( 'vars' ), length=mrc.Ref( 'code_len' ) )
unk3 = mrc.Bytes( mrc.EndOffset( 'code' ) )
@property
def code_len( self ):
return self.vars[0] if self.vars else 0
@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 ShapeType( IntEnum ):
RECTANGLE = 1
ROUND_RECT = 2
OVAL = 3
LINE = 4
[docs]
class ShapeCastV4( mrc.Block ):
type = mrc.UInt16_BE( 0x00, enum=ShapeType )
rect = mrc.BlockField( Rect, 0x02 )
pattern = mrc.UInt16_BE( 0x0a )
fg_colour = mrc.UInt8( 0x0c )
bg_colour = mrc.UInt8( 0x0d )
fill_type = mrc.UInt8( 0x0e )
line_thickness = mrc.UInt8( 0x0f )
line_direction = mrc.UInt8( 0x10 )
[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_y = mrc.UInt16_BE( 0x12 )
reg_x = 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,
CastType.SHAPE: ShapeCastV4,
}
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_P( 0x00 )
mmap_offset = mrc.UInt32_P( 0x04 )
version = mrc.UInt32_P( 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 )
garbage = mrc.Bytes( mrc.EndOffset( 'entries' ) )
@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),
('TELL', 0x1c, Blank),
('TELL_DONE', 0x1d, Blank),
('LIST', 0x1e, Blank),
('PROPLIST', 0x1f, Blank),
# 1 byte payload
('PUSH_INT', 0x41, Write8),
('PUSH_ARGCNORET', 0x42, Write8),
('PUSH_ARGC', 0x43, Write8),
('PUSH_CONST', 0x44, Write8),
('PUSH_NAME', 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_INT_U16', 0x81, Write16),
('PUSH_ARGCNORET_U16', 0x82, Write16),
('PUSH_ARGC_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.Const( mrc.UInt32_BE( 0x00 ), 0x0a )
sign = mrc.Bits16( 0x04, bits=0x8000 )
exponent = mrc.Bits16( 0x04, bits=0x7fff )
integer = mrc.Bits64( 0x06, bits=0x8000000000000000 )
fraction = mrc.Bits64( 0x06, bits=0x7fffffffffffffff )
@property
def value( self ):
if (self.exponent == 0):
f64exp = 0
elif (self.exponent == 0x7fff):
f64exp = 0x7ff
else:
normexp = self.exponent - 0x3fff # value range from -0x3ffe to 0x3fff
if not (-0x3fe <= normexp < 0x3ff):
raise ValueError( 'Exponent too big for a float64' )
f64exp = normexp + 0x3ff
f64fract = self.fraction >> 11
f64bin = utils.to_uint64_be( f64fract + (f64exp << 52) + 0x80000000*self.sign )
f64 = utils.from_float64_be( f64bin )
return f64
@property
def repr( self ):
return self.value
[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=0x1c )
cast_id = mrc.UInt16_BE( 0x2e )
factory_name_id = mrc.Int16_BE( 0x30 )
unk9 = mrc.Bytes( 0x32, length=0xe )
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 ConfigV4( mrc.Block ):
length = mrc.UInt16_BE( 0x00 )
ver1 = mrc.UInt16_BE( 0x02 )
movie_rect = mrc.BlockField( Rect, 0x04 )
cast_array_start = mrc.UInt16_BE( 0x0c )
cast_array_end = mrc.UInt16_BE( 0x0e )
frame_rate = mrc.UInt8( 0x10 )
light_switch = mrc.UInt8( 0x11 )
unk1 = mrc.Int16_BE( 0x12 )
comment_font = mrc.Int16_BE( 0x14 )
comment_size = mrc.Int16_BE( 0x16 )
comment_style = mrc.UInt8( 0x18 )
comment_style_2 = mrc.UInt8( 0x19 )
stage_colour = mrc.Int16_BE( 0x1a )
bit_depth = mrc.Int16_BE( 0x1c )
colour_flag = mrc.UInt8( 0x1e )
unk5 = mrc.UInt8( 0x1f )
unk6 = mrc.Int32_BE( 0x20 )
unk7 = mrc.Int16_BE( 0x24 )
unk8 = mrc.Int16_BE( 0x26 )
unk9 = mrc.Int32_BE( 0x28 )
unk10 = mrc.Int32_BE( 0x2c )
unk11 = mrc.Int32_BE( 0x30 )
unk12 = mrc.UInt8( 0x34 )
unk17 = mrc.UInt8( 0x35 )
unk13 = mrc.Int16_BE( 0x36 )
unk14 = mrc.Int16_BE( 0x38 )
protection_bits = mrc.Int16_BE( 0x3a )
unk15 = mrc.UInt32_BE( 0x3c )
checksum = mrc.UInt32_BE( 0x40 )
unk16 = mrc.UInt16_BE( 0x44 )
palette_id = mrc.UInt16_BE( 0x46 )
unk4 = mrc.Bytes( 0x48, length=0x08 )
@property
def checksum_v4( self ):
mult = lambda a, b: (a * b) & 0xffffffff
stack = []
ax = self.movie_rect.right
ax += 6
stack.append( ax )
ax = self.movie_rect.bottom
ax += 5
stack.append( ax )
ax = self.movie_rect.left
ax += 4
stack.append( ax )
ax = self.movie_rect.top
ax += 3
stack.append( ax )
ax = self.ver1
ax += 2
cx = self.length
cx += 1
ax *= cx
stack.append( ax )
ax = stack.pop() // stack.pop()
stack.append( ax )
ax = mult( stack.pop(), stack.pop() )
stack.append( ax )
ax = stack.pop() // stack.pop()
stack.append( ax )
ax = mult( stack.pop(), stack.pop() )
bx = ax
ax = self.cast_array_start
ax += 7
bx -= ax
stack.append( bx )
ax = self.cast_array_end
ax += 8
stack.append( ax )
ax = mult( stack.pop(), stack.pop() )
temp_sum = ax
ax = self.frame_rate
ax += 9
temp_sum -= ax
ax = self.light_switch
ax += 10
temp_sum -= ax
ax = self.unk1
ax += 11
temp_sum += ax
stack.append( temp_sum )
ax = self.comment_font
ax += 12
stack.append( ax )
temp_sum = mult( stack.pop(), stack.pop() )
ax = self.comment_size
ax += 13
temp_sum += ax
stack.append( temp_sum )
ax = self.comment_style
ax += 14
stack.append( ax )
temp_sum = mult( stack.pop(), stack.pop() )
ax = self.stage_colour
ax += 15
temp_sum += ax
ax = self.bit_depth
ax += 16
temp_sum += ax
ax = self.colour_flag
ax += 17
temp_sum += ax
stack.append( temp_sum )
ax = self.unk5
ax += 18
stack.append( ax )
temp_sum = mult( stack.pop(), stack.pop() )
eax = self.unk6
eax += 19
temp_sum += eax
stack.append( temp_sum )
ax = self.unk7
ax += 20
stack.append( ax )
temp_sum = mult( stack.pop(), stack.pop() )
ax = self.unk8
ax += 21
temp_sum += ax
ax = self.unk9
ax += 22
temp_sum += ax
ax = self.unk10
ax += 23
temp_sum += ax
ax = self.unk11
ax += 24
temp_sum += ax
stack.append( temp_sum )
ax = self.unk12
ax += 25
stack.append( ax )
temp_sum = mult( stack.pop(), stack.pop() )
ax = self.unk13
ax += 26
temp_sum += ax
stack.append( temp_sum )
ax = self.unk14
ax += 27
stack.append( ax )
ax = mult( stack.pop(), stack.pop() )
stack.append( ax )
ax = 0xe06
ax = self.protection_bits * ax
ax -= 0x00bb0000
stack.append( ax )
ax = mult( stack.pop(), stack.pop() )
ax ^= 0x72616c66 # 'ralf'
return ax
[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,
riff.Tag( b'VWCF' ): ConfigV4,
}
DirectorV4Map.CHUNK_MAP[riff.Tag( b'RIFX' )] = DirectorV4Map
[docs]
class DirectorV4( riff.RIFX ):
CHUNK_MAP_CLASS = DirectorV4Map
[docs]
class DirectorV4_LE( riff.RIFX ):
_endian = 'little'
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 ' ): mrc.Unknown,
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,
riff.Tag( b'VWCF' ): ConfigV4,
}
DirectorV4Map.CHUNK_MAP[riff.Tag( b'RIFX' )] = DirectorV4Map
[docs]
class DirectorV5( riff.RIFX ):
CHUNK_MAP_CLASS = DirectorV5Map
[docs]
class DirectorV5_LE( riff.RIFX ):
_endian = 'little'
CHUNK_MAP_CLASS = DirectorV5Map
[docs]
class PJ93_LE( 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_LE( 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( 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_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'' )
[docs]
class DirectorV4Parser( object ):
def __init__( self, dirfile ):
self.dirfile = dirfile
self.map_offset = dirfile.get_field_start_offset( 'map' ) + dirfile.map.get_field_start_offset( 'stream' )
self.riff_offsets = [self.map_offset]
for i, x in enumerate( dirfile.map.stream ):
if i == 0:
continue
self.riff_offsets.append( self.riff_offsets[-1]+dirfile.map.get_field_size( 'stream', index=i-1 ) )
_, self.imap = self.get_from_offset( self.map_offset )
_, self.mmap = self.get_from_offset( self.imap.obj.mmap_offset )
_, self.key = self.get_last_from_mmap( b'KEY*' )
_, self.cas = self.get_last_from_mmap( b'CAS*' )
_, self.score = self.get_last_from_mmap( b'VWSC' )
_, self.config = self.get_last_from_mmap( b'VWCF' )
_, self.cast_order = self.get_last_from_mmap( b'Sord' )
#self.cast = [self.get_from_mmap_index( self.cas.obj.index[i-1] ) for i in self.cast_order.obj.index]
self.cast = [(i, self.get_from_mmap_index( i ).obj) if i else (None, None) for i in self.cas.obj.index]
self.cast_map = {self.config.obj.cast_array_start + i: self.get_from_mmap_index( x ).obj for i, x in enumerate( self.cas.obj.index ) if x != 0}
_, self.script_context = self.get_last_from_mmap( b'Lctx' )
_, self.script_names = self.get_last_from_mmap( b'Lnam' )
self.script_ids = []
self.scripts = []
self.scripts_text = []
for script_cast in [c for _, c in self.cast if c and c.cast_type == CastType.SCRIPT]:
script_id = script_cast.detail.script_id-1
self.script_ids.append( script_id )
self.scripts.append( self.get_from_mmap_index( self.script_context.obj.entries[script_id].index ) )
self.scripts_text.append( script_cast.detail.code.decode('latin').replace('\r', '\n') )
self.bitmaps = []
for index, cast in [(i, c) for i, c in self.cast if c and c.cast_type == CastType.BITMAP]:
bitmaps = [self.get_from_mmap_index( x.section_index ).obj for x in self.key.obj.entries if riff.TagB( x.chunk_id ) == b'BITD' and x.cast_index == index]
if bitmaps:
#bitmaps[0].data = BitmapCompressor().import_data( bitmaps[0].data ).payload
cast.detail._data = bitmaps[0]
self.bitmaps.append((index, cast))
[docs]
def get_from_offset( self, offset ):
for i, x in enumerate( self.riff_offsets ):
if x == offset:
return i, self.dirfile.map.stream[i]
raise ValueError( 'Can\'t find a matching start offset' )
[docs]
def get_all_from_mmap( self, chunk_id, include_missing=False ):
result = [(i, self.get_from_offset( x.offset )[1]) for i, x in enumerate( self.mmap.obj.entries ) if i < self.mmap.obj.entries_used and x.chunk_id == riff.Tag( chunk_id )]
if not include_missing:
result = [x for x in result if x[1].obj is not None]
return result
[docs]
def get_last_from_mmap( self, chunk_id, include_missing=False ):
vals = self.get_all_from_mmap( chunk_id, include_missing )
if not vals:
return None, None
return vals[-1]
[docs]
def get_from_mmap_index( self, index ):
return self.get_from_offset( self.mmap.obj.entries[index].offset )[1]
[docs]
def get_from_mmap_list( self ):
result = []
for i in range( self.mmap.obj.entries_used ):
if self.mmap.obj.entries[i].offset != 0 and self.mmap.obj.entries[i].chunk_id != b'free':
result.append( self.get_from_offset( self.mmap.obj.entries[i].offset )[1] )
else:
result.append( None )
return result
[docs]
def dump_scripts( self ):
for i, script in enumerate( self.scripts ):
print('SCRIPT {}'.format( self.script_ids[i] ))
print('NAMES: {}'.format( self.script_names.obj.names ))
print('CODE:')
print(self.scripts_text[i])
name_lookup = lambda n: self.script_names.obj.names[n] if n in range( len( self.script_names.obj.names ) ) else 'unk_{}'.format(n)
assert riff.TagB( script.id ) == b'Lscr'
for j, f in enumerate(script.obj.functions):
if f is None:
print('FUNCTION {} - None'.format(j))
continue
print('FUNCTION {} - {}({})'.format(j, name_lookup( f.name_index ), ', '.join( [name_lookup( a ) for a in f.args.name_index] )))
print('VARS: {}'.format(', '.join([name_lookup( v ) for v in f.vars.name_index])))
for inst in f.code.instructions:
if self.script_names:
if inst.id in (LingoV4.CALL,):
print('{} # {}()'.format( inst, name_lookup( script.obj.functions[inst.obj.value].name_index ) ))
elif inst.id in (LingoV4.CALL_EXTERNAL,):
print('{} # {}()'.format( inst, name_lookup( inst.obj.value ) ))
elif inst.id in (LingoV4.PUSH_PROPERTY, LingoV4.POP_PROPERTY, LingoV4.PUSH_PROPERTY_CTX, LingoV4.POP_PROPERTY_CTX, LingoV4.PUSH_PROPERTY_OBJ, LingoV4.POP_PROPERTY_OBJ, LingoV4.PUSH_PROPERTY_RO, LingoV4.PUSH_GLOBAL, LingoV4.POP_GLOBAL, LingoV4.PUSH_OBJECT, LingoV4.PUSH_NAME):
print('{} # {}'.format( inst, name_lookup( inst.obj.value ) ))
elif inst.id in (LingoV4.PUSH_CONST,):
print('{} # {}'.format( inst, script.obj.consts[inst.obj.value // 6] ))
elif inst.id in (LingoV4.PUSH_PARAM, LingoV4.POP_PARAM,):
print('{} # {}'.format( inst, name_lookup( f.args.name_index[inst.obj.value // 6] ) if f.args.name_index else 'unk_{}'.format(inst.obj.value // 6) ))
elif inst.id in (LingoV4.PUSH_LOCAL, LingoV4.POP_LOCAL,):
print('{} # {}'.format( inst, name_lookup( f.vars.name_index[inst.obj.value // 6] ) if f.vars.name_index else 'unk_{}'.format(inst.obj.value // 6) ))
else:
print(inst)
else:
print(inst)
for j, c in enumerate(script.obj.consts):
print('CONST {}'.format(j))
print(c)
for j, g in enumerate(script.obj.globals):
print('GLOBAL {}'.format(j))
if self.script_names:
print(self.script_names.obj.names[g.name_index])
else:
print(g.name_index)
print()
[docs]
def unlock_dir_file( filename, klass=DirectorV4 ):
f = open( filename, 'r+b' )
data = f.read()
f.seek( 0 )
parser = DirectorV4Parser( klass( data ) )
index, chunk = parser.get_all_from_mmap( b'VWCF' )[0]
if chunk.obj.protection_bits % 23:
print('File is unprotected!')
else:
print('File is protected, fixing!')
old_data = chunk.obj.export_data()
chunk.obj.ver1 = 0x045D
chunk.obj.protection_bits += 1
chunk.obj.checksum = chunk.obj.checksum_v4
new_data = chunk.obj.export_data()
for location in utils.find_all( data, old_data ):
f.seek( location )
f.write( new_data )
f.close()