1import hashlib 2import os 3from binascii import unhexlify 4 5from fsgs import Option 6from fsgs.drivers.messdriver import MessDriver 7from fsgs.platform import Platform 8from fsgs.platforms.loader import SimpleLoader 9 10A7800_PLATFORM_ID = "a7800" 11A7800_PLATFORM_NAME = "Atari 7800" 12A7800_MODEL_NTSC = "ntsc" 13A7800_MODEL_PAL = "pal" 14A7800_JOYSTICK = { 15 "type": "joystick", 16 "description": "Joystick", 17 "mapping_name": "atari7800", 18} 19NO_CONTROLLER_TYPE = "none" 20NO_CONTROLLER = { 21 "type": NO_CONTROLLER_TYPE, 22 "description": "None", 23 "mapping_name": "", 24} 25A7800_PORTS = [ 26 { 27 "description": "Port 1", 28 "types": [A7800_JOYSTICK, NO_CONTROLLER], 29 "type_option": "a7800_port_1_type", 30 "device_option": "a7800_port_1", 31 }, 32 { 33 "description": "Port 2", 34 "types": [A7800_JOYSTICK, NO_CONTROLLER], 35 "type_option": "a7800_port_2_type", 36 "device_option": "a7800_port_2", 37 }, 38] 39# noinspection SpellCheckingInspection 40A7800_ROMS = { 41 "d9d134bb6b36907c615a594cc7688f7bfcef5b43": "7800.u7", 42 "14584b1eafe9721804782d4b1ac3a4a7313e455f": "c300558-001a.u7", 43} 44 45# noinspection SpellCheckingInspection 46A7800P_ROMS = {"5a140136a16d1d83e4ff32a19409ca376a8df874": "7800pal.rom"} 47 48 49class Atari7800PlatformHandler(Platform): 50 PLATFORM_NAME = A7800_PLATFORM_NAME 51 52 def driver(self, fsgc): 53 return Atari7800MameDriver(fsgc) 54 55 def loader(self, fsgc): 56 return Atari7800Loader(fsgc) 57 58 59class Atari7800Loader(SimpleLoader): 60 def load_extra(self, values): 61 self.config[Option.A7800_MODEL] = values["a7800_model"] 62 self.config[Option.A7800_A78_HEADER] = values["a78_header"] 63 self.config[Option.A7800_PORT_1_TYPE] = values["a7800_port_1_type"] 64 self.config[Option.A7800_PORT_2_TYPE] = values["a7800_port_2_type"] 65 66 67class Atari7800MameDriver(MessDriver): 68 PORTS = A7800_PORTS 69 70 def __init__(self, fsgc): 71 super().__init__(fsgc) 72 self.helper = Atari7800Helper(self.options) 73 74 def prepare(self): 75 super().prepare() 76 # self.emulator.args.extend( 77 # ["-lightgun", "-lightgun_device", "mouse"]) 78 79 def get_game_file(self, config_key="cartridge_slot"): 80 return self.helper.prepare_rom(self) 81 82 def mess_configure(self): 83 self.mess_configure_cartridge() 84 85 def mess_input_mapping(self, port): 86 return { 87 "START": "P#_START", 88 "UP": "P#_JOYSTICK_UP", 89 "DOWN": "P#_JOYSTICK_DOWN", 90 "LEFT": "P#_JOYSTICK_LEFT", 91 "RIGHT": "P#_JOYSTICK_RIGHT", 92 "1": "P#_BUTTON1", 93 "2": "P#_BUTTON2", 94 } 95 96 def mess_romset(self): 97 if self.helper.model() == A7800_MODEL_PAL: 98 return "a7800p", A7800P_ROMS 99 else: 100 return "a7800", A7800_ROMS 101 102 103class Atari7800Helper: 104 def __init__(self, options): 105 self.options = options 106 107 def model(self): 108 model = self.options[Option.A7800_MODEL] 109 if model in ["", A7800_MODEL_NTSC]: 110 return A7800_MODEL_NTSC 111 elif model == A7800_MODEL_PAL: 112 return A7800_MODEL_PAL 113 else: 114 print("[A7800] Warning: Invalid model:", model) 115 return A7800_MODEL_NTSC 116 117 def pal(self): 118 return self.model() == A7800_MODEL_PAL 119 120 def prepare_rom(self, driver): 121 file_uri = self.options[Option.CARTRIDGE_SLOT] 122 input_stream = driver.fsgc.file.open(file_uri) 123 _, ext = os.path.splitext(file_uri) 124 return self.prepare_rom_with_stream(driver, input_stream, ext) 125 126 def prepare_rom_with_stream(self, driver, input_stream, ext): 127 # This should not be necessary for files found via the file database 128 # and the online game database, but could be necessary for manually 129 # loaded files. 130 data = input_stream.read(128) 131 if len(data) == 128 and data[1:10] == b"ATARI7800": 132 print("[A7800] Stripping A78 header") 133 data = None 134 else: 135 # No A78 header, include data 136 pass 137 sha1_obj = hashlib.sha1() 138 path = driver.temp_file("rom" + ext).path 139 with open(path, "wb") as f: 140 header = self.options[Option.A7800_A78_HEADER] 141 if header: 142 assert len(header) == 128 * 2 143 f.write(unhexlify(header)) 144 sha1_obj.update(unhexlify(header)) 145 if data is not None: 146 f.write(data) 147 sha1_obj.update(data) 148 while True: 149 data = input_stream.read(65536) 150 if not data: 151 break 152 f.write(data) 153 sha1_obj.update(data) 154 new_path = os.path.join( 155 os.path.dirname(path), sha1_obj.hexdigest()[:8].upper() + ext 156 ) 157 os.rename(path, new_path) 158 return new_path 159