1import hashlib
2import os
3from fsgs import Option
4from fsgs.drivers.mednafendriver import MednafenDriver
5from fsgs.platform import Platform
6from fsgs.platforms.loader import SimpleLoader
7
8
9class GameGearPlatform(Platform):
10    PLATFORM_NAME = "Game Gear"
11
12    def driver(self, fsgc):
13        return MednafenGameGearDriver(fsgc)
14
15    def loader(self, fsgc):
16        return GameGearLoader(fsgc)
17
18
19class GameGearLoader(SimpleLoader):
20    pass
21
22
23class MednafenGameGearDriver(MednafenDriver):
24    CONTROLLER = {
25        "type": "gamepad",
26        "description": "Built-in Controller",
27        "mapping_name": "gamegear",
28    }
29
30    PORTS = [{"description": "Controller", "types": [CONTROLLER]}]
31
32    def __init__(self, fsgc):
33        super().__init__(fsgc)
34        self.helper = GameGearHelper(self.options)
35
36    def prepare(self):
37        super().prepare()
38        self.set_mednafen_aspect(66, 50)
39        # We do aspect calculation separately. Must not be done twice.
40        # self.emulator.args.extend(["-snes.correct_aspect", "0"])
41        rom_path = self.helper.prepare_rom(self)
42        self.emulator.args.append(rom_path)
43
44    def get_game_refresh_rate(self):
45        return 59.73
46
47    def mednafen_input_mapping(self, port):
48        return {
49            "A": "gb.input.builtin.gamepad.a",
50            "B": "gb.input.builtin.gamepad.b",
51            "UP": "gb.input.builtin.gamepad.up",
52            "DOWN": "gb.input.builtin.gamepad.down",
53            "LEFT": "gb.input.builtin.gamepad.left",
54            "RIGHT": "gb.input.builtin.gamepad.right",
55            "SELECT": "gb.input.builtin.gamepad.select",
56            "START": "gb.input.builtin.gamepad.start",
57        }
58
59    def mednafen_rom_extensions(self):
60        return [".gg"]
61
62    def mednafen_scanlines_setting(self):
63        return 0
64
65    def mednafen_special_filter(self):
66        return "nn2x"
67
68    def mednafen_system_prefix(self):
69        return "gg"
70
71    def game_video_par(self):
72        return (6.6 / 5.0) / (160 / 144)
73
74    def game_video_size(self):
75        return 160, 144
76
77    def get_game_file(self, config_key="cartridge_slot"):
78        return None
79
80
81class GameGearHelper:
82    def __init__(self, options):
83        self.options = options
84
85    def prepare_rom(self, driver):
86        file_uri = self.options[Option.CARTRIDGE_SLOT]
87        input_stream = driver.fsgc.file.open(file_uri)
88        _, ext = os.path.splitext(file_uri)
89        return self.prepare_rom_with_stream(driver, input_stream, ext)
90
91    def prepare_rom_with_stream(self, driver, input_stream, ext):
92        sha1_obj = hashlib.sha1()
93        path = driver.temp_file("rom" + ext).path
94        with open(path, "wb") as f:
95            while True:
96                data = input_stream.read(65536)
97                if not data:
98                    break
99                f.write(data)
100                sha1_obj.update(data)
101        new_path = os.path.join(
102            os.path.dirname(path), sha1_obj.hexdigest()[:8].upper() + ext
103        )
104        os.rename(path, new_path)
105        return new_path
106