Source code for mrcrowbar.lib.games.lostvikings

"""File format classes for the games The Lost Vikings (1992) and Blackthorne (1994).
"""
import logging
logger = logging.getLogger( __name__ )

from mrcrowbar import models as mrc
from mrcrowbar import utils

[docs] class Config( mrc.Block ): pad1 = mrc.UInt16_LE( 0x00 ) sound_type = mrc.UInt16_LE( 0x02 ) sound_port = mrc.UInt16_LE( 0x04 ) sound_irq = mrc.UInt16_LE( 0x06 ) sound_dma = mrc.UInt16_LE( 0x08 ) pad2 = mrc.UInt16_LE( 0x0a ) unk1 = mrc.UInt16_LE( 0x0c ) pad3 = mrc.UInt16_LE( 0x0e ) pad4 = mrc.UInt16_LE( 0x10 ) music_type = mrc.UInt16_LE( 0x12 ) music_port = mrc.UInt16_LE( 0x14 ) pad5 = mrc.UInt16_LE( 0x16 ) pad6 = mrc.UInt16_LE( 0x18 ) pad7 = mrc.UInt16_LE( 0x1a ) pad8 = mrc.UInt16_LE( 0x1c ) pad9 = mrc.UInt16_LE( 0x1e )
[docs] class Data( mrc.Block ): count = mrc.UInt32_LE( 0x00 ) offsets = mrc.UInt32_LE( 0x04, bitmask=b'\xff\xff\xff\x3f', count=mrc.Ref( 'count' ) ) data_raw = mrc.Bytes( mrc.EndOffset( 'offsets' ) ) def __init__( self, *args, **kwargs ): super().__init__( *args, **kwargs ) self.data = mrc.LinearStore( parent=self, source=mrc.Ref( 'data_raw' ), block_klass=mrc.Unknown, offsets=mrc.Ref( 'offsets' ), base_offset=mrc.EndOffset( 'offsets', neg=True ) )
[docs] class Tile( mrc.Block ): index = mrc.UInt16_LE( 0x00 )
[docs] class TileMap( mrc.Block ): tiles = mrc.BlockField( Tile, 0x00, stream=True )
[docs] class TileQuad( mrc.Block ): index = mrc.Bits16( 0x00, 0xffc0, endian='little' ) flip_v = mrc.Bits16( 0x00, 0x0020, endian='little' ) flip_h = mrc.Bits16( 0x00, 0x0010, endian='little' ) unk1 = mrc.Bits16( 0x00, 0x000f, endian='little' ) @property def repr( self ): return 'index: {}, flip_h: {}, flip_v: {}'.format( self.index, self.flip_h, self.flip_v )
[docs] class MetaTile( mrc.Block ): top_left = mrc.BlockField( TileQuad, 0x00 ) top_right = mrc.BlockField( TileQuad, 0x02 ) bottom_left = mrc.BlockField( TileQuad, 0x04 ) bottom_right = mrc.BlockField( TileQuad, 0x06 ) @property def repr( self ): return 'top_left: {}, top_right: {}, bottom_left: {}, bottom_right: {}'.format( self.top_left, self.top_right, self.bottom_left, self.bottom_right )
[docs] class MetaTileMap( mrc.Block ): metatiles = mrc.BlockField( MetaTile, 0x00, stream=True )
[docs] class LZSS( mrc.Transform ):
[docs] def import_data( self, buffer ): output_size = utils.from_uint32_le( buffer[:4] ) edx = output_size data_p = 4 bx = 0 cx = 0 work_ram = bytearray( 0x1000 ) output = bytearray() while True: cx >>= 1 if cx < 0x100: logger.debug( '@ new pattern: {:08b}'.format( buffer[data_p] ) ) cx = buffer[data_p] + 0xff00 data_p += 1 if not (cx & 1): info = buffer[data_p] + (buffer[data_p+1] << 8) data_p += 2 work_p = info & 0xfff count = (info >> 12) + 3 logger.debug( '# work_ram[0x{:04x}:0x{:04x}] = work_ram[0x{:04x}:0x{:04x}]'.format( bx, (bx+count) & 0xfff, work_p, (work_p+count) & 0xfff ) ) logger.debug( '! output[0x{:04x}:0x{:04x}] = work_ram[0x{:04x}:0x{:04x}]'.format( len( output ), len( output )+count, work_p, (work_p+count) & 0xfff ) ) for i in range( count ): # loc_103C4 dat = work_ram[work_p] work_ram[bx] = dat work_p += 1 work_p &= 0xfff bx += 1 bx &= 0xfff output.append( dat ) edx -= 1 if edx == 0: break if edx == 0: break else: logger.debug( '# work_ram[0x{:04x}] = buffer[0x{:04x}]'.format( bx, data_p ) ) logger.debug( '! output[0x{:04x}] = buffer[0x{:04x}]'.format( len( output ), data_p ) ) dat = buffer[data_p] work_ram[bx] = dat data_p += 1 bx += 1 bx &= 0xfff output.append( dat ) edx -= 1 if edx == 0: break logger.info( '{} - output_size: {:08x}, output_end: {:08x}, input_size: {:08x}, input_end: {:08x}'.format( self, output_size, len( output ), len( buffer ), data_p ) ) return mrc.TransformResult( payload=bytes( output ), end_offset=data_p )
[docs] class Interlace( mrc.Transform ):
[docs] def import_data( self, buffer ): assert len( buffer ) % 64 == 0 result = bytearray( len( buffer ) ) for i in range( 0, len( buffer ), 64 ): deint = buffer[i:i+64:2] + buffer[i+1:i+64:2] result[i:i+64] = bytes( [deint[8*(j%8)+(j//8)] for j in range( 64 )] ) return mrc.TransformResult( payload=bytes( result ), end_offset=len( result ) )
# in BLACK.EXE at 0x14ef8. BLACKTHORNE_PALETTE_ENTRIES = [ 0x4f, 0x8c, 0x9d, 0x54, 0x109, 0x108, 0x10a, 0x55, 0x57, 0x52, 0x4e, 0x58, 0x116, 0x50, 0x48, 0x124, 0x7c, 0x53, 0x56, 0x51, 0x10b, 0x128, 0x129, 0x125, 0x12d, 0x12e, 0x126, 0x127, 0x12f, 0x12b, 0x12a, 0x12c, 0x130 ]