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 FileLookup(mrc.Block): offset = mrc.UInt32_LE(0x00) size = mrc.UInt32_LE(0x04) file = mrc.StoreRef(mrc.Unknown, store=mrc.Ref('_parent.files_store'), offset=mrc.Ref('offset'), size=mrc.Ref('size'), transform=BoppinCompressor())
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 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 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 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))
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 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 TIMFile(mrc.Block): file_name = mrc.Bytes(length=13) size = mrc.UInt32_LE() data = mrc.Bytes(length=mrc.Ref('size')) @property def repr(self): return self.file_name.split(b'\x00')[0].decode('utf8')
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 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 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 IMGFile(mrc.Block): count = mrc.UInt32_LE(0x00) entries = mrc.BlockField(ImageEntry, 0x04, count=mrc.Ref('count')) images_raw = mrc.Bytes(mrc.EndOffset('entries')) def __init__(self, *args, **kwargs): self.images = mrc.Store(parent=self, source=mrc.Ref('images_raw'), base_offset=mrc.EndOffset('entries', neg=True)) super().__init__(*args, **kwargs)
class MUSFile(mrc.Block): count = mrc.UInt32_LE(0x00) entries = mrc.BlockField(MusicEntry, 0x04, count=mrc.Ref('count')) music_raw = mrc.Bytes(mrc.EndOffset('entries')) def __init__(self, *args, **kwargs): self.tracks = mrc.Store(parent=self, source=mrc.Ref('music_raw'), base_offset=mrc.EndOffset('entries', neg=True)) super().__init__(*args, **kwargs)
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 AEDAT(mrc.Block): offsets = mrc.UInt32_LE(0x00, stream=True, stop_check=offset_stream_end) raw_data = mrc.Bytes() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.data = mrc.LinearStore(self, source=mrc.Ref('raw_data'), offsets=mrc.Ref('offsets'), base_offset=mrc.EndOffset('offsets', neg=True), block_klass=mrc.Unknown)
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 MUSFile(mrc.Block): song_count = mrc.UInt16_LE(0x00) song_offsets = mrc.UInt32_LE(0x02, count=mrc.Ref('song_count')) songs_raw = mrc.Bytes(mrc.EndOffset('song_offsets')) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.songs = mrc.LinearStore(parent=self, source=mrc.Ref('songs_raw'), block_klass=Song, offsets=mrc.Ref('song_offsets'), base_offset=mrc.EndOffset('song_offsets', neg=True))
class ANIM(mrc.Block): unk1 = mrc.UInt16_LE(0x00) unk2 = mrc.UInt16_LE(0x02) num_frames = mrc.UInt16_LE(0x04) palette_id = mrc.UInt8(0x06) four_bit = mrc.Bits(0x07, bits=0b10000000) unk4 = mrc.Bits(0x07, bits=0b01111111) unk5 = mrc.UInt32_LE(0x08) frames = mrc.BlockField(AnimFrame, 0x0c, count=mrc.Ref('num_frames'), alignment=4) @property def palette(self): if self._parent: # HACK: get the first HEAD chunk and use the palette list head = next( (x.obj for x in self._parent.chunks if x.id == b'HEAD'), None) return head.palettes[self.palette_id].colours if head else None return []
class NEHeader(NEBase): linker_ver = mrc.UInt8(0x02) linker_rev = mrc.UInt8(0x03) entry_size = mrc.UInt16_LE(0x06) crc = mrc.UInt32_LE(0x08) ds_id = mrc.UInt16_LE(0x0e) resource_count = mrc.UInt16_LE(0x34) segtable = mrc.BlockField(SegmentHeader, 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' ) ) entrydata = mrc.Bytes(mrc.Ref('entry_offset'), length=mrc.Ref('entry_size')) nonresnametable = mrc.BlockField(ResidentNameTable, mrc.Ref('nonresnames_offset')) @property def nonresnames_offset(self): return self.nonresnames_rel_offset - (self._parent.ne_offset if self._parent else 0) def __init__(self, *args, **kwargs): self.impnamestore = mrc.Store(self, mrc.Ref('impnamedata')) super().__init__(*args, **kwargs)
class STUP( mrc.Block ): size = mrc.UInt32_LE( 0x00 ) # should be 0x14000 data = mrc.BlockField( StupData, 0x04, length=0x120 )
class Data2( mrc.Block ): payload = mrc.UInt32_LE( 0x00 )
class SoundEntry(mrc.Block): offset = mrc.UInt32_LE(0x00) size = mrc.UInt32_LE(0x04) sound = mrc.StoreRef(SoundData, mrc.Ref('_parent.sounds'), mrc.Ref('offset'), mrc.Ref('size'))
class DataBlock(mrc.Block): compat_stop = mrc.Const(mrc.UInt8(0x00), 0x66) data_type = mrc.UInt8(0x01) length = mrc.UInt32_LE(0x02) data = mrc.Bytes(0x06, length=mrc.Ref('length'))
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 MusicEntry(mrc.Block): offset = mrc.UInt32_LE(0x00) size = mrc.UInt32_LE(0x04) music = mrc.StoreRef(MusicData, mrc.Ref('_parent.tracks'), mrc.Ref('offset'), mrc.Ref('size'))
class ImageEntry(mrc.Block): offset = mrc.UInt32_LE(0x00) size = mrc.UInt32_LE(0x04) image = mrc.StoreRef(ImageData, mrc.Ref('_parent.images'), mrc.Ref('offset'), mrc.Ref('size'))
class EGAHeaderBitmapRef( mrc.Block ): width_raw = mrc.UInt16_LE( 0x00 ) height = mrc.UInt16_LE( 0x02 ) location = mrc.UInt32_LE( 0x04 ) name = mrc.Bytes( 0x08, length=8 )
class SNDS( mrc.Block ): size = mrc.UInt32_LE( 0x00 )
class TIMFileEntry(mrc.Block): _file = mrc.StoreRef(TIMFile, mrc.Ref('_parent._resource.store'), mrc.Ref('offset')) name_hash = mrc.Int32_LE() offset = mrc.UInt32_LE()