Ejemplo n.º 1
0
def _parse_gameboy_header(metadata: 'Metadata', header: bytes):
	nintendo_logo = header[4:0x34]
	nintendo_logo_valid = crc32(nintendo_logo) == _nintendo_logo_crc32
	metadata.specific_info['Nintendo Logo Valid'] = nintendo_logo_valid
	
	title = header[0x34:0x44]
	cgb_flag = title[15]
	title_length = 16
	try:
		metadata.specific_info['Is Colour?'] = GameBoyColourFlag(cgb_flag)
		title_length = 15
	except ValueError:
		#On older carts, this would just be the last character of the title, so it would be some random value
		#Anyway, that would logically mean it is not in colour
		metadata.specific_info['Is Colour?'] = GameBoyColourFlag.No
	#On newer games, product code is at title[11:15], the tricky part is what exactly is a newer game and what isn't, because if the product code isn't there then those characters are just the last 4 characters of the title. It seems that it's always there on GBC exclusives, and _maybe_ there on GBC-enhanced games. And that's only for officially licensed stuff of course.
	#Well, might as well try that. If it's junk, we're looking up the software list later for the proper serial anyway.
	if cgb_flag == 0xc0:
		try:
			metadata.product_code = title[11:15].decode('ascii').rstrip('\0')
			title_length = 11
		except UnicodeDecodeError:
			pass
	else:
		#Things end in null chars, so if there's null chars in the middle, that indicates if nothing else that the title ends there. If there is then 4 chars left over, that would probably be the product code
		maybe_title_and_serial = re.split(b'\0+', title[:title_length].rstrip(b'\0'))
		if len(maybe_title_and_serial) == 2:
			title_length = len(maybe_title_and_serial[0])
			if len(maybe_title_and_serial[1]) == 4:
				metadata.product_code = maybe_title_and_serial[1].decode('ascii').rstrip('\0')

	#Might as well add that to the info. I thiiink it's just ASCII and not Shift-JIS
	metadata.specific_info['Internal Title'] = title[:title_length].decode('ascii', errors='backslashreplace').rstrip('\0')
	
	metadata.specific_info['SGB Enhanced?'] = header[0x46] == 3
	if header[0x47] in game_boy_mappers:
		mapper = game_boy_mappers[header[0x47]]
		metadata.specific_info['Mapper'] = mapper.name
		metadata.save_type = SaveType.Cart if mapper.has_battery else SaveType.Nothing
		metadata.specific_info['Force Feedback?'] = mapper.has_rumble
		metadata.specific_info['Has RTC?'] = mapper.has_rtc
		if mapper.has_accelerometer:
			metadata.input_info.input_options[0].inputs.append(input_metadata.MotionControls())

	metadata.specific_info['Destination Code'] = header[0x4a]
	#0 means Japan and 1 means not Japan. Not sure how reliable that is.
	licensee_code_int = header[0x4b]
	if licensee_code_int == 0x33:
		try:
			licensee_code = convert_alphanumeric(header[0x44:0x46])
			if licensee_code in _nintendo_licensee_codes:
				metadata.publisher = _nintendo_licensee_codes[licensee_code]
		except NotAlphanumericException:
			pass
	else:
		licensee_code = '{:02X}'.format(licensee_code_int)
		if licensee_code in _nintendo_licensee_codes:
			metadata.publisher = _nintendo_licensee_codes[licensee_code]
	metadata.specific_info['Revision'] = header[0x4c]
Ejemplo n.º 2
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
Ejemplo n.º 3
0
def _parse_gba_header(metadata: 'Metadata', header: bytes):
	#Entry point: 0-4
	nintendo_logo = header[4:0xa0]
	nintendo_logo_valid = crc32(nintendo_logo) == nintendo_gba_logo_crc32
	metadata.specific_info['Nintendo Logo Valid?'] = nintendo_logo_valid
	
	internal_title = header[0xa0:0xac].decode('ascii', errors='backslashreplace').rstrip('\0')
	metadata.specific_info['Internal Title'] = internal_title
	if internal_title == 'mb2gba':
		return
	
	product_code = None
	try:
		product_code = convert_alphanumeric(header[0xac:0xb0])
		if len(product_code) == 4:
			game_type = product_code[0]
			if game_type in {'K', 'R'}:
				metadata.input_info.input_options[0].inputs.append(input_metadata.MotionControls())
			metadata.specific_info['Force Feedback?'] = game_type in {'R', 'V'}

			metadata.product_code = product_code
	except NotAlphanumericException:
		pass

	licensee_code = None
	try:
		licensee_code = convert_alphanumeric(header[0xb0:0xb2])

		if licensee_code in nintendo_licensee_codes:
			metadata.publisher = nintendo_licensee_codes[licensee_code]
	except NotAlphanumericException:
		pass

	#"Fixed value": 0xb2, apparently should be 0x96
	#Main unit code: 0xb3, apparently should be 0
	#Device type: 0xb4, apparently normally should be 0
	#Reserved: 0xb5 - 0xbc

	metadata.specific_info['Revision'] = header[0xbc]
Ejemplo n.º 4
0
def _parse_plain_region(rom: FileROM, metadata: 'Metadata', offset: int,
                        length: int):
    #Plain region stores the libraries used, at least for official games
    #See also: https://github.com/Zowayix/ROMniscience/wiki/3DS-libraries-used for research
    #Hmm… since I sort of abandoned ROMniscience I should put that somewhere else
    plain_region = rom.read(seek_to=offset, amount=length)
    libraries = (lib.decode('ascii', errors='backslashreplace')
                 for lib in plain_region.split(b'\x00') if lib)

    #TODO: If a game has an update which adds functionality identified by one of these library names, then that'll be a separate file, so it's like... how do we know that Super Smash Bros the .3ds file has amiibo support when Super Smash Bros 1.1.7 update data the .cxi is where it says that, because with and only with the update data it would support amiibos, etc; if that all makes sense
    #Unless like... I search ~/.local/share/citra-emu/sdmc/Nintendo 3DS for what update CIAs are installed and... aaaaaaaaaaaaaaaa
    for library in libraries:
        if library.startswith('[SDK+ISP:QRDec'):
            metadata.specific_info['Reads QR Codes?'] = True
        elif library.startswith('[SDK+ISP:QREnc'):
            metadata.specific_info['Makes QR Codes?'] = True
        elif library == '[SDK+NINTENDO:ExtraPad]':
            metadata.specific_info['Uses Circle Pad Pro?'] = True
            #ZL + ZR + right analog stick; New 3DS has these too but the extra controls there are internally represented as a Circle Pad Pro for compatibility so this all works out I think
            inbuilt_controller = cast(
                input_metadata.NormalController,
                cast(input_metadata.CombinedController, metadata.input_info.
                     input_options[0].inputs[0]).components[0])
            inbuilt_controller.analog_sticks += 1
            inbuilt_controller.shoulder_buttons += 2
        elif library == '[SDK+NINTENDO:Gyroscope]':
            metadata.specific_info['Uses Gyroscope?'] = True
            metadata.input_info.input_options[0].inputs.append(
                input_metadata.MotionControls())
        elif library == '[SDK+NINTENDO:IsRunOnSnake]':
            #There's also an IsRunOnSnakeForApplet found in some not-completely-sure-what-they-are builtin apps and amiibo Settings. Not sure if it does what I think it does
            metadata.specific_info['New 3DS Enhanced?'] = True
        elif library == '[SDK+NINTENDO:NFP]':
            metadata.specific_info['Uses Amiibo?'] = True
        elif library.startswith('[SDK+NINTENDO:CTRFaceLibrary-'):
            metadata.specific_info['Uses Miis?'] = True
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
def add_input_info(game: 'MAMEGame') -> None:
	game.metadata.input_info.set_inited()
	if game.machine.input_element is None:
		#Seems like this doesn't actually happen
		if main_config.debug:
			print('Oi m8', game.machine.basename, '/', game.machine.name, 'has no input')
		return

	controller = input_metadata.CombinedController()

	has_normal_input = False
	has_added_vii_motion_controls = False
	normal_input = input_metadata.NormalController()

	has_control_elements = False

	for control in game.machine.input_element.iterfind('control'):
		has_control_elements = True
		buttons = int(control.attrib.get('buttons', 0))

		if control.attrib.get('player', '1') != '1':
			#I care not for these "other people" and "social interaction" concepts
			#Anyway, this would only matter for stuff where player 2 has a different control scheme like Lucky & Wild, and... not sure what I'm gonna do about that, because we wanna avoid doubling up on input types where number of players > 1, and then that seems to be correct anyway
			continue

		#Still kinda feel like this is messy but ehhh
		#Input metadata will probably never be perfect, MAME -listxml outputs things for a different purpose really, it just be like that sometimes
		#I wonder if I'd be better off making some kind of controls.ini file myself
		input_type = control.attrib['type']
		if input_type == 'only_buttons':
			has_normal_input = True
			normal_input.face_buttons += buttons
		elif input_type == 'joy':
			has_normal_input = True
			normal_input.face_buttons += buttons
			normal_input.dpads += 1
		elif input_type == 'doublejoy':
			has_normal_input = True
			normal_input.face_buttons += buttons
			normal_input.dpads += 2
		elif input_type == 'triplejoy':
			has_normal_input = True
			normal_input.face_buttons += buttons
			normal_input.dpads += 3
		elif input_type == 'paddle':
			if game.metadata.genre == 'Driving':
				#Yeah this looks weird and hardcody and dodgy but am I wrong
				if buttons > 0:
					has_normal_input = True
					normal_input.face_buttons += buttons
				controller.components.append(input_metadata.SteeringWheel())
			elif game.machine.basename == 'vii':
				#Uses 3 "paddle" inputs to represent 3-axis motion and I guess I'll have to deal with that
				if not has_added_vii_motion_controls:
					controller.components.append(input_metadata.MotionControls())
					has_added_vii_motion_controls = True
			else:
				paddle = input_metadata.Paddle()
				paddle.buttons = buttons
				controller.components.append(paddle)
		elif input_type == 'stick':
			has_normal_input = True
			normal_input.analog_sticks += 1
			normal_input.face_buttons += buttons
		elif input_type == 'pedal':
			if buttons > 0:
				has_normal_input = True
				normal_input.face_buttons += buttons
			pedal = input_metadata.Pedal()
			controller.components.append(pedal)
		elif input_type == 'lightgun':
			#TODO: See if we can be clever and detect if this is actually a touchscreen, like platform = handheld or something
			light_gun = input_metadata.LightGun()
			light_gun.buttons = buttons
			controller.components.append(light_gun)
		elif input_type == 'positional':
			#What _is_ a positional exactly
			positional = input_metadata.Positional()
			controller.components.append(positional)
		elif input_type == 'dial':
			dial = input_metadata.Dial()
			dial.buttons = buttons
			controller.components.append(dial)
		elif input_type == 'trackball':
			trackball = input_metadata.Trackball()
			trackball.buttons = buttons
			controller.components.append(trackball)
		elif input_type == 'mouse':
			mouse = input_metadata.Mouse()
			mouse.buttons = buttons
			controller.components.append(mouse)
		elif input_type == 'keypad':
			keypad = input_metadata.Keypad()
			keypad.keys = buttons
			controller.components.append(keypad)
		elif input_type == 'keyboard':
			keyboard = input_metadata.Keyboard()
			keyboard.keys = buttons
			controller.components.append(keyboard)
		elif input_type == 'mahjong':
			mahjong = input_metadata.Mahjong()
			mahjong.buttons = buttons
			controller.components.append(mahjong)
		elif input_type == 'hanafuda':
			hanafuda = input_metadata.Hanafuda()
			hanafuda.buttons = buttons
			controller.components.append(hanafuda)
		elif input_type == 'gambling':
			gambling = input_metadata.Gambling()
			gambling.buttons = buttons
			controller.components.append(gambling)
		else:
			if buttons:
				description = 'Custom input device with {0}'.format(pluralize(buttons, 'button'))
			else:
				description = 'Custom input device'
			controller.components.append(input_metadata.Custom(description))

	if has_normal_input:
		controller.components.append(normal_input)

	if not has_control_elements:
		#Sometimes you get some games with 1 or more players, but no control type defined.  This usually happens with
		#pinball games and weird stuff like a clock, but also some genuine games like Crazy Fight that are more or less
		#playable just fine, so we'll leave them in
		if game.machine.number_of_players > 0:
			game.metadata.input_info.add_option(input_metadata.Custom('Unknown input device'))
		return

	game.metadata.input_info.add_option(controller)