コード例 #1
0
def _add_gamecube_disc_metadata(rom: FileROM, metadata: Metadata, header: bytes, tgc_data: Optional[Mapping[str, int]]=None):
	#TODO: Use namedtuple/dataclass for tgc_data
	metadata.platform = 'GameCube'

	if rom.extension != 'tgc':
		#Not gonna bother working out what's going on with apploader offsets in tgc
		add_apploader_date(header, metadata)

	region_code = int.from_bytes(header[0x458:0x45c], 'big')
	try:
		metadata.specific_info['Region Code'] = NintendoDiscRegion(region_code)
	except ValueError:
		pass

	if tgc_data:
		fst_offset = tgc_data['fst offset']
		fst_size = tgc_data['fst size']
	else:
		fst_offset = int.from_bytes(header[0x424:0x428], 'big')
		fst_size = int.from_bytes(header[0x428:0x42c], 'big')

	try:
		if tgc_data:
			add_fst_info(rom, metadata, fst_offset, fst_size, tgc_data['file offset'])
		else:
			add_fst_info(rom, metadata, fst_offset, fst_size)
	except (IndexError, ValueError) as ex:
		if main_config.debug:
			print(rom.path, 'encountered error when parsing FST', ex)
コード例 #2
0
def parse_woz_info_chunk(metadata: Metadata, chunk_data: bytes):
	info_version = chunk_data[0]
	#1: Disk type = 5.25" if 1 else 3.25 if 2
	#2: 1 if write protected
	if info_version == 2:
		compatible_hardware = int.from_bytes(chunk_data[40:42], 'little')
		if compatible_hardware:
			machines = set()
			if compatible_hardware & 1:
				machines.add(AppleIIHardware.AppleII)
			if compatible_hardware & 2:
				machines.add(AppleIIHardware.AppleIIPlus)
			if compatible_hardware & 4:
				machines.add(AppleIIHardware.AppleIIE)
			if compatible_hardware & 8:
				machines.add(AppleIIHardware.AppleIIC)
			if compatible_hardware & 16:
				machines.add(AppleIIHardware.AppleIIEEnhanced)
			if compatible_hardware & 32:
				machines.add(AppleIIHardware.AppleIIgs)
			if compatible_hardware & 64:
				machines.add(AppleIIHardware.AppleIICPlus)
			if compatible_hardware & 128:
				machines.add(AppleIIHardware.AppleIII)
			if compatible_hardware & 256:
				machines.add(AppleIIHardware.AppleIIIPlus)
			metadata.specific_info['Machine'] = machines
		minimum_ram = int.from_bytes(chunk_data[42:44], 'little')
		if minimum_ram:
			metadata.specific_info['Minimum RAM'] = minimum_ram
コード例 #3
0
ファイル: gametdb.py プロジェクト: Zowayix/Meow-Launcher
    def parse_genre(self, metadata: Metadata, genre_list: str):
        #genres = [g.title() for g in genre_list.split(',')]
        genres = genre_list.split(',')
        if 'software' in genres:
            #This isn't really a genre so much as a category
            genres.remove('software')

        if not genres:
            return

        items = tuple(self._organize_genres(genres).items())
        if items:
            metadata.genre = items[0][0].title()
            subgenres = items[0][1]
            if subgenres:
                metadata.subgenre = ', '.join(s.title() for s in subgenres)
            if len(items) > 1:
                metadata.specific_info['Additional Genres'] = ', '.join(
                    [g[0].title() for g in items[1:]])
                additional_subgenres = {
                    s.title()
                    for g in items[1:] for s in g[1]
                }
                if additional_subgenres:
                    metadata.specific_info['Additional Subgenres'] = ', '.join(
                        additional_subgenres)
コード例 #4
0
def add_peripherals_info(metadata: Metadata, peripherals):
    metadata.specific_info['Uses Windows CE?'] = (peripherals & 1) > 0
    metadata.specific_info['Supports VGA?'] = (peripherals & (1 << 4)) > 0
    metadata.specific_info['Uses Other Expansions?'] = (
        peripherals & (1 << 8)) > 0  #How very vague and mysterious…
    metadata.specific_info['Force Feedback?'] = (peripherals & (1 << 9)) > 0
    metadata.specific_info['Supports Microphone?'] = (peripherals &
                                                      (1 << 10)) > 0
    metadata.save_type = SaveType.MemoryCard if peripherals & (
        1 << 11) else SaveType.Nothing
    #TODO: Set up metadata.input_info

    button_bits = {
        'Start, A, B, D-pad': 12,
        'C': 13,  #Naomi?
        'D': 14,  #Naomi?
        'X': 15,
        'Y': 16,
        'Z': 17,
        'Exanded D-pad':
        18,  #Some kind of second dpad? 8-way? What does this mean
        'Analog R': 19,
        'Analog L': 20,
        'Analog Horizontal': 21,
        'Analog Vertical': 22,
        'Expanded Analog Horizontal': 23,  #What does this mean
        'Expanded Analog Vertical': 24,
    }
    buttons = {k for k, v in button_bits.items() if peripherals & (1 << v)}
    metadata.specific_info['Controls Used'] = buttons

    metadata.specific_info['Uses Gun?'] = (peripherals & (1 << 25)) > 0
    metadata.specific_info['Uses Keyboard?'] = (peripherals & (1 << 26)) > 0
    metadata.specific_info['Uses Mouse?'] = (peripherals & (1 << 27)) > 0
コード例 #5
0
	def add_related_images(self, metadata: Metadata):
		for image_name, config_key in image_config_keys.items():
			image = get_image(config_key, self.software_list_name, self.name)
			if image:
				metadata.images[image_name] = image
				continue
			if self.parent_name:
				image = get_image(config_key, self.software_list_name, self.parent_name)
				if image:
					metadata.images[image_name] = image
コード例 #6
0
ファイル: nes.py プロジェクト: Zowayix/Meow-Launcher
def add_unif_metadata(rom: FileROM, metadata: Metadata):
	metadata.specific_info['Headered?'] = True
	metadata.specific_info['Header Format'] = 'UNIF'

	pos = 32
	size = rom.size
	while pos < size:
		chunk = rom.read(amount=8, seek_to=pos)
		chunk_type = chunk[0:4].decode('ascii', errors='ignore')
		chunk_length = int.from_bytes(chunk[4:8], 'little')	
		
		chunk_data = rom.read(amount=chunk_length, seek_to=pos+8)
		parse_unif_chunk(metadata, chunk_type, chunk_data)

		pos += 8 + chunk_length
コード例 #7
0
def parse_gamecube_banner_text(metadata: Metadata, banner_bytes: bytes, encoding: str, lang: Optional[str]=None):
	short_title_line_1 = banner_bytes[0:0x20].decode(encoding, errors='backslashreplace').rstrip('\0 ')
	short_title_line_2 = banner_bytes[0x20:0x40].decode(encoding, errors='backslashreplace').rstrip('\0 ')
	title_line_1 = banner_bytes[0x40:0x80].decode(encoding, errors='backslashreplace').rstrip('\0 ')
	title_line_2 = banner_bytes[0x80:0xc0].decode(encoding, errors='backslashreplace').rstrip('\0 ')
	description = banner_bytes[0xc0:0x140].decode(encoding, errors='backslashreplace').rstrip('\0 ').replace('\n', ' ')

	prefix = 'Banner'
	if lang:
		prefix = '{0} {1}'.format(lang, prefix)
	metadata.add_alternate_name(short_title_line_1, '{0} Short Title'.format(prefix))
	metadata.specific_info['{0} Short Title Line 2'.format(prefix)] = short_title_line_2
	metadata.add_alternate_name(title_line_1, '{0} Title'.format(prefix))
	metadata.specific_info['{0} Title Line 2'.format(prefix)] = title_line_2
	metadata.descriptions['{0} Description'.format(prefix)] = description
コード例 #8
0
def add_apploader_date(header: bytes, metadata: Metadata):
	try:
		apploader_date = header[0x2440:0x2450].decode('ascii').rstrip('\0')
		try:
			actual_date = datetime.strptime(apploader_date, '%Y/%m/%d')
			year = actual_date.year
			month = actual_date.month
			day = actual_date.day
			metadata.specific_info['Build Date'] = Date(year, month, day)
			if not metadata.release_date or metadata.release_date.is_guessed:
				metadata.release_date = Date(year, month, day, True)
		except ValueError:
			pass
	except UnicodeDecodeError:
		pass
コード例 #9
0
def add_banner_info(rom: ROM, metadata: Metadata, banner: bytes):
	banner_magic = banner[:4]
	if banner_magic in {b'BNR1', b'BNR2'}:
		#(BNR2 has 6 instances of all of these with English, German, French, Spanish, Italian, Dutch in that order)
		#Dolphin uses line 2 as Publisher field but that's not always accurate (e.g. Paper Mario: The Thousand Year Door puts subtitle of the game's name on line 2) so it won't be used here
		#Very often, short title and not-short title are exactly the same, but not always. I guess it just be like that
		encoding = 'shift_jis' if metadata.specific_info['Region Code'] == NintendoDiscRegion.NTSC_J else 'latin-1'
		parse_gamecube_banner_text(metadata, banner[0x1820:0x1960], encoding)

		if banner_magic == b'BNR2':
			languages = {
				#0: English, but we have already done that
				1: 'German',
				2: 'French',
				3: 'Spanish',
				4: 'Italian',
				5: 'Dutch',
			}
			for i, lang_name in languages.items():
				offset = 0x1820 + (i * 0x140)
				parse_gamecube_banner_text(metadata, banner[offset: offset + 0x140], encoding, lang_name)

		if have_pillow:
			metadata.images['Banner'] = decode_icon(banner)
	else:
		if main_config.debug:
			print('Invalid banner magic', rom.path, banner_magic)
コード例 #10
0
def _parse_peripherals(metadata: Metadata, peripherals: Collection[str]):
    for peripheral_char in peripherals:
        if peripheral_char == 'M':
            #3 buttons if I'm not mistaken
            mouse = input_metadata.Mouse()
            mouse.buttons = 3
            metadata.input_info.add_option(mouse)
        elif peripheral_char == 'V':
            #Is this just the SMS paddle?
            metadata.input_info.add_option(input_metadata.Paddle())
        elif peripheral_char == 'A':
            xe_1_ap = input_metadata.NormalController()
            xe_1_ap.face_buttons = 10
            xe_1_ap.shoulder_buttons = 4
            xe_1_ap.analog_sticks = 2  #The second one only has one axis, though
            metadata.input_info.add_option(xe_1_ap)
        elif peripheral_char == 'G':
            menacer = input_metadata.LightGun()
            menacer.buttons = 2  #Also pause button
            metadata.input_info.add_option(menacer)
        elif peripheral_char == 'K':
            xband_keyboard = input_metadata.Keyboard()
            xband_keyboard.keys = 68  #I think I counted that right... I was just looking at the picture
            metadata.input_info.add_option(xband_keyboard)
        elif peripheral_char == 'J':
            metadata.input_info.add_option(standard_gamepad)
        elif peripheral_char == '6':
            six_button_gamepad = input_metadata.NormalController()
            six_button_gamepad.face_buttons = 6
            six_button_gamepad.dpads = 1
            metadata.input_info.add_option(six_button_gamepad)
            metadata.specific_info['Uses 6-Button Controller?'] = True
        elif peripheral_char == '0':
            sms_gamepad = input_metadata.NormalController()
            sms_gamepad.face_buttons = 2
            sms_gamepad.dpads = 1
            metadata.input_info.add_option(sms_gamepad)
        elif peripheral_char == 'L':
            #Activator
            metadata.input_info.add_option(input_metadata.MotionControls())
        elif peripheral_char in {'4', 'O'}:
            #Team Play and J-Cart respectively
            #num_players = 4
            pass
        elif peripheral_char == 'C':
            metadata.specific_info['Uses CD?'] = True
コード例 #11
0
ファイル: nes.py プロジェクト: Zowayix/Meow-Launcher
def add_fds_metadata(rom: FileROM, metadata: Metadata):
	if _nes_config and _nes_config.options.get('set_fds_as_different_platform'):
		metadata.platform = 'FDS'

	header = rom.read(amount=56)
	if header[:4] == b'FDS\x1a':
		metadata.specific_info['Headered?'] = True
		metadata.specific_info['Header Format'] = 'fwNES'
		rom.header_length_for_crc_calculation = 16
		header = rom.read(seek_to=16, amount=56)
	else:
		metadata.specific_info['Headered?'] = False

	licensee_code = f'{header[15]:02X}'
	if licensee_code in _nintendo_licensee_codes:
		metadata.publisher = _nintendo_licensee_codes[licensee_code]

	metadata.specific_info['Revision'] = header[20]
	#Uses Showa years (hence 1925), in theory... but then some disks (notably Zelda) seem to use 19xx years, as it has an actual value of 0x86 which results in it being Showa 86 = 2011, but it should be [Feb 21] 1986, so... hmm
	year = decode_bcd(header[31])
	#Showa 61 = 1986 when the FDS was released. Year > 99 wouldn't be valid BCD, so... I'll check back in 2025 to see if anyone's written homebrew for the FDS in that year and then I'll figure out what I'm doing. But homebrew right now seems to leave the year as 00 anyway, though
	year = 1925 + year if 61 <= year <= 99 else 1900 + year
	month = decode_bcd(header[32])
	day = decode_bcd(header[33])
	if not metadata.release_date:
		metadata.release_date = Date(year, month, day, True)
コード例 #12
0
def _add_info_from_copyright_string(metadata: Metadata, copyright_string: str):
    metadata.specific_info['Copyright'] = copyright_string
    copyright_match = _copyright_regex.match(copyright_string)
    if copyright_match:
        maker = copyright_match[1].strip().rstrip(',')
        maker = _t_with_zero.sub('T-', maker)
        maker = _t_not_followed_by_dash.sub('T-', maker)
        if maker in _licensee_codes:
            metadata.publisher = _licensee_codes[maker]
        year = copyright_match[2]
        month: Union[str, int]
        try:
            month = datetime.strptime(copyright_match[3], '%b').month
        except ValueError:
            #There are other spellings such as JUR, JLY out there, but oh well
            month = '??'
        metadata.specific_info['Copyright Date'] = Date(year, month)
        if not metadata.release_date:
            metadata.release_date = Date(year, month, is_guessed=True)
コード例 #13
0
def add_woz_metadata(rom: 'FileROM', metadata: Metadata):
	#https://applesaucefdc.com/woz/reference1/
	#https://applesaucefdc.com/woz/reference2/
	magic = rom.read(amount=8)
	if magic == b'WOZ1\xff\n\r\n':
		metadata.specific_info['ROM Format'] = 'WOZ v1'
	elif magic == b'WOZ2\xff\n\r\n':
		metadata.specific_info['ROM Format'] = 'WOZ v2'
	else:
		print('Weird .woz magic', rom.path, magic)
		return

	position = 12
	size = rom.size
	while position:
		position = parse_woz_chunk(rom, metadata, position)
		if position >= size:
			break
	if 'Header-Title' in metadata.names and 'Subtitle' in metadata.specific_info:
		metadata.add_alternate_name(metadata.names['Header Title'] + ': ' + metadata.specific_info['Subtitle'], 'Header Title with Subtitle')
コード例 #14
0
def add_atari_5200_footer_garbage_info(rom: 'FileROM', metadata: Metadata):
    footer = rom.read(seek_to=rom.size - 24, amount=24)
    year = footer[20:22]  #Y2K incompliant whee
    #Entry point: 22-23, lil' endian
    if year[1] != 255:  #If set to this, the BIOS is skipped?
        title_bytes = footer[:20].rstrip(b'\0')
        if title_bytes:
            title = ''.join(
                atari_5200_charset.get(b, '\0x{0:x}'.format(b))
                for b in title_bytes)
            metadata.add_alternate_name(title.strip(), 'Banner Title')
        try:
            year_first_digit = int(atari_5200_charset[year[0]])
            year_second_digit = int(atari_5200_charset[year[1]])
            terrible_date = Date(year=1900 + (year_first_digit * 10) +
                                 year_second_digit,
                                 is_guessed=True)
            if terrible_date.is_better_than(metadata.release_date):
                metadata.release_date = terrible_date
        except (ValueError, KeyError):
            pass
コード例 #15
0
ファイル: ps2.py プロジェクト: Zowayix/Meow-Launcher
def add_info_from_system_cnf(metadata: Metadata, system_cnf: str):
    for line in system_cnf.splitlines():
        boot_line_match = _boot_line_regex.match(line)
        if boot_line_match:
            filename = boot_line_match[1]
            metadata.specific_info['Executable Name'] = filename
            boot_file_match = _boot_file_regex.match(filename)
            if boot_file_match:
                metadata.product_code = boot_file_match[
                    1] + '-' + boot_file_match[2] + boot_file_match[3]
                #Can look this up in /usr/local/share/games/PCSX2/GameIndex.dbf to get PCSX2 compatibility I guess
        else:
            other_line_match = _other_systemcnf_line_regex.match(line)
            if other_line_match:
                key = other_line_match[1]
                value = other_line_match[2]
                if key == 'VER':
                    metadata.specific_info['Version'] = value
                elif key == 'VMODE':
                    try:
                        metadata.specific_info['TV Type'] = TVSystem[value]
                    except ValueError:
                        pass
コード例 #16
0
def add_cover(metadata: Metadata, product_code: str, licensee_code: str):
	#Intended for the covers database from GameTDB
	if 'Wii' not in platform_configs:
		return

	covers_path = platform_configs['Wii'].options.get('covers_path')
	if not covers_path:
		return
	cover_path = covers_path.joinpath(product_code + licensee_code)
	for ext in ('png', 'jpg'):
		potential_cover_path = cover_path.with_suffix(os.extsep + ext)
		if potential_cover_path.is_file():
			metadata.images['Cover'] = potential_cover_path
			return
コード例 #17
0
ファイル: uzebox.py プロジェクト: Zowayix/Meow-Launcher
def add_info_from_uze_header(header: bytes, metadata: Metadata):
    #Header version: 6
    #Target: 7 (0 = ATmega644, 1 = reserved for ATmega1284)
    #Program size: 8-0xc (LE)
    metadata.release_date = Date(int.from_bytes(header[0xc:0xe], 'little'))
    metadata.add_alternate_name(
        header[0xe:0x2e].decode('ascii',
                                errors='backslashreplace').rstrip('\0'),
        'Banner Title')
    metadata.developer = metadata.publisher = header[0x2e:0x4e].decode(
        'ascii', errors='backslashreplace').rstrip('\0')
    #Icon (sadly unused) (16 x 16, BBGGGRRR): 0x4e:0x14e
    #CRC32: 0x14e:0x152
    uses_mouse = header[0x152] == 1
    metadata.specific_info['Uses Mouse?'] = uses_mouse
    #Potentially it could use other weird SNES peripherals but this should do
    metadata.input_info.add_option(
        snes_controllers.mouse if uses_mouse else snes_controllers.controller)

    description = header[0x153:0x193].decode(
        'ascii', errors='backslashreplace').rstrip('\0')
    if description:
        #Official documentation claims this is unused, but it seems that it is used after all (although often identical to title)
        metadata.descriptions['Banner Description'] = description
コード例 #18
0
def add_megadrive_software_list_metadata(software: 'Software',
                                         metadata: Metadata):
    add_generic_software_info(software, metadata)
    if software.get_shared_feature('addon') == 'SVP':
        metadata.specific_info['Expansion Chip'] = 'SVP'
    if software.get_shared_feature('incompatibility') == 'TMSS':
        metadata.specific_info['Bad TMSS?'] = True

    slot = software.get_part_feature('slot')
    if slot == 'rom_eeprom' or software.has_data_area('sram'):
        metadata.save_type = SaveType.Cart
    elif metadata.platform == 'Mega Drive':
        metadata.save_type = SaveType.Nothing

    if software.name == 'aqlian':
        #This is naughty, but this bootleg game doesn't run on some stuff so I want to be able to detect it
        metadata.specific_info['Mapper'] = 'aqlian'
    else:
        if slot:
            if slot not in ('rom_sram', 'rom_fram'):
                mapper = slot[4:] if slot.startswith('rom_') else slot
                if mapper in {
                        'eeprom', 'nbajam_alt', 'nbajamte', 'nflqb96', 'cslam',
                        'nhlpa', 'blara', 'eeprom_mode1'
                }:
                    metadata.specific_info['Mapper'] = 'EEPROM'
                elif mapper == 'jcart':
                    metadata.specific_info['Mapper'] = 'J-Cart'
                elif mapper in {'codemast', 'mm96'}:
                    metadata.specific_info['Mapper'] = 'J-Cart + EEPROM'
                else:
                    #https://github.com/mamedev/mame/blob/master/src/devices/bus/megadrive/md_carts.cpp
                    metadata.specific_info['Mapper'] = mapper
            if software.name == 'pokemon' and software.software_list_name == 'megadriv':
                #This is also a bit naughty, but Pocket Monsters has different compatibility compared to other games with rom_kof99
                metadata.specific_info['Mapper'] = slot[4:] + '_pokemon'
コード例 #19
0
def add_gamecube_wii_disc_metadata(rom: FileROM, metadata: Metadata, header: bytes):
	internal_title = header[32:128]
	metadata.specific_info['Internal Title'] = internal_title.decode('ascii', errors='backslashreplace').rstrip('\0 ')
	if internal_title[:28] == b'GAMECUBE HOMEBREW BOOTLOADER':
		return

	product_code = None
	try:
		product_code = convert_alphanumeric(header[:4])
	except NotAlphanumericException:
		pass

	publisher = None
	licensee_code = None
	try:
		licensee_code = convert_alphanumeric(header[4:6])
		publisher = nintendo_licensee_codes.get(licensee_code)
	except NotAlphanumericException:
		pass

	if not (product_code == 'RELS' and licensee_code == 'AB'):
		# This is found on a few prototype discs, it's not valid
		metadata.product_code = product_code
		metadata.publisher = publisher
		if product_code and licensee_code:
			add_info_from_tdb(_tdb, metadata, product_code + licensee_code)
			add_cover(metadata, product_code, licensee_code)

	disc_number = header[6] + 1
	if disc_number:
		metadata.disc_number = disc_number

	metadata.specific_info['Revision'] = header[7]

	#Audio streaming: header[8] > 1
	#Audio streaming buffer size: header[9]
	#Unused: 10-24

	is_wii = header[0x18:0x1c] == b']\x1c\x9e\xa3'
	is_gamecube = header[0x1c:0x20] == b'\xc23\x9f='
	# Is this ever set to both? In theory no, but... hmm

	if not is_wii and not is_gamecube:
		metadata.specific_info['No Disc Magic?'] = True
	elif main_config.debug:
		if metadata.platform == 'Wii' and not is_wii:
			print(rom.path, 'lacks Wii disc magic')
		if metadata.platform == 'GameCube' and not is_gamecube:
			print(rom.path, 'lacks GameCube disc magic')
コード例 #20
0
ファイル: nes.py プロジェクト: Zowayix/Meow-Launcher
def add_nes_software_list_metadata(software: 'Software', metadata: Metadata):
	software.add_standard_metadata(metadata)

	nes_peripheral = None

	#FIXME: Acktually, you can have multiple feature = peripherals
	#See also: SMB / Duck Hunt / World Class Track Meet multicart, with both zapper and powerpad
	#Actually, how does that even work in real life? Are the controllers hotplugged? Different ports?
	peripheral = software.get_part_feature('peripheral')
	if peripheral == 'zapper':
		nes_peripheral = NESPeripheral.Zapper
		zapper = input_metadata.LightGun()
		zapper.buttons = 1
		metadata.input_info.add_option(zapper)
	elif peripheral == 'vaus':
		nes_peripheral = NESPeripheral.ArkanoidPaddle
		vaus = input_metadata.Paddle()
		vaus.buttons = 1
		metadata.input_info.add_option(vaus)
		#Can still use standard controller
		metadata.input_info.add_option(_standard_controller)
	elif peripheral in {'powerpad', 'ftrainer', 'fffitness'}:
		nes_peripheral = NESPeripheral.PowerPad

		power_pad = input_metadata.NormalController()
		power_pad.face_buttons = 12 #"face"
		metadata.input_info.add_option(power_pad)
	elif peripheral == 'powerglove':
		nes_peripheral = NESPeripheral.PowerGlove
		#Hmm... apparently it functions as a standard NES controller, but there are 2 games specifically designed for glove usage? So it must do something extra I guess

		power_glove = input_metadata.MotionControls()
		#game.metadata.input_info.buttons = 11 #Standard A + B + 9 program buttons
		metadata.input_info.add_option(power_glove)
	elif peripheral == 'rob':
		nes_peripheral = NESPeripheral.ROB
		#I'll leave input info alone, because I'm not sure how I would classify ROB
		metadata.input_info.add_option(_standard_controller)
	elif peripheral == 'fc_keyboard':
		nes_peripheral = NESPeripheral.FamicomKeyboard

		famicom_keyboard = input_metadata.Keyboard()
		famicom_keyboard.keys = 72
		metadata.input_info.add_option(famicom_keyboard)
	elif peripheral == 'subor_keyboard':
		nes_peripheral = NESPeripheral.SuborKeyboard

		subor_keyboard = input_metadata.Keyboard()
		subor_keyboard.keys = 96
		metadata.input_info.add_option(subor_keyboard)
	elif peripheral == 'mpiano':
		nes_peripheral = NESPeripheral.Piano
		#Apparently, it's actually just a MIDI keyboard, hence the MAME driver adds MIDI in/out ports

		miracle_piano = input_metadata.Custom('88-key piano')
		#game.metadata.input_info.buttons = 88
		metadata.input_info.add_option(miracle_piano)
	else:
		metadata.input_info.add_option(_standard_controller)

	#Well, it wouldn't be a controller... not sure how this one works exactly
	metadata.specific_info['Uses 3D Glasses?'] = peripheral == '3dglasses'
	if peripheral == 'turbofile':
		#Thing that goes into Famicom controller expansion port and saves stuff
		metadata.save_type = SaveType.MemoryCard
	#There's a "battlebox" which Armadillo (Japan) uses?
	#Barcode World (Japan) uses "barcode"
	#Peripheral = 4p_adapter: 4 players
	#Gimmi a Break stuff: "partytap"?
	#Hyper Olympic (Japan): "hypershot"
	#Ide Yousuke Meijin no Jissen Mahjong (Jpn, Rev. A): "mjcontroller" (mahjong controller?)
	#RacerMate Challenge 2: "racermate"
	#Top Rider (Japan): "toprider"

	metadata.add_notes(software.infos.get('usage'))
	#This only works on a Famicom with Mahjong Controller attached
	#This only is only supported by Famicom [sic?]

	if nes_peripheral:
		metadata.specific_info['Peripheral'] = nes_peripheral
コード例 #21
0
    def __init__(self, machine: 'Machine', platform_config: 'PlatformConfig'):
        super().__init__(platform_config)
        self.machine = machine
        self.metadata = Metadata()

        self._add_metadata_fields()
コード例 #22
0
	def add_standard_metadata(self, metadata: Metadata):
		metadata.specific_info['MAME Software'] = self
		#We'll need to use that as more than just a name, though, I think; and by that I mean I get dizzy if I think about whether I need to do that or not right now
		#TODO: Whatever is checking metadata.names needs to just check for game.software etc manually rather than this being here, I think
		metadata.add_alternate_name(self.description, 'Software List Name')

		metadata.specific_info['MAME Software List'] = self.software_list

		if not metadata.product_code:
			metadata.product_code = self.serial
		barcode = self.infos.get('barcode')
		if barcode:
			metadata.specific_info['Barcode'] = barcode
		ring_code = self.infos.get('ring_code')
		if ring_code:
			metadata.specific_info['Ring Code'] = ring_code
		
		version = self.infos.get('version')
		if version:
			if version[0].isdigit():
				version = 'v' + version
			metadata.specific_info['Version'] = version

		alt_title = self.infos.get('alt_title', self.infos.get('alt_name', self.infos.get('alt_disk')))
		if alt_title:
			_add_alt_titles(metadata, alt_title)

		year_text = self.xml.findtext('year')
		if year_text:
			year_guessed = False
			if len(year_text) == 5 and year_text[-1] == '?':
				#Guess I've created a year 10000 problem, please fix this code in several millennia to be more smart
				year_guessed = True
				year_text = year_text[:-1]
			year = Date(year_text, is_guessed=year_guessed)
			if year.is_better_than(metadata.release_date):
				metadata.release_date = year

		release = self.infos.get('release')
		release_date: Optional[Date] = None
		if release:
			release_date = _parse_release_date(release)

		if release_date:
			if release_date.is_better_than(metadata.release_date):
				metadata.release_date = release_date

		developer = consistentify_manufacturer(self.infos.get('developer'))
		if not developer:
			developer = consistentify_manufacturer(self.infos.get('author'))
		if not developer:
			developer = consistentify_manufacturer(self.infos.get('programmer'))
		if developer:
			metadata.developer = developer

		publisher = consistentify_manufacturer(self.xml.findtext('publisher'))
		if publisher:
			already_has_publisher = metadata.publisher and (not metadata.publisher.startswith('<unknown'))
			if publisher in {'<doujin>', '<homebrew>', '<unlicensed>'} and developer:
				metadata.publisher = developer
			elif not (already_has_publisher and (publisher == '<unknown>')):
				if ' / ' in publisher:
					publishers: Collection[str] = set(cast(str, consistentify_manufacturer(p)) for p in publisher.split(' / '))
					if main_config.sort_multiple_dev_names:
						publishers = sorted(publishers)
					publisher = ', '.join(publishers)

				metadata.publisher = publisher

		self.add_related_images(metadata)

		add_history(metadata, self.software_list_name, self.name)
コード例 #23
0
def parse_woz_kv(rompath: str, metadata: Metadata, key: str, value: str):
	#rompath is just here for making warnings look better which is a bit silly I think… hm
	if key in {'side', 'side_name', 'contributor', 'image_date', 'collection', 'requires_platform'}:
		#No use for these
		#"collection" is not part of the spec but it shows up and it just says where the image came from
		#requires_platform is not either, it just seems to be "apple2" so far and I don't get it
		return

	if key == 'version':
		#Note that this is free text
		if not value.startswith('v'):
			value = 'v' + value
		metadata.specific_info['Version'] = value
	elif key == 'title':
		metadata.add_alternate_name(value, 'Header Title')
	elif key == 'subtitle':
		metadata.specific_info['Subtitle'] = value
	elif key == 'requires_machine':
		if metadata.specific_info.get('Machine'):
			#Trust the info from the INFO chunk more if it exists
			return
		machines = set()
		for machine in value.split('|'):
			if machine in woz_meta_machines:
				machines.add(woz_meta_machines[machine])
			else:
				print('Unknown compatible machine in Woz META chunk', rompath, machine)
		metadata.specific_info['Machine'] = machines
	elif key == 'requires_ram':
		#Should be in INFO chunk, but sometimes isn't
		if value[-1].lower() == 'k':
			value = value[:-1]
		try:
			metadata.specific_info['Minimum RAM'] = int(value)
		except ValueError:
			pass
	elif key == 'publisher':
		metadata.publisher = consistentify_manufacturer(value)
	elif key == 'developer':
		metadata.developer = consistentify_manufacturer(value)
	elif key == 'copyright':
		metadata.specific_info['Copyright'] = value
		try:
			metadata.release_date = Date(value)
		except ValueError:
			pass
	elif key == 'language':
		metadata.languages = {lang for lang in (get_language_by_english_name(lang_name) for lang_name in value.split('|')) if lang}
	elif key == 'genre':
		#This isn't part of the specification, but I've seen it
		if value == 'rpg':
			metadata.genre = 'RPG'
		else:
			metadata.genre = value.capitalize() if value.islower() else value
	elif key == 'notes':
		#This isn't part of the specification, but I've seen it
		metadata.add_notes(value)
	else:
		if main_config.debug:
			print('Unknown Woz META key', rompath, key, value)
コード例 #24
0
def _add_alt_titles(metadata: Metadata, alt_title: str):
	#Argh this is annoying because we don't want to split in the middle of brackets
	for piece in _split_preserve_brackets.split(alt_title):
		ends_with_brackets_match = _ends_with_brackets.match(piece)
		if ends_with_brackets_match:
			name_type = ends_with_brackets_match[2]
			if name_type in {'Box', 'USA Box', 'US Box', 'French Box', 'Box?', 'Cart', 'cart', 'Label', 'label', 'Fra Box'}:
				#There must be a better way for me to do this…
				metadata.add_alternate_name(ends_with_brackets_match[1], name_type.title() + ' Title')
			elif name_type in {'Box, Cart', 'Box/Card'}:
				#Grr
				metadata.add_alternate_name(ends_with_brackets_match[1], 'Box Title')
				metadata.add_alternate_name(ends_with_brackets_match[1], 'Cart Title')
			elif name_type == 'Japan':
				metadata.add_alternate_name(ends_with_brackets_match[1], 'Japanese Name')
			elif name_type == 'China':
				metadata.add_alternate_name(ends_with_brackets_match[1], 'Chinese Name')
			else:
				#Sometimes the brackets are actually part of the name
				metadata.add_alternate_name(piece, name_type)
		else:
			metadata.add_alternate_name(piece)
コード例 #25
0
ファイル: gametdb.py プロジェクト: Zowayix/Meow-Launcher
def _add_info_from_tdb_entry(tdb: TDB, db_entry: ElementTree.Element,
                             metadata: Metadata):
    metadata.add_alternate_name(db_entry.attrib['name'], 'GameTDB Name')
    #(Pylint is on drugs if I don't add more text here) id: What we just found
    #(it thinks I need an indented block) type: 3DS, 3DSWare, VC, etc (we probably don't need to worry about that)
    #region: PAL, etc (we can see region code already)
    #languages: "EN" "JA" etc (I guess we could parse this if the filename isn't good enough for us)
    #rom: What they think the ROM should be named
    #case: Has "color" and "versions" attribute? I don't know what versions does but I presume it all has to do with the db_entry box

    if main_config.debug:
        for element in db_entry:
            if element.tag not in ('developer', 'publisher', 'date', 'rating',
                                   'id', 'type', 'region', 'languages',
                                   'locale', 'genre', 'wi-fi', 'input', 'rom',
                                   'case', 'save'):
                print('uwu', db_entry.attrib['name'], 'has unknown', element,
                      'tag')

    developer = db_entry.findtext('developer')
    if developer and developer != 'N/A':
        metadata.developer = _clean_up_company_name(developer)
    publisher = db_entry.findtext('publisher')
    if publisher:
        metadata.publisher = _clean_up_company_name(publisher)
    date = db_entry.find('date')
    if date is not None:
        year = date.attrib.get('year')
        month = date.attrib.get('month')
        day = date.attrib.get('day')
        if any([year, month, day]):
            metadata.release_date = Date(year, month, day)

    genre = db_entry.findtext('genre')
    if genre:
        tdb.parse_genre(metadata, genre)

    for locale in db_entry.iterfind('locale'):
        synopsis = locale.findtext('synopsis')
        if synopsis:
            key_name = f"Synopsis-{locale.attrib.get('lang')}" if 'lang' in locale.attrib else 'Synopsis'
            metadata.descriptions[key_name] = synopsis

    rating = db_entry.find('rating')
    if rating is not None:
        #Rating board (attrib "type") is implied by region (db_entrys released in e.g. both Europe and Australia just tend to not have this here)
        value = rating.attrib.get('value')
        if value:
            metadata.specific_info['Age Rating'] = value

        descriptors = {e.text for e in rating.iterfind('descriptor')}
        if descriptors:
            metadata.specific_info['Content Warnings'] = descriptors

    #This stuff will depend on platform…

    save = db_entry.find('save')
    if save is not None:
        blocks = save.attrib.get('blocks')
        #Other platforms may have "size" instead, also there are "copy" and "move" attributes which we'll ignore
        if blocks:
            if metadata.platform == 'Wii':
                metadata.save_type = SaveType.Internal
            elif metadata.platform == 'GameCube':
                metadata.save_type = SaveType.MemoryCard
        #Have not seen a db_entry with blocks = 0 or missing blocks or size

    if metadata.platform != 'GameCube':
        wifi = db_entry.find('wi-fi')
        if wifi:
            features = {feature.text for feature in wifi.iterfind('feature')}
            metadata.specific_info['Wifi Features'] = features
            #online, download, score, nintendods

    input_element = db_entry.find('input')
    if input_element is not None:
        #TODO: DS has players-multi-cart and players-single-cart instead (which one do I want?)
        number_of_players = input_element.attrib.get('players', None)
        if number_of_players is not None:  #Maybe 0 could be a valid amount? For like demos or something
            metadata.specific_info['Number of Players'] = number_of_players

        if metadata.platform != 'GameCube':
            #wiimote, nunchuk, motionplus, db_entrycube, nintendods, classiccontroller, wheel, zapper, balanceboard, wiispeak, microphone, guitar, drums, dancepad, keyboard, draw
            optional_controls = set()
            required_controls = set()
            for control in input_element.iterfind('control'):
                control_type = control.attrib.get('type')
                if control.attrib.get('required', 'false') == 'true':
                    required_controls.add(control_type)
                else:
                    optional_controls.add(control_type)

                #cbf setting up input_info just yet
                metadata.specific_info[
                    'Optional Additional Controls'] = optional_controls
                metadata.specific_info[
                    'Required Additional Controls'] = required_controls
コード例 #26
0
ファイル: nes.py プロジェクト: Zowayix/Meow-Launcher
def add_ines_metadata(rom: FileROM, metadata: Metadata, header: bytes):
	metadata.specific_info['Headered?'] = True
	#Some emulators are okay with not having a header if they have something like an internal database, others are not.
	#Note that \x00 at the end instead of \x1a indicates this is actually Wii U VC, but it's still the same header format
	rom.header_length_for_crc_calculation = 16 #We use a custom software list matcher anyway, but we need to just chop the header off to find it in libretro-database
	prg_size = header[4]
	chr_size = header[5]

	flags = header[6]
	has_battery = (flags & 2) > 0
	metadata.save_type = SaveType.Cart if has_battery else SaveType.Nothing
	if (flags & 4) > 0:
		metadata.specific_info['Has iNES Trainer?'] = True
	mapper_lower_nibble = (flags & 0b1111_0000) >> 4

	more_flags = header[7]
	if (more_flags & 3) == 1:
		metadata.specific_info['Arcade System'] = 'VS Unisystem'
	elif (more_flags & 3) == 2:
		metadata.specific_info['Arcade System'] = 'PlayChoice-10'

	mapper_upper_nibble = more_flags & 0b1111_0000
	is_nes_2_0 = ((more_flags & 0b_00_00_11_00) >> 2) == 2
	if is_nes_2_0:
		metadata.specific_info['Header Format'] = 'NES 2.0'
		mapper_upper_upper_nibble = header[8] & 0b1111
		mapper = mapper_lower_nibble | mapper_upper_nibble | (mapper_upper_upper_nibble << 8)
		metadata.specific_info['Mapper Number'] = mapper
		if mapper in _ines_mappers:
			metadata.specific_info['Mapper'] = _ines_mappers[mapper]
		else:
			metadata.specific_info['Mapper'] = 'NES 2.0 Mapper %d' % mapper

		metadata.specific_info['Submapper'] = (header[8] & 0b1111_0000) >> 4
		
		prg_size_msb = ((header[9] & 0b1111) << 4)
		metadata.specific_info['PRG Size'] = (prg_size_msb | prg_size) * 16 * 1024 if prg_size_msb != 15 else (2 ** ((prg_size & 0b1111_1100) >> 2)) * (((prg_size & 0b11) * 2) + 1)
		chr_size_msb = (header[9] & 0b1111_0000)
		metadata.specific_info['CHR Size'] = (chr_size_msb | chr_size) * 8 * 1024 if chr_size_msb != 15 else (2 ** ((chr_size & 0b1111_1100) >> 2)) * (((chr_size & 0b11) * 2) + 1)

		#9/10: PRG/CHR RAM and NVRAM size

		cpu_ppu_timing = header[12] & 0b11
		if cpu_ppu_timing == 0:
			metadata.specific_info['TV Type'] = TVSystem.NTSC
		elif cpu_ppu_timing == 1:
			metadata.specific_info['TV Type'] = TVSystem.PAL
		elif cpu_ppu_timing == 2:
			metadata.specific_info['TV Type'] = TVSystem.Agnostic
		elif cpu_ppu_timing == 3:
			metadata.specific_info['Is Dendy?'] = True

		if (header[7] & 3) == 3:
			#If header[7] = 1, specifies VS System type
			metadata.specific_info['Extended Console Type'] = extended_console_types.get(header[13], header[13])
		if header[15]:
			default_expansion_device = default_expansion_devices.get(header[15], header[15])
			metadata.specific_info['Default Expansion Device'] = default_expansion_device
			if default_expansion_device == 1:
				metadata.specific_info['Peripheral'] = NESPeripheral.NormalController
			#42 = multicart also exists I guess but it doesn't mean much to us
	else:
		metadata.specific_info['Header Format'] = 'iNES'
		mapper = mapper_lower_nibble | mapper_upper_nibble
		metadata.specific_info['Mapper Number'] = mapper
		if mapper in _ines_mappers:
			metadata.specific_info['Mapper'] = _ines_mappers[mapper]
		else:
			metadata.specific_info['Mapper'] = 'iNES Mapper %d' % mapper

		metadata.specific_info['PRG Size'] = prg_size * 16 * 1024
		metadata.specific_info['CHR Size'] = chr_size * 8 * 1024
コード例 #27
0
ファイル: nes.py プロジェクト: Zowayix/Meow-Launcher
def parse_unif_chunk(metadata: Metadata, chunk_type: str, chunk_data: bytes):
	if chunk_type == 'PRG0':
		metadata.specific_info['PRG CRC'] = get_crc32_for_software_list(chunk_data)
	elif chunk_type.startswith('CHR'):
		metadata.specific_info['CHR CRC'] = get_crc32_for_software_list(chunk_data)
	elif chunk_type == 'MAPR':
		metadata.specific_info['Mapper'] = chunk_data.decode('utf-8', errors='ignore').rstrip('\0')
	elif chunk_type == 'TVCI':
		tv_type = chunk_data[0]
		if tv_type == 0:
			metadata.specific_info['TV Type'] = TVSystem.NTSC
		elif tv_type == 1:
			metadata.specific_info['TV Type'] = TVSystem.PAL
		elif tv_type == 2:
			metadata.specific_info['TV Type'] = TVSystem.Agnostic
	elif chunk_type == 'BATR':
		metadata.save_type = SaveType.Cart if chunk_data[0] else SaveType.Nothing
	elif chunk_type == 'CTRL':
		controller_info = chunk_data[0]
		#TODO: This is a bitfield, so actually one could have multiple peripherals
		if controller_info & 16:
			metadata.specific_info['Peripheral'] = NESPeripheral.PowerPad
		if controller_info & 8:
			metadata.specific_info['Peripheral'] = NESPeripheral.ArkanoidPaddle
		if controller_info & 4:
			metadata.specific_info['Peripheral'] = NESPeripheral.ROB
		if controller_info & 2:
			metadata.specific_info['Peripheral'] = NESPeripheral.Zapper
		if controller_info & 1:
			metadata.specific_info['Peripheral'] = NESPeripheral.NormalController
	elif chunk_type == 'READ':
		metadata.add_notes(chunk_data.decode('utf-8', errors='ignore').rstrip('\0'))
	elif chunk_type == 'NAME':
		metadata.add_alternate_name(chunk_data.decode('utf-8', errors='ignore').rstrip('\0'), 'Header Title')
コード例 #28
0
def add_megadrive_info(metadata: Metadata, header: bytes):
    try:
        console_name = header[:16].decode('ascii')
    except UnicodeDecodeError:
        metadata.specific_info['Bad TMSS?'] = True
        return

    if not console_name.startswith('SEGA') and not console_name.startswith(
            ' SEGA') and console_name not in ('IMA IKUNOUJYUKU ',
                                              'IMA IKUNOJYUKU  ',
                                              'SAMSUNG PICO    '):
        metadata.specific_info['Console Name'] = console_name
        metadata.specific_info['Bad TMSS?'] = True
        return

    if metadata.platform == 'Mega CD' and console_name.startswith('SEGA 32X'):
        #Could also set platform to something like "Mega CD 32X" I guess
        metadata.specific_info['32X Only?'] = True

    try:
        copyright_string = header[16:32].decode('ascii')
        _add_info_from_copyright_string(metadata, copyright_string)
    except UnicodeDecodeError:
        pass

    domestic_title = header[32:80].decode(
        'shift_jis', errors='backslashreplace').rstrip('\0 ')
    overseas_title = header[80:128].decode(
        'shift_jis', errors='backslashreplace').rstrip('\0 ')
    if domestic_title:
        metadata.specific_info['Internal Title'] = domestic_title
    if overseas_title:
        #Often the same as domestic title, but for games that get their names changed yet work on multiple regions, domestic is the title in Japan and and overseas is in USA (and maybe Europe). I don't know what happens if a game is originally in USA then gets its name changed when it goes to Japan, but it might just be "Japan is domestic and everwhere else is overseas"
        metadata.specific_info['Internal Overseas Title'] = overseas_title
    #Product type: 128:130, it's usually GM for game but then some other values appear too (especially in Sega Pico)
    #Space for padding: 130

    try:
        serial = header[131:142].decode('ascii')
        metadata.product_code = serial[:8].rstrip('\0 ')
        #- in between
        version = serial[-2]
        if version.isdigit():
            metadata.specific_info['Revision'] = int(version)
    except UnicodeDecodeError:
        pass
    #Checksum: header[142:144]

    peripherals = {
        c
        for c in header[144:160].decode('ascii', errors='ignore')
        if c not in ('\x00', ' ')
    }
    _parse_peripherals(metadata, peripherals)

    if metadata.platform == 'Mega Drive':
        save_id = header[0xb0:0xb4]
        #Apparently... what the heck
        #This seems to be different on Mega CD, and also 32X
        metadata.save_type = SaveType.Cart if save_id[:
                                                      2] == b'RA' else SaveType.Nothing

    modem_info = header[0xbc:0xc8]
    memo_bytes = header[0xc8:0xf0]
    modem_string = None
    if modem_info[:2] == b'MO':
        metadata.specific_info['Supports Modem?'] = True
    elif modem_info[:11] == b'No modem...':
        metadata.specific_info['Supports Modem?'] = False
    else:
        modem_string = modem_info.decode('ascii', errors='ignore').strip('\0 ')

    try:
        memo = memo_bytes.decode('ascii').strip('\0 ')
        if modem_string:
            #Not really correct, but a few homebrews use the modem part to put in a longer message (and sometimes, varying amounts of it - the first 2 or 4 bytes might be filled with garbage data…)
            memo = modem_string + memo

        if memo:
            if memo == 'SV':
                metadata.specific_info['Expansion Chip'] = 'SVP'
            else:
                #This only seems to really be used for homebrews bootlegs etc
                metadata.descriptions['Memo'] = memo
    except UnicodeDecodeError:
        pass

    regions = header[0xf0:0xf3]
    region_codes = _parse_region_codes(regions)
    metadata.specific_info['Region Code'] = region_codes
    if console_name[:12] == 'SEGA GENESIS' and not region_codes:
        #Make a cheeky guess (if it wasn't USA it would be SEGA MEGADRIVE etc presumably)
        metadata.specific_info['Region Code'] = [MegadriveRegionCodes.USA]
コード例 #29
0
	def __init__(self) -> None:
		self.metadata = Metadata()
コード例 #30
0
    def __init__(self, software, platform, media_type):
        self.software = software
        self.platform = platform
        self.media_type = media_type

        self.metadata = Metadata()