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