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 SongPatch(mrc.Block): mod_visc = mrc.UInt8(0x0000) mod_vol = mrc.UInt8(0x0001) mod_ad = mrc.UInt8(0x0002) mod_sr = mrc.UInt8(0x0003) mod_wave = mrc.UInt8(0x0004) car_misc = mrc.UInt8(0x0005) car_vol = mrc.UInt8(0x0006) car_ad = mrc.UInt8(0x0007) car_sr = mrc.UInt8(0x0008) car_wave = mrc.UInt8(0x0009) feedback = mrc.UInt8(0x000a) keyoff = mrc.UInt8(0x000b) portamento = mrc.UInt8(0x000c) glide = mrc.UInt8(0x000d) finetune = mrc.UInt8(0x000e) vibrato = mrc.UInt8(0x000f) vibdelay = mrc.UInt8(0x0010) mod_trem = mrc.UInt8(0x0011) car_trem = mrc.UInt8(0x0012) tremwait = mrc.UInt8(0x0013) arpeggio = mrc.UInt8(0x0014) arp_tab = mrc.UInt8(0x0015, count=12) start = mrc.UInt16_LE(0x0021) size = mrc.UInt16_LE(0x0023) fms = mrc.UInt8(0x0025) transp = mrc.UInt16_LE(0x0026) midinst = mrc.UInt8(0x0028) midvelo = mrc.UInt8(0x0029) midkey = mrc.UInt8(0x002a) midtrans = mrc.UInt8(0x002b) middum1 = mrc.UInt8(0x002c) middum2 = mrc.UInt8(0x002d)
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 RelocationOSFixup(mrc.Block): fixup = mrc.UInt16_LE(0x00, enum=RelocationOSFixupType) check = mrc.Const(mrc.UInt16_LE(0x02), 0) @property def repr(self): return 'fixup=0x{:04x}'.format(self.fixup)
class SoundData(mrc.Block): unk1 = mrc.UInt32_LE(0x00) unk2 = mrc.UInt16_LE(0x04) unk3 = mrc.UInt32_LE(0x06) unk4 = mrc.Bytes(0x0a, length=0x12) unk5 = mrc.UInt32_LE(0x1c) unk6 = mrc.UInt16_LE(0x20) unk7 = mrc.UInt16_LE(0x22) sample_rate = mrc.UInt32_LE(0x24) playback_rate = mrc.UInt32_LE(0x28) channels = mrc.UInt16_LE(0x2c) sample_bits = mrc.UInt16_LE(0x2e) data = mrc.Bytes(0x30) @property def sample_signedness(self): return 'unsigned' if self.sample_bits == 8 else 'signed' @property def sample_width(self): return self.sample_bits // 8 def __init__(self, *argc, **argv): self.audio = aud.Wave(self, mrc.Ref('data'), mrc.Ref('channels'), mrc.Ref('sample_rate'), int, mrc.Ref('sample_width'), mrc.Ref('sample_signedness'), 'big') super().__init__(*argc, **argv)
class NEModule(NEBase): usage_count = mrc.UInt16_LE(0x02) next_table = mrc.UInt16_LE(0x06) dgroup_offset = mrc.UInt16_LE(0x08) fileinfo_offset = mrc.UInt16_LE(0x0a) dgroup_segid = mrc.UInt16_LE(0x0e) segtable = mrc.BlockField(ModuleSegmentHeader, mrc.Ref('segtable_offset'), count=mrc.Ref('segtable_count')) restable = mrc.BlockField(ResourceTable, mrc.Ref('restable_offset')) resnametable = mrc.BlockField(ResidentNameTable, mrc.Ref('resnames_offset')) modreftable = mrc.BlockField(ModuleReference, mrc.Ref('modref_offset'), count=mrc.Ref('modref_count')) impnamedata = mrc.Bytes(mrc.Ref('impnames_offset'), length=mrc.Ref('impnames_size')) #entrydata = mrc.BlockField( EntryBundle, mrc.Ref( 'entry_offset' ), stream=True, length=mrc.Ref( 'entry_size' ) ) #nonresnametable = mrc.BlockField( ResidentNameTable, mrc.Ref( 'nonresnames_rel_offset' ) ) def __init__(self, *args, **kwargs): self.impnamestore = mrc.Store(self, mrc.Ref('impnamedata')) super().__init__(*args, **kwargs)
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 RelocationImportOrdinal(mrc.Block): index = mrc.UInt16_LE(0x00) ordinal = mrc.UInt16_LE(0x02) @property def repr(self): return 'index=0x{:04x}, ordinal=0x{:04x}'.format( self.index, self.ordinal)
class InteractiveInfo(mrc.Block): """Contains a Ground style definition for an interactive object.""" anim_flags = mrc.UInt16_LE(0x0000) start_frame = mrc.UInt8(0x0002) end_frame = mrc.UInt8(0x0003) width = mrc.UInt8(0x0004) height = mrc.UInt8(0x0005) frame_data_size = mrc.UInt16_LE(0x0006) mask_rel_offset = mrc.UInt16_LE(0x0008) unknown_1 = mrc.UInt16_LE(0x000a) unknown_2 = mrc.UInt16_LE(0x000c) trigger_x_raw = mrc.UInt16_LE(0x000e) trigger_y_raw = mrc.UInt16_LE(0x0010) trigger_width_raw = mrc.UInt8(0x0012) trigger_height_raw = mrc.UInt8(0x0013) trigger_effect = mrc.UInt8(0x0014, enum=TriggerEffect) base_offset = mrc.UInt16_LE(0x0015) preview_frame = mrc.UInt16_LE(0x0017) unknown_3 = mrc.UInt16_LE(0x0019) #: Sound effect to play. Only used when trigger_effect is set to TRAP. sound_effect = mrc.UInt8(0x001b, enum=SoundEffect) vgagr = mrc.StoreRef(InteractiveImage, mrc.Ref('_parent._vgagr.interact_store.store'), mrc.Ref('base_offset'), mrc.Ref('size')) @property def size(self): return self.frame_data_size * self.end_frame @property def plane_padding(self): return self.width * self.height // 8 @property def trigger_x(self): return self.trigger_x_raw * 4 @property def trigger_y(self): return self.trigger_y_raw * 4 - 4 @property def trigger_width(self): return self.trigger_width_raw * 4 @property def trigger_height(self): return self.trigger_height_raw * 4
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 RelocationImportName(mrc.Block): index = mrc.UInt16_LE(0x00) name_offset = mrc.UInt16_LE(0x02) name = mrc.StoreRef( ImportedName, mrc.Ref('_parent._parent._parent._parent._parent.impnamestore'), mrc.Ref('name_offset'), size=32) @property def repr(self): return 'index=0x{:04x}, name={}'.format(self.index, self.name)
class LevelHeader( mrc.Block ): width = mrc.UInt16_LE( 0x00 ) height = mrc.UInt16_LE( 0x02 ) plane_count = mrc.UInt16_LE( 0x04, default=2 ) unknown_1 = mrc.Bytes( 0x06, length=4 ) ted_vars = mrc.Bytes( 0x0a, length=4 ) plane_size = mrc.UInt16_LE( 0x0e ) unknown_2 = mrc.Bytes( 0x10, length=16 ) @property def plane_size_calc( self ): size = 2 * self.width * self.height return size + ((16-size)%16)
class VCLFile( mrc.Block ): sound_offsets = mrc.UInt32_LE( 0x00, count=50 ) sound_sizes = mrc.UInt16_LE( 0xc8, count=50 ) sound_freqs = mrc.UInt16_LE( 0x12c, count=50 ) text_offsets = mrc.UInt32_LE( 0x190, count=40 ) text_lengths = mrc.UInt16_LE( 0x230, count=40 ) raw_data = mrc.Bytes( 0x280 ) def __init__( self, *args, **kwargs ): super().__init__( *args, **kwargs ) self.sounds = mrc.LinearStore( self, mrc.Ref( 'raw_data' ), Sound, offsets=mrc.Ref( 'sound_offsets' ), sizes=mrc.Ref( 'sound_sizes' ), base_offset=-0x280 )
class VOC(mrc.Block): magic = mrc.Const(mrc.Bytes(0x00, length=0x14), b'Creative Voice File\x1a') header_size = mrc.UInt16_LE(0x14) version = mrc.Bytes(0x16, length=2) checksum = mrc.Bytes(0x18, length=2) chunks = mrc.ChunkField(VOC_CHUNK_MAP, 0x1a, stream=True, id_field=mrc.UInt8, length_field=mrc.UInt24_LE, stream_end=b'\x00') @property def audio_chunk(self): # TODO: this is pretty cheap theatrics test = [x for x in self.chunks if x.id == 1] if test: return test[0].obj return None def __init__(self, *args, **kwargs): self.audio = aud.Wave(self, mrc.Ref('audio_chunk.data'), channels=1, sample_rate=mrc.Ref('audio_chunk.sample_rate'), format_type=int, field_size=mrc.Ref('audio_chunk.sample_width'), signedness=mrc.Ref('audio_chunk.signedness'), endian='little') super().__init__(*args, **kwargs)
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 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 Scores( mrc.Block ): values = mrc.UInt32_LE( 0x00, length=7 ) items = mrc.BlockField( ScoresItems, 0x1c, count=7 ) num_cities = mrc.UInt16_LE( 0x54, length=7, range=range( 0, 9 ) ) unknown_1 = mrc.Bytes( 0x62, length=14 ) names = mrc.BlockField( ScoresName, 0x70, count=7 ) term = mrc.Const( mrc.Bytes( 0xcb, length=1 ), b'\x00' )
class EXE(mrc.Block): dos_magic = mrc.Const(mrc.Bytes(0x00, length=2), b'MZ') dos_header = mrc.Bytes(0x02, length=0x3a) ne_offset = mrc.UInt16_LE(0x3c) dos_stub = mrc.Bytes(0x3e, length=mrc.Ref('dos_stub_length')) ne_header = mrc.BlockField(NEHeader, mrc.Ref('ne_offset')) segdata = mrc.Bytes( mrc.EndOffset('ne_header', align=mrc.Ref('sector_align'))) @property def sector_align(self): if self.ne_header: return 1 << self.ne_header.sector_shift return 32 @property def dos_stub_length(self): return self.ne_offset - 0x3e def __init__(self, *args, **kwargs): self.segdatastore = mrc.Store(self, mrc.Ref('segdata'), base_offset=mrc.EndOffset( 'ne_header', neg=True, align=mrc.Ref('sector_align')), align=mrc.Ref('sector_align')) super().__init__(*args, **kwargs)
class EGAHeaderSpriteRef( mrc.Block ): width_raw = mrc.UInt16_LE( 0x00 ) height = mrc.UInt16_LE( 0x02 ) prog_offset = mrc.UInt16_LE( 0x04 ) location_raw = mrc.UInt16_LE( 0x06 ) hitbox_left = mrc.UInt16_LE( 0x08 ) hitbox_top = mrc.UInt16_LE( 0x0a ) hitbox_right = mrc.UInt16_LE( 0x0c ) httbox_bottom = mrc.UInt16_LE( 0x0e ) name = mrc.Bytes( 0x10, length=12 ) horiz_offset = mrc.UInt16_LE( 0x1c ) vert_offset = mrc.UInt16_LE( 0x1e ) @property def location( self ): return self.location_raw*16
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 ModuleSegmentHeader(SegmentHeader): selector = mrc.UInt16_LE(0x08) @property def repr(self): return 'offset_sect=0x{:04x}, size=0x{:04x}, data_seg={}, alloc_size=0x{:04x}, selector={:04x}'.format( self.offset_sect, self.size, self.data_seg, self.alloc_size, self.selector)
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')
class TestL(mrc.Block): i16 = mrc.Int16_LE() i32 = mrc.Int32_LE() i64 = mrc.Int64_LE() ui16 = mrc.UInt16_LE() ui32 = mrc.UInt32_LE() ui64 = mrc.UInt64_LE() f32 = mrc.Float32_LE() f64 = mrc.Float64_LE()
class SHAFile( mrc.Block ): tileset_offsets = mrc.UInt32_LE( 0x0000, count=128 ) tileset_sizes = mrc.UInt16_LE( 0x0200, count=128 ) tileset_data = mrc.Bytes( 0x0300 ) tilesets = mrc.StoreRef( Tileset, mrc.Ref( 'store' ), mrc.Ref( 'tileset_offsets' ), mrc.Ref( 'tileset_sizes' ), count=128 ) def __init__( self, *args, **kwargs ): self.store = mrc.Store( self, mrc.Ref( 'tileset_data' ) ) super().__init__( *args, **kwargs )
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 FixedSegmentIndicator(mrc.Block): exported = mrc.Bits(0x00, 0b00000001) shared = mrc.Bits(0x00, 0b00000010) offset = mrc.UInt16_LE(0x01) @property def repr(self): return 'offset=0x{:04x}, exported={}, shared={}'.format( self.offset, self.exported, self.shared)
class SegmentDescriptor(mrc.Block): limit_low = mrc.UInt16_LE(0x00) base_low = mrc.UInt16_LE(0x02) base_middle = mrc.UInt8(0x04) accessed = mrc.Bits(0x05, 0b00000001) readable = mrc.Bits(0x05, 0b00000010) conforming = mrc.Bits(0x05, 0b00000100) code_seg = mrc.Bits(0x05, 0b00001000) non_system = mrc.Bits(0x05, 0b00010000) privilege_level = mrc.Bits(0x05, 0b01100000) present = mrc.Bits(0x05, 0b10000000) limit_high = mrc.Bits(0x06, 0b00001111) available = mrc.Bits(0x06, 0b00010000) op_size_64 = mrc.Bits(0x06, 0b00100000) op_size_32 = mrc.Bits(0x06, 0b01000000) granularity = mrc.Bits(0x06, 0b10000000) base_high = mrc.UInt8(0x07) @property def limit(self): return self.limit_low + (self.limit_high << 16) @limit.setter def limit(self, value): value &= 0xfff self.limit_high = (value & 0xf00) >> 16 self.limit_low = (value & 0xff) @property def base(self): return self.base_low + (self.base_middle << 16) + ( self.base_high << 24) @base.setter def base(self, value): value &= 0xffffffff self.base_high = (value & 0xff000000) >> 24 self.base_middle = (value & 0xff0000) >> 16 self.base_low = (value & 0xffff) @property def repr(self): return 'present={}, code_seg={}, base={:08x}, limit={:05x}'.format( self.present, self.code_seg, self.base, self.limit)
class EGAHeader( mrc.Block ): _egalatch = None # should be manually pointed at the relevant EGALatch object latch_plane_size = mrc.UInt32_LE( 0x00 ) sprite_plane_size = mrc.UInt32_LE( 0x04 ) image_data_start = mrc.UInt32_LE( 0x08 ) sprite_data_start = mrc.UInt32_LE( 0x0c ) tile8_count = mrc.UInt16_LE( 0x10 ) tile8_offset = mrc.UInt32_LE( 0x12 ) tile32_count = mrc.UInt16_LE( 0x16 ) tile32_offset = mrc.UInt32_LE( 0x18 ) tile16_count = mrc.UInt16_LE( 0x1c ) tile16_offset = mrc.UInt32_LE( 0x1e ) bitmap_count = mrc.UInt16_LE( 0x22 ) bitmap_offset = mrc.UInt32_LE( 0x24 ) sprite_count = mrc.UInt16_LE( 0x28 ) sprite_offset = mrc.UInt32_LE( 0x2a ) latch_compressed = mrc.Bits( 0x2e, 0b00000010 ) sprite_compressed = mrc.Bits( 0x2e, 0b00000001 )
class PRSChunk(mrc.Block): tag = mrc.Bytes(0x00, length=4) unk1 = mrc.UInt16_LE(0x04) name = mrc.CStringN(0x06, length=0x12) size = mrc.UInt32_LE(0x18) # length of chunk header + data data = mrc.Bytes(0x1c, length=mrc.Ref('size_data')) @property def size_data(self): return self.size - 0x1c
class ModuleReference(mrc.Block): name_offset = mrc.UInt16_LE(0x00) name = mrc.StoreRef(ImportedName, mrc.Ref('_parent.impnamestore'), mrc.Ref('name_offset'), size=32) @property def repr(self): return 'name={}'.format(self.name)