diff --git a/CHANGES.md b/CHANGES.md index 5fe9261..832790f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,10 @@ # Release notes +### v3.35 (released 2023-11-25) +- Added support for DRV with 29LV320DB and ALTERA CPLD *(thanks TheNFCookie)* +- Added support for HC007-BGA-V2 with M29W640 *(thanks LucentW)* +- Added support for reading and writing the save data of some GBA multigame bootleg cartridges +- Updated the Game Boy and Game Boy Advance lookup databases for save types, ROM sizes and checksums + ### v3.34 (released 2023-09-26) - Minor bug fixes and improvements diff --git a/FlashGBX/FlashGBX_CLI.py b/FlashGBX/FlashGBX_CLI.py index b1b09c0..d01772f 100644 --- a/FlashGBX/FlashGBX_CLI.py +++ b/FlashGBX/FlashGBX_CLI.py @@ -54,7 +54,7 @@ def run(self): args = self.ARGS["argparsed"] config_path = Util.formatPathOS(Util.CONFIG_PATH) - print("Configuration directory: {:s}\n".format(config_path)) + print("Configuration folder: {:s}\n".format(config_path)) # Ask interactively if no args set if args.action is None: @@ -961,7 +961,7 @@ def FlashROM(self, args, header): if answer != "n": fix_bootlogo = bootlogo else: - Util.dprint("Couldn’t find boot logo file in configuration directory.") + Util.dprint("Couldn’t find boot logo file in configuration folder.") if not hdr["header_checksum_correct"] and (self.CONN.GetMode() == "AGB" or (self.CONN.GetMode() == "DMG" and mbc not in (0x203, 0x205))): print("{:s}WARNING: The ROM file you selected will not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).{:s}".format(ANSI.YELLOW, hdr["header_checksum_calc"], hdr["header_checksum"], ANSI.RESET)) diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py index ffa4b32..cb82ee3 100644 --- a/FlashGBX/FlashGBX_GUI.py +++ b/FlashGBX/FlashGBX_GUI.py @@ -210,7 +210,7 @@ def __init__(self, args): self.mnuConfig.addSeparator() self.mnuConfig.addAction("Re-&enable suppressed messages", self.ReEnableMessages) self.mnuConfig.addSeparator() - self.mnuConfig.addAction("Show &configuration directory", self.OpenPath) + self.mnuConfig.addAction("Open &config folder", self.OpenPath) self.mnuConfig.actions()[0].setCheckable(True) self.mnuConfig.actions()[1].setCheckable(True) self.mnuConfig.actions()[2].setCheckable(True) @@ -604,7 +604,9 @@ def ReEnableMessages(self): def OpenPath(self, path=None): if path is None: path = Util.CONFIG_PATH - self.WriteDebugLogOnShiftKey() + kbmod = QtWidgets.QApplication.keyboardModifiers() + if kbmod != QtCore.Qt.ShiftModifier: + self.WriteDebugLog() path = 'file://{0:s}'.format(path) try: if platform.system() == "Windows": @@ -615,11 +617,6 @@ def OpenPath(self, path=None): subprocess.Popen(["xdg-open", path]) except: QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "The path is:\n{:s}".format(path), QtWidgets.QMessageBox.Ok) - - def WriteDebugLogOnShiftKey(self, event=None): - kbmod = QtWidgets.QApplication.keyboardModifiers() - if kbmod == QtCore.Qt.ShiftModifier: - self.WriteDebugLog() def WriteDebugLog(self, event=None): try: @@ -910,7 +907,7 @@ def FinishOperation(self): else: button_dump_report = msgbox.addButton(" Generate Dump &Report ", QtWidgets.QMessageBox.ActionRole) - button_open_dir = msgbox.addButton(" Open &Directory ", QtWidgets.QMessageBox.ActionRole) + button_open_dir = msgbox.addButton(" Open Fol&der ", QtWidgets.QMessageBox.ActionRole) if self.CONN.GetMode() == "DMG": if self.CONN.INFO["rom_checksum"] == self.CONN.INFO["rom_checksum_calc"]: @@ -1025,7 +1022,7 @@ def FinishOperation(self): return if "last_path" in self.CONN.INFO: - button_open_dir = msgbox.addButton(" Open &Directory ", QtWidgets.QMessageBox.ActionRole) + button_open_dir = msgbox.addButton(" Open Fol&der ", QtWidgets.QMessageBox.ActionRole) msgbox.setText("The save data backup is complete!" + msg_te) msgbox.exec() if "last_path" in self.CONN.INFO and msgbox.clickedButton() == button_open_dir: @@ -1368,7 +1365,7 @@ def FlashROM(self, dpath=""): elif msgbox.clickedButton() == button_2: pass else: - Util.dprint("Couldn’t find boot logo file in configuration directory.") + Util.dprint("Couldn’t find boot logo file in configuration folder.") msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg_text) msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) msgbox.setDefaultButton(QtWidgets.QMessageBox.Cancel) @@ -1431,6 +1428,7 @@ def BackupRAM(self): path = Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=self.SETTINGS) path = os.path.splitext(path)[0] path += "{:s}.sav".format(path_datetime) + cart_type = 0 if self.CONN.GetMode() == "DMG": setting_name = "LastDirSaveDataDMG" last_dir = self.SETTINGS.value(setting_name) @@ -1440,6 +1438,7 @@ def BackupRAM(self): if save_type == 0: QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok) return + cart_type = self.cmbDMGCartridgeTypeResult.currentIndex() #save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(save_type)] elif self.CONN.GetMode() == "AGB": @@ -1451,6 +1450,7 @@ def BackupRAM(self): if save_type == 0: QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok) return + cart_type = self.cmbAGBCartridgeTypeResult.currentIndex() #save_size = Util.AGB_Header_Save_Sizes[save_type] else: return @@ -1515,7 +1515,7 @@ def BackupRAM(self): args.update(bl_args) self.CONN.BackupROM(fncSetProgress=self.PROGRESS.SetProgress, args=args) else: - args = { "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "verify_read":verify_read } + args = { "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "verify_read":verify_read, "cart_type":cart_type } self.CONN.BackupRAM(fncSetProgress=self.PROGRESS.SetProgress, args=args) self.grpStatus.setTitle("Transfer Status") diff --git a/FlashGBX/RomFileAGB.py b/FlashGBX/RomFileAGB.py index 4d7dbdc..80ee9fc 100644 --- a/FlashGBX/RomFileAGB.py +++ b/FlashGBX/RomFileAGB.py @@ -85,18 +85,6 @@ def GetHeader(self, unchanged=False): data["save_type"] = None data["save_size"] = 0 - # 3D Memory (GBA Video 64 MB) - data["3d_memory"] = False - # if (data["game_title"] == "DISNEYVOL002" and data["game_code"] == "MDSE" and data["header_checksum"] == 0x58) or \ - # (data["game_title"] == "SHARKS TALE" and data["game_code"] == "MSAE" and data["header_checksum"] == 0x98) or \ - # (data["game_title"] == "SHARKS TALE" and data["game_code"] == "MSAE" and data["header_checksum"] == 0x97) or \ - # (data["game_title"] == "SHREK MOVIE" and data["game_code"] == "MSKE" and data["header_checksum"] == 0x83) or \ - # (data["game_title"] == "SHREK MOVIE" and data["game_code"] == "MSKE" and data["header_checksum"] == 0x82) or \ - # (data["game_title"] == "SHREKSHARK21" and data["game_code"] == "MSTE" and data["header_checksum"] == 0x3E) or \ - # (data["game_title"] == "SHREK2MOVIE" and data["game_code"] == "M2SE" and data["header_checksum"] == 0x8A) or \ - # (data["game_title"] == "SHREK2MOVIE" and data["game_code"] == "M2SE" and data["header_checksum"] == 0x89): - # data["3d_memory"] = True - # 8M FLASH DACS data["dacs_8m"] = False if (data["game_title"] == "NGC-HIKARU3" and data["game_code"] == "GHTJ" and data["header_checksum"] == 0xB3): @@ -112,6 +100,9 @@ def GetHeader(self, unchanged=False): data["unchanged"] = copy.copy(data) self.DATA = data data["db"] = self.GetDatabaseEntry() + + # 3D Memory (GBA Video 64 MB) + data["3d_memory"] = False if data["db"] is not None and "3d" in data["db"]: data["3d_memory"] = data["db"]["3d"] diff --git a/FlashGBX/Util.py b/FlashGBX/Util.py index 975682d..1f1bf42 100644 --- a/FlashGBX/Util.py +++ b/FlashGBX/Util.py @@ -7,9 +7,9 @@ # Common constants APPNAME = "FlashGBX" -VERSION_PEP440 = "3.34" +VERSION_PEP440 = "3.35" VERSION = "v{:s}".format(VERSION_PEP440) -VERSION_TIMESTAMP = 1695720959 +VERSION_TIMESTAMP = 1700872410 DEBUG = False DEBUG_LOG = [] APP_PATH = "" diff --git a/FlashGBX/config/db_AGB.json b/FlashGBX/config/db_AGB.json index 029aaea..e042ec7 100644 --- a/FlashGBX/config/db_AGB.json +++ b/FlashGBX/config/db_AGB.json @@ -17008,6 +17008,18 @@ "lg": "En", "rg": "USA" }, + "82ef3fc0ac580cc4773ed2cfd76d6f6b21edfe1e": { + "gn": "Rockman EXE 4 - Tournament Blue Moon", + "ne": "(Japan) (Rev 1)", + "gc": "B4BJ", + "rc": 1889255175, + "rs": 8388608, + "st": 3, + "ss": 32768, + "rv": "Rev 1", + "lg": "Ja", + "rg": "Japan" + }, "d77b0c9c0c0f57d689805d2830d4638fee84d056": { "gn": "Rockman EXE 4 - Tournament Blue Moon", "ne": "(Japan)", @@ -32052,8 +32064,8 @@ "gn": "AGB Aging Cartridge", "ne": "(World) (v1.0) (Test Program)", "gc": "TCHK", - "rc": 3210032432, - "rs": 2097152, + "rc": 3538423347, + "rs": 4194304, "st": 1, "ss": 512, "rv": "v1.0", @@ -32076,8 +32088,8 @@ "gn": "AGS Aging Cartridge", "ne": "(World) (Rev 3) (v9.0) (Test Program)", "gc": "TCHK", - "rc": 778659949, - "rs": 2097152, + "rc": 1481358791, + "rs": 4194304, "st": 1, "ss": 512, "rv": "Rev 3", @@ -32088,8 +32100,8 @@ "gn": "AGS Aging Cartridge", "ne": "(World) (Rev 1) (v7.0) (Test Program)", "gc": "TCHK", - "rc": 3149310304, - "rs": 2097152, + "rc": 3782087520, + "rs": 4194304, "st": 1, "ss": 512, "rv": "Rev 1", diff --git a/FlashGBX/config/db_DMG.json b/FlashGBX/config/db_DMG.json index 5dee9e1..434f829 100644 --- a/FlashGBX/config/db_DMG.json +++ b/FlashGBX/config/db_DMG.json @@ -63,6 +63,15 @@ "lg": "En", "rg": "USA" }, + "dc52b66850f63b354f5aaec225902be58923f89a": { + "gn": "Humans, The", + "ne": "(USA) (QUByte Classics)", + "gc": "", + "rc": 1166077064, + "rs": 262144, + "lg": "En", + "rg": "USA" + }, "a8e6ef37e84a9190a7c23244f98379fd2488db5a": { "gn": "M&M's Minis Madness", "ne": "(USA) (Sample)", @@ -152,6 +161,16 @@ "lg": "En", "rg": "USA" }, + "32ccbcbd4edd7cd5a391f0b85b9dde79b427cff7": { + "gn": "SGB Test Program", + "ne": "(World) (1994.4) (Demo) (SGB Enhanced)", + "gc": "", + "rc": 2304814109, + "rs": 49152, + "rv": "1994.4", + "lg": "En", + "rg": "World" + }, "81fc6bc03e7e094ba544d8d9c1f684271939865b": { "gn": "Shantae", "ne": "(World) (GBA Enhanced) (Switch)", @@ -206,6 +225,26 @@ "lg": "En", "rg": "USA" }, + "affa8b29cfe1addebb6b436566dc5eeb4fce04e3": { + "gn": "Zelda no Densetsu - Yume o Miru Shima", + "ne": "(Japan) (Rev 1) (Demo) (Special Version)", + "gc": "", + "rc": 3166706074, + "rs": 524288, + "rv": "Rev 1", + "lg": "Ja", + "rg": "Japan" + }, + "4c6867774bf7936969ffcae71511a1546df15490": { + "gn": "Zelda no Densetsu - Yume o Miru Shima DX", + "ne": "(Japan) (Rev 1) (Demo) (Special Version 2) (GB Compatible)", + "gc": "", + "rc": 3859812658, + "rs": 1048576, + "rv": "Rev 1", + "lg": "Ja", + "rg": "Japan" + }, "49127b4b99e43568ae9d83089c57fc81068f9654": { "gn": "Mani 4 in 1 - Tetris + Alleyway + Yakuman + Tennis", "ne": "(China) (Ja)", @@ -20929,6 +20968,16 @@ "lg": "Ja", "rg": "Japan" }, + "2861fc1debb7191f1f966597d3f823953e37795b": { + "gn": "Jetsons, The - Robot Panic", + "ne": "(USA, Europe) (Rev 1)", + "gc": "DMG-JSE", + "rc": 3426275085, + "rs": 131072, + "rv": "Rev 1", + "lg": "En", + "rg": "USA, Europe" + }, "e20196d19e705dadef444ba7a04b14a1d4df7070": { "gn": "Jetsons, The - Robot Panic", "ne": "(USA, Europe)", diff --git a/FlashGBX/config/fc_DMG_29LV320DB.txt b/FlashGBX/config/fc_DMG_29LV320DB.txt new file mode 100644 index 0000000..74a4f93 --- /dev/null +++ b/FlashGBX/config/fc_DMG_29LV320DB.txt @@ -0,0 +1,91 @@ +{ + "type":"DMG", + "names":[ + "DRV with 29LV320DB and ALTERA CPLD" + ], + "flash_ids":[ + [ 0xC1, 0xC1, 0xA8, 0xA8 ] + ], + "voltage":3.3, + "flash_size":0x400000, + "start_addr":0x4000, + "first_bank":0, + "write_pin":"WR", + "sector_size_from_cfi":true, + "chip_erase_timeout":120, + "mbc":"manual", + "command_set":"AMD", + "commands":{ + "reset":[ + [ 0x4000, 0xF0 ] + ], + "read_identifier":[ + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ 0x4AAA, 0x90 ] + ], + "read_cfi":[ + [ 0x4AAA, 0x98 ] + ], + "chip_erase":[ + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ 0x4AAA, 0x80 ], + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ 0x4AAA, 0x10 ] + ], + "chip_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ 0, 0xFF, 0xFF ] + ], + "sector_erase":[ + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ 0x4AAA, 0x80 ], + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ "SA", 0x30 ] + ], + "sector_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", 0xFF, 0xFF ] + ], + "buffer_write":[ + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ "SA", 0x26 ], + [ "SA", "BS" ], + [ "PA", "PD" ], + [ "SA", 0x2A ] + ], + "buffer_write_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", "PD", 0xFFFF ] + ], + "single_write":[ + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ 0x4AAA, 0xA0 ], + [ "PA", "PD" ] + ], + "single_write_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ] + ] + } +} diff --git a/FlashGBX/config/fc_DMG_M29W640_2.txt b/FlashGBX/config/fc_DMG_M29W640_2.txt new file mode 100644 index 0000000..5815494 --- /dev/null +++ b/FlashGBX/config/fc_DMG_M29W640_2.txt @@ -0,0 +1,90 @@ +{ + "type":"DMG", + "names":[ + "HC007-BGA-V2 with M29W640" + ], + "flash_ids":[ + [ 0x20, 0x00, 0x7D, 0x00 ] + ], + "voltage":3.3, + "flash_size":0x800000, + "start_addr":0, + "first_bank":1, + "write_pin":"WR", + "sector_size_from_cfi":true, + "_chip_erase_timeout":70, + "command_set":"AMD", + "commands":{ + "reset":[ + [ 0, 0xF0 ] + ], + "read_identifier":[ + [ 0xAAA, 0xA9 ], + [ 0x555, 0x56 ], + [ 0xAAA, 0x90 ] + ], + "read_cfi":[ + [ 0xAA, 0x98 ] + ], + "_chip_erase":[ + [ 0xAAA, 0xA9 ], + [ 0x555, 0x56 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xA9 ], + [ 0x555, 0x56 ], + [ 0xAAA, 0x10 ] + ], + "_chip_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ 0, 0xFF, 0xFF ] + ], + "sector_erase":[ + [ 0xAAA, 0xA9 ], + [ 0x555, 0x56 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xA9 ], + [ 0x555, 0x56 ], + [ "SA", 0x30 ] + ], + "sector_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", 0xFF, 0xFF ] + ], + "buffer_write":[ + [ 0x4AAA, 0xA9 ], + [ 0x4555, 0x56 ], + [ "SA", 0x26 ], + [ "SA", "BS" ], + [ "PA", "PD" ], + [ "SA", 0x2A ] + ], + "buffer_write_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", "PD", 0xFFFF ] + ], + "single_write":[ + [ 0xAAA, 0xA9 ], + [ 0x555, 0x56 ], + [ 0xAAA, 0xA0 ], + [ "PA", "PD" ] + ], + "single_write_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ] + ] + } +} diff --git a/FlashGBX/hw_GBxCartRW.py b/FlashGBX/hw_GBxCartRW.py index 74e509c..942d9c3 100644 --- a/FlashGBX/hw_GBxCartRW.py +++ b/FlashGBX/hw_GBxCartRW.py @@ -843,7 +843,11 @@ def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True): cart_type = supported_carts[cart_type_id] if self.MODE == "DMG" and "command_set" in cart_type and cart_type["command_set"] == "DMG-MBC5-32M-FLASH": checkSaveType = False - elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0: + elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1: + save_size == 65536 + save_type = 7 + checkSaveType = False + elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 1: checkSaveType = False elif self.MODE == "DMG" and "mbc" in cart_type and cart_type["mbc"] == 0x105: # G-MMC1 header = self.ReadROM(0, 0x180) @@ -1757,6 +1761,7 @@ def AutoDetectFlash(self, limitVoltage=False): if self.MODE == "DMG" and not flash_id_found: self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"]) time.sleep(0.1) + return (flash_types, flash_type_id, flash_id, cfi_s, cfi) def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most horribly written function @@ -2461,6 +2466,12 @@ def _BackupRestoreRAM(self, args): empty_data_byte = 0x00 extra_size = 0 audio_low = False + + cart_type = None + if "cart_type" in args: + supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values()) + cart_type = copy.deepcopy(supported_carts[args["cart_type"]]) + if self.MODE == "DMG": _mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle) if not self.IsSupportedMbc(args["mbc"]): @@ -2604,6 +2615,11 @@ def _BackupRestoreRAM(self, args): self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value))) # ↑↑↑ Load commands into firmware + # Bootleg mapper + if cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1: + sram_5 = struct.unpack("B", bytes(self._cart_read(address=5, length=1, agb_save_flash=True)))[0] + self._cart_write(address=5, value=1, sram=True) + commands = [ # save type commands [ [None], [None] ], # No save [ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 1]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 1]) ], # 4K EEPROM @@ -2889,6 +2905,10 @@ def _BackupRestoreRAM(self, args): self.NO_PROG_UPDATE = False self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)+len(rtc_buffer)}) + # Bootleg mapper + if self.MODE == "AGB" and cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1: + buffer[5] = sram_5 + if args["path"] is not None: if self.MODE == "DMG" and _mbc.GetName() == "MBC2": for i in range(0, len(buffer)): @@ -2928,8 +2948,8 @@ def _BackupRestoreRAM(self, args): start_address = 0 end_address = buffer_offset if self.MODE == "AGB" and args["save_type"] == 6: # DACS - end_address -= 0x2000 - + end_address = min(len(buffer), 0xFE000) + path = args["path"] # backup path verify_args.update({"mode":2, "verify_write":buffer, "path":None}) self.ReadROM(0, 4) # dummy read @@ -2971,6 +2991,11 @@ def _BackupRestoreRAM(self, args): self._set_fw_variable("DMG_READ_CS_PULSE", 0) if audio_low: self._set_fw_variable("FLASH_WE_PIN", 0x02) self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"]) # Prevent hotplugging corruptions on rare occasions + + # Bootleg mapper + elif self.MODE == "AGB" and cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1: + self._cart_write(address=5, value=0, sram=True) + self._cart_write(address=5, value=sram_5, sram=True) # Clean up self.INFO["last_action"] = self.INFO["action"] diff --git a/FlashGBX/pyside.py b/FlashGBX/pyside.py index 04118aa..cf2b722 100644 --- a/FlashGBX/pyside.py +++ b/FlashGBX/pyside.py @@ -8,17 +8,19 @@ from .Util import dprint try: - import PySide2 + import PySide2, PIL # PySide2>=5.14 is required major, minor, *_ = PySide2.__version_info__ if (major, minor) < (5, 14): raise ImportError('Requires PySide2>=5.14', name=PySide2.__package__, path=PySide2.__path__) psversion = 2 + except ImportError as err: try: import PySide6 except ImportError: raise err + dprint('Using PySide6 code path.') psversion = 6 from PySide6 import QtCore @@ -32,6 +34,15 @@ from PySide2 import QtGui from PySide2.QtWidgets import QApplication + try: + # Pillow<10.0.0 is required + major, minor = map(int, PIL.__version__.split('.')[:2]) + if (major, minor) >= (10, 0): + raise ImportError('Requires Pillow<10.0.0', name=PIL.__package__, path=PIL.__path__) + except ImportError as err2: + raise err2 + + __all__ = ['QtCore', 'QtWidgets', 'QtGui', 'QApplication', 'QDesktopWidget'] diff --git a/FlashGBX/res/config.zip b/FlashGBX/res/config.zip index 51eebb3..15c7402 100644 Binary files a/FlashGBX/res/config.zip and b/FlashGBX/res/config.zip differ diff --git a/README.md b/README.md index 7553050..0d739fc 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed - Game Boy - 29LV Series Flash BOY with 29LV160DB + - Action Replay - BennVenn MBC3000 RTC cart - BLAZE Xploder GB - BUNG Doctor GB Card 64M @@ -116,6 +117,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed - Ferrante Crafts cart 512 KB - FunnyPlaying MidnightTrace 4 MiB Flash Cart - Gamebank-web DMG-29W-04 with M29W320ET + - GameShark Pro - GB-CART32K-A with SST39SF020A - GB Smart 32M - HDR Game Boy Camera Flashcart @@ -164,12 +166,14 @@ Use this command in a Terminal or Command Prompt window to launch the installed - 36VF3204 and ALTERA CPLD (no PCB text) - DMG-DHCN-20 with MX29LV320ET - DMG-GBRW-20 with 29LV320ETMI-70G + - DRV with 29LV320DB and ALTERA CPLD - DRV with AM29LV160DB and ALTERA CPLD - DRV with AM29LV160DT and ALTERA CPLD - ES29LV160_DRV with 29DL32TF-70 - GB-M968 with 29LV160DB - GB-M968 with M29W160EB - GB-M968 with MX29LV320ABTC + - HC007-BGA-V2 with M29W640 - S29GL032N90T and ALTERA CPLD configured for MBC1 or MBC5 - SD007_48BALL_64M with GL032M11BAIR4 - SD007_48BALL_64M with M29W640 @@ -318,7 +322,7 @@ Many different reproduction cartridges share their flash chip command set, so ev The author would like to thank the following very kind people for their help, contributions or documentation (in alphabetical order): -2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr +2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr ## DISCLAIMER diff --git a/setup.py b/setup.py index e588d00..04cd279 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name="FlashGBX", - version="3.34", + version="3.35", author="Lesserkuma", description="Reads and writes Game Boy and Game Boy Advance cartridge data using the GBxCart RW by insideGadgets", url="https://github.com/lesserkuma/FlashGBX",