class Write16(mrc.Block): value = mrc.UInt16_BE(0x00) @property def repr(self): return f'0x{self.value:04x}'
class ScriptChannelV4(mrc.Block): index = mrc.UInt16_BE(0x00)
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'))
def import_data(self, buffer, parent=None): assert utils.is_bytes(buffer) pattern_count = mrc.UInt16_BE(0x0000).get_from_buffer(buffer) xor_mode = (pattern_count & 0x8000) != 0 pattern_count &= 0x7fff index = 2 lut = {} prev_pal_index = 0 while index < len(buffer): test = buffer[index] if test == 0xff: break elif test & 0x80: code_raw = buffer[index + 2] bit_count = buffer[index + 1] & 0x0f code = ''.join([ '1' if (code_raw & (1 << i)) else '0' for i in range(bit_count - 1, -1, -1) ]) lut[code] = { 'pal_index': buffer[index] & 0x0f, 'copy_count': ((buffer[index + 1] & 0xf0) >> 4) + 1, } prev_pal_index = lut[code]['pal_index'] index += 3 else: code_raw = buffer[index + 1] bit_count = buffer[index] & 0x0f code = ''.join([ '1' if (code_raw & (1 << i)) else '0' for i in range(bit_count - 1, -1, -1) ]) lut[code] = { 'pal_index': prev_pal_index, 'copy_count': ((buffer[index] & 0xf0) >> 4) + 1, } index += 2 bs = bits.BitStream(buffer[index + 1:], 0, bit_endian='big') state = { 'output': bytearray(64 * pattern_count), 'output_index': 0, 'current_row': [], 'prev_row': bytearray(8) } def push_pal(pal, state): state['current_row'].append(pal) if len(state['current_row']) == 8: output_index = state['output_index'] for i in range(8): state['output'][output_index + i] = state['current_row'][i] if xor_mode: for i in range(8): state['output'][output_index + i] ^= state['prev_row'][i] prev_row = state['output'][output_index:output_index + 8] state['output_index'] += 8 state['current_row'].clear() return max_key_size = max([len(x) for x in lut.keys()]) while state['output_index'] < 64 * pattern_count: test = '' for i in range(max_key_size): test += '1' if bs.read(1) else '0' if test in lut or test == '111111': break if test in lut: for i in range(lut[test]['copy_count']): push_pal(lut[test]['pal_index'], state) elif test == '111111': copy_count = bs.read(3) pal_index = bs.read(4) for i in range(copy_count): push_pal(pal_index, state) else: raise Exception('Invalid code found in data stream, aborting') return bytes(state['output'])
class OddRecord(mrc.Block): """Represents an alternative set of conditions for a level. Used in Lemmings to repeat the same level with a different difficulty. """ #: Minimum Lemming release-rate. release_rate = mrc.UInt16_BE(0x0000, range=range(0, 251)) #: Number of Lemmings released. num_released = mrc.UInt16_BE(0x0002, range=range(0, 115)) #: Number of Lemmings required to be saved. num_to_save = mrc.UInt16_BE(0x0004, range=range(0, 115)) #: Time limit for the level (minutes). time_limit_mins = mrc.UInt16_BE(0x0006, range=range(0, 256)) #: Number of Climber skills. num_climbers = mrc.UInt16_BE(0x0008, range=range(0, 251)) #: Number of Floater skills. num_floaters = mrc.UInt16_BE(0x000a, range=range(0, 251)) #: Number of Bomber skills. num_bombers = mrc.UInt16_BE(0x000c, range=range(0, 251)) #: Number of Blocker skills. num_blockers = mrc.UInt16_BE(0x000e, range=range(0, 251)) #: Number of Builder skills. num_builders = mrc.UInt16_BE(0x0010, range=range(0, 251)) #: Number of Basher skills. num_bashers = mrc.UInt16_BE(0x0012, range=range(0, 251)) #: Number of Miner skills. num_miners = mrc.UInt16_BE(0x0014, range=range(0, 251)) #: Number of Digger skills. num_diggers = mrc.UInt16_BE(0x0016, range=range(0, 251)) #: Name of the level (ASCII string). name = mrc.Bytes(0x0018, length=32, default=b' ') @property def repr(self): return self.name.strip().decode('utf8')
class Level(mrc.Block): """Represents a single Lemmings level.""" #: Minimum Lemming release-rate. release_rate = mrc.UInt16_BE(0x0000, range=range(0, 251)) #: Number of Lemmings released. num_released = mrc.UInt16_BE(0x0002, range=range(0, 115)) #: Number of Lemmings required to be saved. num_to_save = mrc.UInt16_BE(0x0004, range=range(0, 115)) #: Time limit for the level (minutes). time_limit_mins = mrc.UInt16_BE(0x0006, range=range(0, 256)) #: Number of Climber skills. num_climbers = mrc.UInt16_BE(0x0008, range=range(0, 251)) #: Number of Floater skills. num_floaters = mrc.UInt16_BE(0x000a, range=range(0, 251)) #: Number of Bomber skills. num_bombers = mrc.UInt16_BE(0x000c, range=range(0, 251)) #: Number of Blocker skills. num_blockers = mrc.UInt16_BE(0x000e, range=range(0, 251)) #: Number of Builder skills. num_builders = mrc.UInt16_BE(0x0010, range=range(0, 251)) #: Number of Basher skills. num_bashers = mrc.UInt16_BE(0x0012, range=range(0, 251)) #: Number of Miner skills. num_miners = mrc.UInt16_BE(0x0014, range=range(0, 251)) #: Number of Digger skills. num_diggers = mrc.UInt16_BE(0x0016, range=range(0, 251)) #: Raw value for the start x position of the camera. camera_x_raw = mrc.UInt16_BE(0x0018, range=range(0, 1265)) #: Index denoting which graphical Style to use. style_index = mrc.UInt16_BE(0x001a) #: Index denoting which Special graphic to use (optional). custom_index = mrc.UInt16_BE(0x001c) #: List of Interactive object references (32 slots). interactives = mrc.BlockField(Interactive, 0x0020, count=32, fill=b'\x00' * 8) #: List of Terrain object references (400 slots). terrains = mrc.BlockField(Terrain, 0x0120, count=400, fill=b'\xff' * 4) #: List of SteelArea object references (32 slots). steel_areas = mrc.BlockField(SteelArea, 0x0760, count=32, fill=b'\x00' * 4) #: Name of the level (ASCII string). name = mrc.Bytes(0x07e0, length=32, default=b' ') @property def camera_x(self): """Start x position of the camera.""" return self.camera_x_raw - (self.camera_x_raw % 8) @property def repr(self): return self.name.strip().decode('utf8')
class Test(mrc.Block): field1 = mrc.UInt16_BE(0x00) field2 = mrc.Int32_LE() field3 = mrc.Bits8(0x08, bits=0b00111100) field4 = mrc.Bits8(0x08, bits=0b11000011) field5 = mrc.Int8(enum=TestEnum)
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'))