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