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 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'')
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 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 MZHeader(mrc.Block): magic = mrc.Const(mrc.Bytes(0x00, length=2), b'MZ') last_page_size = mrc.UInt16_LE(0x02) page_count = mrc.UInt16_LE(0x04) reltable_count = mrc.UInt16_LE(0x06) header_size_raw = mrc.UInt16_LE(0x08) min_alloc_raw = mrc.UInt16_LE(0x0a) max_alloc_raw = mrc.UInt16_LE(0x0c) initial_ss = mrc.UInt16_LE(0x0e) initial_sp = mrc.UInt16_LE(0x10) checksum = mrc.UInt16_LE(0x12) initial_ip = mrc.UInt16_LE(0x14) initial_cs = mrc.UInt16_LE(0x16) reltable_offset = mrc.UInt16_LE(0x18) overlay = mrc.UInt16_LE(0x1a) @property def header_size(self): return self.header_size_raw * 16 @header_size.setter def header_size(self, value): self.header_size_raw = (value // 16) + (1 if value % 16 else 0) @property def extra_offset(self): return self.page_count * 512
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 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 Interactive(mrc.Block): """Represents a single interactive piece placed in a level.""" #: Raw value for the x position of the left edge. x_raw = mrc.Int16_BE(0x00, range=range(-8, 1601)) #: The y position of the top edge. y = mrc.Int16_BE(0x02, range=range(-41, 201)) #: Index of the InteractiveInfo block in the accompanying GroundDAT. obj_id = mrc.UInt16_BE(0x04, range=range(0, 16)) #: If 1, blit image behind background. draw_back = mrc.Bits(0x06, 0b10000000) #: If 1, draw piece flipped vertically. draw_masked = mrc.Bits(0x06, 0b01000000) #: If 1, draw piece as a hole. draw_upsidedown = mrc.Bits(0x07, 0b10000000) #: Check to ensure the last chunk of the block is empty. mod_check = mrc.Const(mrc.UInt16_BE(0x06, bitmask=b'\x3f\x7f'), 0x000f) @property def x(self): """The x position of the left edge.""" return (self.x_raw - 16) - ((self.x_raw - 16) % 8) @property def repr(self): return "obj_id={}, x={}, y={}".format(self.obj_id, self.x, self.y)
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 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 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
class RIFX(mrc.Block): _endian = 'big' CHUNK_MAP_CLASS = RIFXMap magic = mrc.Const(mrc.UInt32_P(0x00), Tag(b'RIFX')) size = mrc.UInt32_P(0x04) map = mrc.BlockField(mrc.Ref('CHUNK_MAP_CLASS'), 0x08, length=mrc.Ref('size'))
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 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 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)
class UPS(mrc.Block): STOP_CHECK = lambda buffer, pointer: pointer >= len(buffer) - 12 magic = mrc.Const(mrc.Bytes(0x00, length=4), b'UPS1') input_size = UIntVLV(0x04) output_size = UIntVLV(mrc.EndOffset('input_size')) blocks = mrc.BlockField(UPSBlock, mrc.EndOffset('output_size'), stream=True, stop_check=STOP_CHECK) input_crc32 = mrc.UInt32_LE(mrc.EndOffset('blocks')) output_crc32 = mrc.UInt32_LE(mrc.EndOffset('input_crc32')) patch_crc32 = mrc.UInt32_LE(mrc.EndOffset('output_crc32'))
class Fission(NDBase): const = mrc.Const(mrc.Bits(0x00, 0b00000111, size=1), 0b101) nd = mrc.Bits(0x00, 0b11111000, size=1) m = mrc.UInt8(0x01) def set_m(self, m): self.m = m return self @property def repr(self): return 'nd: ({}, {}, {}), m: {}'.format(self.ndx, self.ndy, self.ndz, self.m)
class IPS(mrc.Block): magic = mrc.Const(mrc.Bytes(0x00, length=5), b'PATCH') records = mrc.BlockField(IPSRecord, 0x05, stream=True, stream_end=b'EOF') @property def repr(self): return f'records: {len( self.records )}' def create(self, source, target): pass def patch(self, source): return source
class RIFF(mrc.Block): CHUNK_MAP = {} magic = mrc.Const(mrc.Bytes(0x00, length=4), b'RIFF') size = mrc.UInt32_LE(0x04) form_type = mrc.Bytes(0x08, length=4) stream = mrc.ChunkField(mrc.Ref('CHUNK_MAP'), 0x0c, length=mrc.Ref('size'), id_field=mrc.UInt32_BE, length_field=mrc.UInt32_LE, alignment=2, default_klass=mrc.Unknown)
class SMove(mrc.Block): const = mrc.Const(mrc.Bits(0x00, 0b1100111111100000, size=2), 0b000100000) llda = mrc.Bits(0x00, 0b0011000000000000, size=2) lldi = mrc.Bits(0x00, 0b0000000000011111, size=2) def set_lld(self, x, y, z): if (x == y == 0): self.lldz = z elif (x == z == 0): self.lldy = y elif (y == z == 0): self.lldx = x else: raise ValueError('only one axis allowed') return self @property def lldx(self): return 0 if self.llda != 0b01 else (self.lldi - 15) @lldx.setter def lldx(self, value): assert value in range(-15, 17) self.llda = 0b01 self.lldi = value + 15 @property def lldy(self): return 0 if self.llda != 0b10 else (self.lldi - 15) @lldy.setter def lldy(self, value): assert value in range(-15, 17) self.llda = 0b10 self.lldi = value + 15 @property def lldz(self): return 0 if self.llda != 0b11 else (self.lldi - 15) @lldz.setter def lldz(self, value): assert value in range(-15, 17) self.llda = 0b11 self.lldi = value + 15 @property def repr(self): return 'lld: ({}, {}, {})'.format(self.lldx, self.lldy, self.lldz)
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) @property def repr(self): return 'entries_max: {}, entries_used: {}'.format( self.entries_max, self.entries_used)
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 f'entries_max: {self.entries_max}, entries_used: {self.entries_used}'
class SteelArea(mrc.Block): """Represents an indestructable rectangular area in a level.""" #: Raw value (coarse component) for the x position of the left edge. x_raw_coarse = mrc.UInt8(0x00, range=range(0, 200)) #: Raw value (fine component) for the x position of the left edge. x_raw_fine = mrc.Bits(0x01, 0b10000000) #: Raw value for the y position of the area's top edge. y_raw = mrc.UInt8(0x01, bitmask=b'\x7f', range=range(0, 128)) #: Raw value for the width. width_raw = mrc.Bits(0x02, 0b11110000) #: Raw value for the height. height_raw = mrc.Bits(0x02, 0b00001111) #: Check to ensure the last byte of the block is empty. mod_check = mrc.Const(mrc.UInt8(0x03), 0x00) @property def x(self): """The x position of the left edge.""" return (self.x_raw_coarse * 2 + self.x_raw_fine) * 4 - 16 @property def y(self): """The y position of the top edge.""" return (self.y_raw * 4) @property def width(self): """Width of the steel area.""" return (self.width_raw + 1) * 4 @property def height(self): """Height of the steel area.""" return (self.height_raw + 1) * 4 @property def repr(self): return "x={}, y={}, width={}, height={}".format( self.x, self.y, self.width, self.height)
class SoundHeader( mrc.Block ): magic = mrc.Const( mrc.Bytes( 0x00, length=4 ), b'SND\x00' ) size = mrc.UInt16_LE( 0x04 ) unknown_1 = mrc.UInt16_LE( 0x06 ) count = mrc.UInt16_LE( 0x08 ) padding = mrc.Const( mrc.Bytes( 0x0a, length=6 ), b'\x00'*6 )
class Wait(mrc.Block): const = mrc.Const(mrc.UInt8(0x00), 0xfe)
class LMove(mrc.Block): const = mrc.Const(mrc.Bits(0x00, 0b0000111100000000, size=2), 0b1100) sld2a = mrc.Bits(0x00, 0b1100000000000000, size=2) sld1a = mrc.Bits(0x00, 0b0011000000000000, size=2) sld2i = mrc.Bits(0x00, 0b0000000011110000, size=2) sld1i = mrc.Bits(0x00, 0b0000000000001111, size=2) def set_sld1(self, x, y, z): if (x == y == 0): self.sld1z = z elif (x == z == 0): self.sld1y = y elif (y == z == 0): self.sld1x = x else: raise ValueError('only one axis allowed') return self def set_sld2(self, x, y, z): if (x == y == 0): self.sld2z = z elif (x == z == 0): self.sld2y = y elif (y == z == 0): self.sld2x = x else: raise ValueError('only one axis allowed') return self @property def sld2x(self): return 0 if self.sld2a != 0b01 else (self.sld2i - 5) @sld2x.setter def sld2x(self, value): assert value in range(-5, 11) self.sld2a = 0b01 self.sld2i = value + 5 @property def sld2y(self): return 0 if self.sld2a != 0b10 else (self.sld2i - 5) @sld2y.setter def sld2y(self, value): assert value in range(-5, 11) self.sld2a = 0b10 self.sld2i = value + 5 @property def sld2z(self): return 0 if self.sld2a != 0b11 else (self.sld2i - 5) @sld2z.setter def sld2z(self, value): assert value in range(-5, 11) self.sld2a = 0b11 self.sld2i = value + 5 @property def sld1x(self): return 0 if self.sld1a != 0b01 else (self.sld1i - 5) @sld1x.setter def sld1x(self, value): assert value in range(-5, 11) self.sld1a = 0b01 self.sld1i = value + 5 @property def sld1y(self): return 0 if self.sld1a != 0b10 else (self.sld1i - 5) @sld1y.setter def sld1y(self, value): assert value in range(-5, 11) self.sld1a = 0b10 self.sld1i = value + 5 @property def sld1z(self): return 0 if self.sld1a != 0b11 else (self.sld1i - 5) @sld1z.setter def sld1z(self, value): assert value in range(-5, 11) self.sld1a = 0b11 self.sld1i = value + 5 @property def repr(self): return 'sld1: ({}, {}, {}), sld2: ({}, {}, {})'.format( self.sld1x, self.sld1y, self.sld1z, self.sld2x, self.sld2y, self.sld2z)
class Halt(mrc.Block): const = mrc.Const(mrc.UInt8(0x00), 0xff)
class GVoid(FDBase): const = mrc.Const(mrc.Bits(0x00, 0b00000111, size=1), 0b000) nd = mrc.Bits(0x00, 0b11111000, size=1) fdx_raw = mrc.UInt8(0x01) fdy_raw = mrc.UInt8(0x02) fdz_raw = mrc.UInt8(0x03)
class Void(NDBase): const = mrc.Const(mrc.Bits(0x00, 0b00000111, size=1), 0b010) nd = mrc.Bits(0x00, 0b11111000, size=1)