class RGBColour(Colour): r_raw = mrc.UInt8(0x00) g_raw = mrc.UInt8(0x01) b_raw = mrc.UInt8(0x02) @property def r(self): return self.r_raw / 255 @r.setter def r(self, value): self.r_raw = round(value * 255) @property def g(self): return self.g_raw / 255 @g.setter def g(self, value): self.g_raw = round(value * 255) @property def b(self): return self.b_raw / 255 @b.setter def b(self, value): self.b_raw = round(value * 255)
class VGM150(mrc.Block): magic = mrc.Const(mrc.Bytes(0x00, length=0x04, default=b'Vgm '), b'Vgm ') eof_offset = mrc.UInt32_LE(0x04) version = mrc.UInt32_LE(0x08) sn76489_clock = mrc.UInt32_LE(0x0c) ym2413_clock = mrc.UInt32_LE(0x10) gd3_offset = mrc.UInt32_LE(0x14) total_sample_count = mrc.UInt32_LE(0x18) loop_offset = mrc.UInt32_LE(0x1c) loop_sample_count = mrc.UInt32_LE(0x20) rate = mrc.UInt32_LE(0x24) sn76489_feedback = mrc.UInt16_LE(0x28) sn76489_shiftwidth = mrc.UInt8(0x2a) sn76489_flags = mrc.UInt8(0x2b) ym2612_clock = mrc.UInt32_LE(0x2c) ym2151_clock = mrc.UInt32_LE(0x30) vgm_data_offset_raw = mrc.UInt32_LE(0x34) header_extra = mrc.Bytes(0x38, length=0x08, default=b'\x00' * 8) @property def vgm_data_offset(self): if self.version >= 0x150: return self.vgm_data_offset_raw + 0x34 return 0x40 vgm_data = mrc.ChunkField(COMMAND_MAP, mrc.Ref('vgm_data_offset'), id_field=mrc.UInt8, id_enum=Command, default_klass=mrc.Unknown, stream_end=b'\x66') extra = mrc.Bytes(mrc.EndOffset('vgm_data'), default=b'')
class RegisterWrite8(mrc.Block): register = mrc.UInt8(0x00) value = mrc.UInt8(0x00) @property def repr(self): return f'register=0x{self.register:02x}, value=0x{self.value:02x}'
class TerrainInfo(mrc.Block): width = mrc.UInt8(0x0000) height = mrc.UInt8(0x0001) base_offset = mrc.UInt16_LE(0x0002) mask_rel_offset = mrc.UInt16_LE(0x0004) unknown_1 = mrc.UInt16_LE(0x0006) vgagr = mrc.StoreRef(TerrainImage, mrc.Ref('_parent._vgagr.terrain_store.store'), mrc.Ref('base_offset'), mrc.Ref('size')) @property def size(self): return self.width * self.height * 5 // 8 @property def mask_offset(self): return self.mask_rel_offset - self.base_offset #return self.width*self.height*4//8 @property def mask_stride(self): return self.width * self.height * 4 // 8 @property def mask_size(self): return self.width * self.height // 8
class VGAColour( img.Colour ): r_raw = mrc.UInt8( 0x00, range=range( 0, 64 ) ) g_raw = mrc.UInt8( 0x01, range=range( 0, 64 ) ) b_raw = mrc.UInt8( 0x02, range=range( 0, 64 ) ) @property def r( self ): return self.r_raw / 63 @r.setter def r( self, value ): self.r_raw = round( value * 63 ) @property def g( self ): return self.g_raw / 63 @g.setter def g( self, value ): self.g_raw = round( value * 63 ) @property def b( self ): return self.b_raw / 63 @b.setter def b( self, value ): self.b_raw = round( value * 63 )
class PRSFile(mrc.Block): magic = mrc.Const(mrc.Bytes(0x00, length=0x18), b'PRS Format Resource File') unk1 = mrc.UInt8(0x18) unk2 = mrc.UInt8(0x19) unk3 = mrc.UInt8(0x1f) chunks = mrc.BlockField(PRSChunk, 0x30, stream=True)
class VGAColour(img.Colour): r_raw = mrc.UInt8(0x00, range=range(0, 64)) g_raw = mrc.UInt8(0x01, range=range(0, 64)) b_raw = mrc.UInt8(0x02, range=range(0, 64)) @property def r_8(self): return self.r_raw * 255 // 63 @r_8.setter def r_8(self, value): self.r_raw = value * 63 // 255 @property def g_8(self): return self.g_raw * 255 // 63 @g_8.setter def g_8(self, value): self.g_raw = value * 63 // 255 @property def b_8(self): return self.b_raw * 255 // 63 @b_8.setter def b_8(self, value): self.b_raw = value * 63 // 255
class MacBinary(mrc.Block): version_old = mrc.Const(mrc.UInt8(0x00), 0) name_size = mrc.UInt8(0x01, range=range(1, 64)) name = mrc.Bytes(0x02, length=mrc.Ref('name_size')) type = mrc.Bytes(0x41, length=4) creator = mrc.Bytes(0x45, length=4) locked = mrc.Bits(0x49, 0b10000000) invisible = mrc.Bits(0x49, 0b01000000) bundle = mrc.Bits(0x49, 0b00100000) system = mrc.Bits(0x49, 0b00010000) bozo = mrc.Bits(0x49, 0b00001000) busy = mrc.Bits(0x49, 0b00000100) changed = mrc.Bits(0x49, 0b00000010) inited = mrc.Bits(0x49, 0b00000001) const1 = mrc.Const(mrc.UInt8(0x4a), 0) pos_y = mrc.UInt16_BE(0x4b) pos_x = mrc.UInt16_BE(0x4d) folder_id = mrc.UInt16_BE(0x4f) protected = mrc.Bits(0x51, 0b00000001) const2 = mrc.Const(mrc.UInt8(0x52), 0) data_size = mrc.UInt32_BE(0x53) resource_size = mrc.UInt32_BE(0x57) created = mrc.UInt32_BE(0x5a) modified = mrc.UInt32_BE(0x5e) data = mrc.Bytes(0x80, length=mrc.Ref('data_size')) resource = mrc.Bytes(mrc.EndOffset('data', align=0x80), length=mrc.Ref('resource_size'))
class SpeakAndSpellROM( mrc.Block ): count_a = mrc.UInt8( 0x00 ) count_b = mrc.UInt8( 0x01 ) count_c = mrc.UInt8( 0x02 ) count_d = mrc.UInt8( 0x03 ) offset_a = mrc.Pointer( mrc.UInt16_LE( 0x04 ), mrc.EndOffset( 'common' ) ) offset_b = mrc.Pointer( mrc.UInt16_LE( 0x06 ), mrc.EndOffset( 'list_a' ) ) offset_c = mrc.Pointer( mrc.UInt16_LE( 0x08 ), mrc.EndOffset( 'list_b' ) ) offset_d = mrc.Pointer( mrc.UInt16_LE( 0x0a ), mrc.EndOffset( 'list_c' ) ) @property def common_len( self ): return (self.offset_a - 0x0c) // 2 @common_len.setter def common_len( self, value ): self.offset_a = value * 2 + 0x0c common = mrc.UInt16_LE( 0x0c, count=mrc.Ref( 'common_len' ) ) list_a = mrc.UInt16_LE( mrc.Ref( 'offset_a' ), count=mrc.Ref( 'count_a' ) ) list_b = mrc.UInt16_LE( mrc.Ref( 'offset_b' ), count=mrc.Ref( 'count_b' ) ) list_c = mrc.UInt16_LE( mrc.Ref( 'offset_c' ), count=mrc.Ref( 'count_c' ) ) list_d = mrc.UInt16_LE( mrc.Ref( 'offset_d' ), count=mrc.Ref( 'count_d' ) ) raw_data = mrc.Bytes( mrc.EndOffset( 'list_d' ) )
class Tileset(mrc.Block): num_shapes = mrc.UInt8(0x00) num_rots = mrc.UInt16_LE(0x01) len_cga = mrc.UInt16_LE(0x03) len_ega = mrc.UInt16_LE(0x05) len_vga = mrc.UInt16_LE(0x07) colour_depth = mrc.UInt8(0x09) flags = mrc.UInt16_LE(0x0a)
class RelocationInternalRef(mrc.Block): index = mrc.UInt8(0x00) check = mrc.Const(mrc.UInt8(0x01), 0) offset = mrc.UInt16_LE(0x02) @property def repr(self): return 'index=0x{:02x}, offset=0x{:04x}'.format( self.index, self.offset)
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)
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)
class VOCSoundData12(mrc.Block): sample_rate = mrc.UInt32_LE(0x00) sample_bits = mrc.UInt8(0x04) channels_raw = mrc.UInt8(0x05) codec = mrc.UInt16_LE(0x06) reserved = mrc.Const(mrc.UInt32_LE(0x08), 0) data = mrc.Bytes(0x0c) @property def channels(self): return self.channels_raw + 1
class VOCExtra(mrc.Block): freq_divisor = mrc.UInt16_LE(0x00) codec = mrc.UInt8(0x02, enum=VOCCodec) channels_raw = mrc.UInt8(0x03) @property def channels(self): return self.channels_raw + 1 @property def sample_rate(self): return 256000000 // ((self.channels) * (65536 - self.freq_divisor))
class RGBAColour(RGBColour): r_raw = mrc.UInt8(0x00) g_raw = mrc.UInt8(0x01) b_raw = mrc.UInt8(0x02) a_raw = mrc.UInt8(0x03) @property def a(self): return self.a_raw / 255 @a.setter def a(self, value): self.a_raw = round(value * 255)
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)
class Terrain(mrc.Block): """Represents a single terrain piece placed in a level.""" #: Raw value for the x position of the left edge. x_raw = mrc.UInt16_BE(0x00, bitmask=b'\x0f\xff') #: If 1, blit image behind background. draw_back = mrc.Bits(0x00, 0b10000000) #: If 1, draw piece flipped vertically. draw_upsidedown = mrc.Bits(0x00, 0b01000000) #: If 1, draw piece as a hole. draw_erase = mrc.Bits(0x00, 0b00100000) #: Raw value (coarse component) for the y position of the top edge. y_raw_coarse = mrc.Int8(0x02) #: Raw value (fine component) for the y position of the top edge. y_raw_fine = mrc.Bits(0x03, 0b10000000) unknown_1 = mrc.Bits(0x03, 0b01000000) #: Index of the TerrainInfo block in the accompanying GroundDAT. obj_id = mrc.UInt8(0x03, bitmask=b'\x3f', range=range(0, 64)) @property def x(self): """The x position of the left edge.""" return (self.x_raw - 16) @property def y(self): """The y position of the top edge.""" return (self.y_raw_coarse * 2 + self.y_raw_fine) - 4 @property def repr(self): return "obj_id={}, x={}, y={}".format(self.obj_id, self.x, self.y)
class Resource(mrc.Block): offset = mrc.UInt16_LE(0x00) size = mrc.UInt16_LE(0x02) preload = mrc.Bits(0x04, 0b01000000) sharable = mrc.Bits(0x04, 0b00100000) movable = mrc.Bits(0x04, 0b00010000) in_memory = mrc.Bits(0x04, 0b00000100) unk1 = mrc.Bits(0x04, 0b10001111) discardable = mrc.Bits(0x05, 0b00010000) unk2 = mrc.Bits(0x05, 0b11101111) resource_id_low = mrc.UInt8(0x06) int_id = mrc.Bits(0x07, 0b10000000) resource_id_high = mrc.Bits(0x07, 0b01111111) reserved = mrc.Bytes(0x08, length=0x04) @property def resource_id(self): return (self.resource_id_high << 8) + self.resource_id_low @resource_id.setter def resource_id(self, value): self.resouce_id_high = (value >> 8) & 0b01111111 self.resource_id_low = value & 0b11111111 @property def repr(self): return 'offset=0x{:04x}, size=0x{:04x}, resource_id=0x{:04x}, int_id={}'.format( self.offset, self.size, self.resource_id, self.int_id)
class EntryBundle(mrc.Block): # awful hack for validating blockfield INDICATOR_MAP = {k: FixedSegmentIndicator for k in range(1, 255)} INDICATOR_MAP[0] = EmptySegmentIndicator INDICATOR_MAP[255] = MovableSegmentIndicator count = mrc.UInt8(0x00) indicator = mrc.UInt8(0x01) indicators = mrc.BlockField(INDICATOR_MAP, 0x02, count=mrc.Ref('count'), block_type=mrc.Ref('indicator')) @property def repr(self): return 'count={}, indicator={}'.format(self.count, self.indicator)
class ImportedName(mrc.Block): size = mrc.UInt8(0x00) name = mrc.Bytes(0x01, length=mrc.Ref('size')) @property def repr(self): return 'name={}'.format(self.name)
class B800Char(mrc.Block): _palette = ibm_pc.EGA_DEFAULT_PALETTE code_point = mrc.UInt8(0x00) fg_blink = mrc.Bits(0x01, 0b10000000) bg_colour = mrc.Bits(0x01, 0b01110000) fg_colour = mrc.Bits(0x01, 0b00001111) @property def char(self): return CP437[self.code_point] def ansi_format(self): return ansi.format_string(self.char, self._palette[self.fg_colour], self._palette[self.bg_colour], blink=self.fg_blink) def __str__(self): return self.ansi_format() def __repr__(self): return '<{}: char {}, bg {}, fg {}>'.format(self.__class__.__name__, self.char, self.bg_colour, self.fg_colour)
class SoundCastV4Extra(mrc.Block): name_size = mrc.UInt8(0x00) name = mrc.CString(0x01, length=mrc.Ref('name_size')) @property def repr(self): return 'name={}'.format(self.name)
class SongPositionChannel(mrc.Block): pattern_num_raw = mrc.UInt16_LE(0x00) transpose = mrc.UInt8(0x02) @property def pattern_num(self): return self.pattern_num_raw // 2
class NEBase(mrc.Block): ne_magic = mrc.Const(mrc.Bytes(0x00, length=2), b'NE') entry_offset = mrc.UInt16_LE(0x04) flags = mrc.UInt16_LE(0x0c) heap_size = mrc.UInt16_LE(0x10) stack_size = mrc.UInt16_LE(0x12) ip_offset = mrc.UInt16_LE(0x14) cs_id = mrc.UInt16_LE(0x16) sp_offset = mrc.UInt16_LE(0x18) ss_id = mrc.UInt16_LE(0x1a) segtable_count = mrc.UInt16_LE(0x1c) modref_count = mrc.UInt16_LE(0x1e) nonresnames_size = mrc.UInt16_LE(0x20) segtable_offset = mrc.UInt16_LE(0x22) restable_offset = mrc.UInt16_LE(0x24) resnames_offset = mrc.UInt16_LE(0x26) modref_offset = mrc.UInt16_LE(0x28) impnames_offset = mrc.UInt16_LE(0x2a) nonresnames_rel_offset = mrc.UInt32_LE(0x2c) movable_count = mrc.UInt16_LE(0x30) sector_shift = mrc.UInt16_LE(0x32) exe_type = mrc.UInt8(0x36) unk1 = mrc.Bytes(0x37, length=9) @property def impnames_size(self): return self.entry_offset - self.impnames_offset
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
class Test(mrc.Block): count = mrc.UInt8(0x00) elements = mrc.BlockField(ElementRef, count=mrc.Ref('count')) raw_data = mrc.Bytes(mrc.EndOffset('elements')) def __init__(self, *args, **kwargs): self.store = mrc.Store(self, mrc.Ref('raw_data')) super().__init__(*args, **kwargs)
class ResidentName(mrc.Block): size = mrc.UInt8(0x00) name = mrc.Bytes(0x01, length=mrc.Ref('size')) index = mrc.UInt16_LE(mrc.EndOffset('name')) @property def repr(self): return 'index=0x{:02x}, name={}'.format(self.index, self.name)
class Accelerator( mrc.Block ): RANGE = 0xC8 axis_raw = mrc.UInt8( 0x06 ) @property def axis( self ): return min( max( (255*(self.axis_raw)//self.RANGE), 0 ), 255 )
class ResidentNameTable(mrc.Block): module_name_size = mrc.UInt8(0x00) module_name = mrc.Bytes(0x01, length=mrc.Ref('module_name_size')) padding = mrc.UInt16_LE(mrc.EndOffset('module_name')) resnames = mrc.BlockField(ResidentName, mrc.EndOffset('padding'), stream=True, stream_end=b'\x00')