diff --git a/.github/01.png b/.github/01.png index 667bd15..6797a5f 100644 Binary files a/.github/01.png and b/.github/01.png differ diff --git a/.github/02.png b/.github/02.png index 79ab7ce..3cc1799 100644 Binary files a/.github/02.png and b/.github/02.png differ diff --git a/CHANGES.md b/CHANGES.md index fd3d97c..57f036a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,19 @@ # Release notes +### v3.21 (released 2023-01-16) +- Bundles GBxCart RW v1.4/v1.4a firmware version R39+L8 (adds support for insideGadgets WonderSwan and Game Gear flash carts) +- Added support for SD007_48BALL_SOP28 with M29W320ET *(thanks DevDavisNunez)* +- Added support for the BennVenn MBC3000 RTC cart *(thanks LucentW)* +- Added support for Flash2Advance Ultra 256M with 8× 3204C3B100 *(thanks djeddit)* +- Added support for SD007_T40_64BALL_SOJ28 with 29LV016T *(thanks Stitch)* +- Confirmed support for SD007_T40_64BALL_S71_TV_TS28 with TC58FVB016FT-85 *(thanks edo999)* +- Added support for F864-3 with M36L0R7050B *(thanks s1cp)* +- Allowed for switching between different Write Enable pins during chip erase and sector erase command sequences via a third parameter (“WR” or “AUDIO”) *(thanks ALXCO-Hardware for the suggestion)* +- Added support for the Squareboi 4 MB (2× 2 MB) cart *(thanks ALXCO-Hardware)* +- Added an option to limit the baud rate for GBxCart RW v1.4/v1.4a +- Minor bug fixes and improvements *(thanks gboh, Grender and orangeglo)* + ### v3.20 (released 2022-11-30) -- Bundles GBxCart RW v1.4/v1.4a firmware version R38+L8 +- Bundles GBxCart RW v1.4/v1.4a firmware version R38+L8 (minor improvements) - Will now retry failed flash sector writes a few times before stopping the process (requires firmware version L1+) - Added delta ROM writing (only write the difference between two ROMs); requires both the original <name>.<ext> ROM file and the changed <name>.delta.<ext> ROM file in the same directory (requires firmware version L1+) *(thanks djeddit for the suggestion)* - Fixed support for Flash2Advance Ultra 64M with 2× 28F320C3B @@ -234,7 +247,7 @@ - Added support for SD007_TSOP_48BALL_V10 with M29W320DT *(thanks Jayro)* - Fixed a problem of reading from a certain type of cartridge that uses the GL256S flash chip *(thanks marv17)* - Added support for B11 with 26L6420MC-90 *(thanks dyf2007)* -- Added support for DIY carts with MBC3 and MX29LV640 *(thanks eveningmoose)* +- Added support for DIY carts with MX29LV640 *(thanks eveningmoose)* ### v2.1 (released 2021-05-05) - Fixed support for SD007_TSOP_29LV017D with L017D70VC *(thanks marv17 and 90sFlav)* diff --git a/FlashGBX/DataTransfer.py b/FlashGBX/DataTransfer.py index aaa1677..317218c 100644 --- a/FlashGBX/DataTransfer.py +++ b/FlashGBX/DataTransfer.py @@ -3,6 +3,7 @@ # Author: Lesserkuma (github.com/lesserkuma) import traceback +from serial import SerialException from . import pyside as PySide2 class DataTransfer(PySide2.QtCore.QThread): @@ -25,6 +26,8 @@ def isRunning(self): return not self.FINISHED def run(self): + tb = "" + error = None try: if self.CONFIG == None: pass @@ -34,7 +37,19 @@ def run(self): self.CONFIG['port'].TransferData(self.CONFIG, self.updateProgress) self.FINISHED = True + except SerialException as e: + if "GetOverlappedResult failed" in e.args[0]: + self.updateProgress.emit({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The USB connection was lost during a transfer. Try different USB cables, reconnect the device, restart the software and try again.", "abortable":False}) + self.FINISHED = True + return + tb = traceback.format_exc() + error = e + except Exception as e: - traceback.print_exc() - self.updateProgress.emit({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An unresolvable error has occured. See console output for more information. Reconnect the device, restart the software and try again.\n\n{:s}: {:s}".format(type(e).__name__, str(e)), "abortable":False}) + tb = traceback.format_exc() + error = e + + if error is not None: + print(tb) + self.updateProgress.emit({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An unresolvable error has occured. See console output for more information. Reconnect the device, restart the software and try again.\n\n{:s}: {:s}".format(type(error).__name__, str(error)), "abortable":False}) self.FINISHED = True diff --git a/FlashGBX/FlashGBX_CLI.py b/FlashGBX/FlashGBX_CLI.py index 701cf61..c56d56a 100644 --- a/FlashGBX/FlashGBX_CLI.py +++ b/FlashGBX/FlashGBX_CLI.py @@ -372,6 +372,7 @@ def FinishOperation(self): self.CONN.INFO["last_action"] = 0 def FindDevices(self, port=None): + # pylint: disable=global-variable-not-assigned global hw_devices for hw_device in hw_devices: dev = hw_device.GbxDevice() @@ -607,7 +608,7 @@ def ReadCartridge(self, data): Util.AGB_Global_CRC32 = 0 db_agb_entry = None if os.path.exists("{0:s}/db_AGB.json".format(self.CONFIG_PATH)): - with open("{0:s}/db_AGB.json".format(self.CONFIG_PATH)) as f: + with open("{0:s}/db_AGB.json".format(self.CONFIG_PATH), encoding="UTF-8") as f: db_agb = f.read() db_agb = json.loads(db_agb) if data["header_sha1"] in db_agb.keys(): diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py index 452a961..4fd98bf 100644 --- a/FlashGBX/FlashGBX_GUI.py +++ b/FlashGBX/FlashGBX_GUI.py @@ -200,7 +200,8 @@ def __init__(self, args): self.mnuConfig.addAction("Prefer full &chip erase over sector erase when both available", lambda: self.SETTINGS.setValue("PreferChipErase", str(self.mnuConfig.actions()[2].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) self.mnuConfig.addAction("&Verify data after writing", lambda: self.SETTINGS.setValue("VerifyWrittenData", str(self.mnuConfig.actions()[3].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) self.mnuConfig.addAction("&Limit voltage to 3.3V when detecting Game Boy flash cartridges", lambda: self.SETTINGS.setValue("AutoDetectLimitVoltage", str(self.mnuConfig.actions()[4].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) - self.mnuConfig.addAction("Always &generate ROM dump reports", lambda: self.SETTINGS.setValue("GenerateDumpReports", str(self.mnuConfig.actions()[5].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) + self.mnuConfig.addAction("Limit &baud rate to 1Mbps for GBxCart RW v1.4 devices", lambda: [ self.SETTINGS.setValue("LimitBaudRate", str(self.mnuConfig.actions()[5].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")), self.SetLimitBaudRate() ]) + self.mnuConfig.addAction("Always &generate ROM dump reports", lambda: self.SETTINGS.setValue("GenerateDumpReports", str(self.mnuConfig.actions()[6].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) self.mnuConfig.addSeparator() self.mnuConfig.addAction("Re-&enable suppressed messages", self.ReEnableMessages) self.mnuConfig.addSeparator() @@ -211,12 +212,14 @@ def __init__(self, args): self.mnuConfig.actions()[3].setCheckable(True) self.mnuConfig.actions()[4].setCheckable(True) self.mnuConfig.actions()[5].setCheckable(True) + self.mnuConfig.actions()[6].setCheckable(True) self.mnuConfig.actions()[0].setChecked(self.SETTINGS.value("UpdateCheck") == "enabled") self.mnuConfig.actions()[1].setChecked(self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled") == "enabled") self.mnuConfig.actions()[2].setChecked(self.SETTINGS.value("PreferChipErase", default="disabled") == "enabled") self.mnuConfig.actions()[3].setChecked(self.SETTINGS.value("VerifyWrittenData", default="enabled") == "enabled") self.mnuConfig.actions()[4].setChecked(self.SETTINGS.value("AutoDetectLimitVoltage", default="disabled") == "enabled") - self.mnuConfig.actions()[5].setChecked(self.SETTINGS.value("GenerateDumpReports", default="disabled") == "enabled") + self.mnuConfig.actions()[5].setChecked(self.SETTINGS.value("LimitBaudRate", default="disabled") == "enabled") + self.mnuConfig.actions()[6].setChecked(self.SETTINGS.value("GenerateDumpReports", default="disabled") == "enabled") self.btnConfig.setMenu(self.mnuConfig) @@ -264,7 +267,7 @@ def __init__(self, args): def GuiCreateGroupBoxDMGCartInfo(self): self.grpDMGCartridgeInfo = QtWidgets.QGroupBox("Game Boy Cartridge Information") - self.grpDMGCartridgeInfo.setMinimumWidth(352) + self.grpDMGCartridgeInfo.setMinimumWidth(364) group_layout = QtWidgets.QVBoxLayout() group_layout.setContentsMargins(-1, 5, -1, -1) @@ -361,7 +364,7 @@ def GuiCreateGroupBoxDMGCartInfo(self): def GuiCreateGroupBoxAGBCartInfo(self): self.grpAGBCartridgeInfo = QtWidgets.QGroupBox("Game Boy Advance Cartridge Information") - self.grpAGBCartridgeInfo.setMinimumWidth(352) + self.grpAGBCartridgeInfo.setMinimumWidth(364) group_layout = QtWidgets.QVBoxLayout() group_layout.setContentsMargins(-1, 5, -1, -1) @@ -459,6 +462,17 @@ def GuiCreateGroupBoxAGBCartInfo(self): self.grpAGBCartridgeInfo.setLayout(group_layout) return self.grpAGBCartridgeInfo + def SetLimitBaudRate(self): + if not self.CheckDeviceAlive(): return + mode = self.CONN.GetMode() + limit_baudrate = self.SETTINGS.value("LimitBaudRate") + if limit_baudrate == "enabled": + self.CONN.ChangeBaudRate(baudrate=1000000) + else: + self.CONN.ChangeBaudRate(baudrate=1700000) + self.DisconnectDevice() + self.FindDevices(connectToFirst=True, mode=mode) + def UpdateCheck(self): update_check = self.SETTINGS.value("UpdateCheck") if update_check is None: @@ -614,7 +628,6 @@ def ConnectDevice(self): self.CONN = None if self.cmbDevice.count() == 0: self.lblDevice.setText("No connection.") return False - elif isinstance(ret, list): for i in range(0, len(ret)): status = ret[i][0] @@ -637,6 +650,7 @@ def ConnectDevice(self): return False if dev.IsConnected(): + dev.SetWriteDelay(enable=str(self.SETTINGS.value("WriteDelay", default="disabled")).lower() == "enabled") qt_app.processEvents() self.CONN = dev self.optDMG.setAutoExclusive(False) @@ -722,6 +736,8 @@ def FindDevices(self, connectToFirst=False, port=None, mode=None): messages = [] last_msg = "" + + # pylint: disable=global-variable-not-assigned global hw_devices for hw_device in hw_devices: dev = hw_device.GbxDevice() @@ -1178,6 +1194,9 @@ def FlashROM(self, dpath=""): if not just_erase: self.SETTINGS.setValue(setting_name, os.path.dirname(path)) + if os.path.getsize(path) == 0: + QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The selected ROM file is empty.", QtWidgets.QMessageBox.Ok) + return if os.path.getsize(path) > 0x10000000: # reject too large files to avoid exploding RAM QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "ROM files bigger than 256 MB are not supported.", QtWidgets.QMessageBox.Ok) return @@ -1234,7 +1253,7 @@ def FlashROM(self, dpath=""): mbc2 = Util.get_mbc_name(hdr["mapper_raw"]) compatible_mbc = [ "None", "MBC2", "MBC3", "MBC5", "MBC7", "GBD", "G-MMC1", "HuC-1", "HuC-3" ] if mbc2 == "None": - mbc = 0x19 # MBC5 + pass elif mbc2 != "None" and not (mbc1 in compatible_mbc and mbc2 in compatible_mbc): if "mbc" in carts[cart_type] and carts[cart_type]["mbc"] == "manual": msg_text = "The ROM file you selected uses a different mapper type than your current selection. What mapper should be used when writing the ROM?\n\nSelected mapper type: {:s}\nROM mapper type: {:s}".format(mbc1, mbc2) @@ -1251,6 +1270,7 @@ def FlashROM(self, dpath=""): elif msgbox.clickedButton() == button_2: mbc = hdr["mapper_raw"] else: + if mbc1 == "None": mbc1 = "None/Unknown" msg_text = "Warning: The ROM file you selected uses a different mapper type than your cartridge type. The ROM file may be incompatible with your cartridge.\n\nCartridge mapper type: {:s}\nROM mapper type: {:s}".format(mbc1, mbc2) answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg_text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) if answer == QtWidgets.QMessageBox.Cancel: return @@ -1416,7 +1436,7 @@ def WriteRAM(self, dpath="", erase=False): if not erase: filesize = os.path.getsize(path) - if filesize > 0x200000: # reject too large files to avoid exploding RAM + if filesize == 0 or filesize > 0x200000: # reject too large files to avoid exploding RAM QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The size of this file is not supported.", QtWidgets.QMessageBox.Ok) return @@ -1795,7 +1815,7 @@ def ReadCartridge(self, resetStatus=True): db_agb_entry = None if os.path.exists("{0:s}/db_AGB.json".format(self.CONFIG_PATH)): - with open("{0:s}/db_AGB.json".format(self.CONFIG_PATH)) as f: + with open("{0:s}/db_AGB.json".format(self.CONFIG_PATH), encoding="UTF-8") as f: db_agb = f.read() db_agb = json.loads(db_agb) if data["header_sha1"] in db_agb.keys(): @@ -2413,5 +2433,9 @@ def run(self): else: qt_app.exec_() # PySide2 +os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" +os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" +QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) +QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) qt_app = QApplication(sys.argv) qt_app.setApplicationName(APPNAME) diff --git a/FlashGBX/Flashcart.py b/FlashGBX/Flashcart.py index a4dc6d8..ce98a53 100644 --- a/FlashGBX/Flashcart.py +++ b/FlashGBX/Flashcart.py @@ -13,19 +13,24 @@ class Flashcart: CART_READ_FNCPTR = None CART_POWERCYCLE_FNCPTR = None PROGRESS_FNCPTR = None + SET_WE_PIN_WR = None + SET_WE_PIN_AUDIO = None + DEFAULT_WE = None SECTOR_COUNT = 0 SECTOR_POS = 0 SECTOR_MAP = None CFI = None LAST_SR = 0x00 - def __init__(self, config=None, cart_write_fncptr=None, cart_write_fast_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, progress_fncptr=None): + def __init__(self, config=None, cart_write_fncptr=None, cart_write_fast_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, progress_fncptr=None, set_we_pin_wr=None, set_we_pin_audio=None): if config is None: config = {} self.CART_WRITE_FNCPTR = cart_write_fncptr self.CART_WRITE_FAST_FNCPTR = cart_write_fast_fncptr self.CART_READ_FNCPTR = cart_read_fncptr self.CART_POWERCYCLE_FNCPTR = cart_powercycle_fncptr self.PROGRESS_FNCPTR = progress_fncptr + self.SET_WE_PIN_WR = set_we_pin_wr + self.SET_WE_PIN_AUDIO = set_we_pin_audio self.CONFIG = config if "command_set" in config: self.CONFIG["_command_set"] = config["command_set"] @@ -33,6 +38,8 @@ def __init__(self, config=None, cart_write_fncptr=None, cart_write_fast_fncptr=N self.CONFIG["_command_set"] = "INTEL" else: self.CONFIG["_command_set"] = "" + if "write_pin" in config: + self.DEFAULT_WE = config["write_pin"] def CartRead(self, address, length=0): if length == 0: @@ -42,16 +49,16 @@ def CartRead(self, address, length=0): length = 1 return self.CART_READ_FNCPTR(address, length) - def CartWrite(self, commands, flashcart=True, sram=False): - if "command_set" in self.CONFIG and self.CONFIG["command_set"] in ("GBMEMORY", "DMG-MBC5-32M-FLASH"): flashcart = False - dprint(commands, flashcart, sram) - if flashcart and not sram: + def CartWrite(self, commands, fast_write=True, sram=False): + if "command_set" in self.CONFIG and self.CONFIG["command_set"] in ("GBMEMORY", "DMG-MBC5-32M-FLASH"): fast_write = False + dprint(commands, fast_write, sram) + if fast_write and not sram: self.CART_WRITE_FAST_FNCPTR(commands, flashcart=True) else: for command in commands: address = command[0] value = command[1] - self.CART_WRITE_FNCPTR(address, value, flashcart=flashcart, sram=sram) + self.CART_WRITE_FNCPTR(address, value, flashcart=fast_write, sram=sram) def GetCommandSetType(self): return self.CONFIG["_command_set"].upper() @@ -158,7 +165,7 @@ def Unlock(self): dprint("Reading 0x{:X} bytes from cartridge at 0x{:X} = {:s}".format(command[1], command[0], str(temp))) time.sleep(0.001) if "unlock" in self.CONFIG["commands"]: - self.CartWrite(self.CONFIG["commands"]["unlock"]) + self.CartWrite(self.CONFIG["commands"]["unlock"], fast_write=False) time.sleep(0.001) def Reset(self, full_reset=False, max_address=0x2000000): @@ -172,7 +179,7 @@ def Reset(self, full_reset=False, max_address=0x2000000): if j >= max_address: break dprint("reset_every @ 0x{:X}".format(j)) for command in self.CONFIG["commands"]["reset"]: - self.CartWrite([[j, command[1]]]) + self.CartWrite([[j + command[0], command[1]]]) time.sleep(0.01) elif "reset" in self.CONFIG["commands"]: self.CartWrite(self.CONFIG["commands"]["reset"]) @@ -285,8 +292,23 @@ def ChipErase(self): for i in range(0, len(self.CONFIG["commands"]["chip_erase"])): addr = self.CONFIG["commands"]["chip_erase"][i][0] data = self.CONFIG["commands"]["chip_erase"][i][1] + if len(self.CONFIG["commands"]["chip_erase"][i]) > 2: + we = self.CONFIG["commands"]["chip_erase"][i][2] + else: + we = None + if not addr == None: + if we == "WR": + self.SET_WE_PIN_WR() + elif we == "AUDIO": + self.SET_WE_PIN_AUDIO() self.CartWrite([[addr, data]]) + if we is not None: + if self.DEFAULT_WE == "WR": + self.SET_WE_PIN_WR() + elif self.DEFAULT_WE == "AUDIO": + self.SET_WE_PIN_AUDIO() + time.sleep(0.1) if self.CONFIG["commands"]["chip_erase_wait_for"][i][0] != None: addr = self.CONFIG["commands"]["chip_erase_wait_for"][i][0] @@ -298,7 +320,18 @@ def ChipErase(self): for j in range(0, len(self.CONFIG["commands"]["read_status_register"])): #sr_addr = self.CONFIG["commands"]["read_status_register"][j][0] sr_data = self.CONFIG["commands"]["read_status_register"][j][1] + + if we == "WR": + self.SET_WE_PIN_WR() + elif we == "AUDIO": + self.SET_WE_PIN_AUDIO() self.CartWrite([[addr, sr_data]]) + if we is not None: + if self.DEFAULT_WE == "WR": + self.SET_WE_PIN_WR() + elif self.DEFAULT_WE == "AUDIO": + self.SET_WE_PIN_AUDIO() + self.CartRead(addr, 2) # dummy read (fixes some bootlegs) wait_for = struct.unpack(" 2: + we = self.CONFIG["commands"]["sector_erase"][i][2] + else: + we = None + if addr == "SA": addr = pos if addr == "SA+1": addr = pos + 1 if addr == "SA+2": addr = pos + 2 if addr == "SA+0x4000": addr = pos + 0x4000 if addr == "SA+0x7000": addr = pos + 0x7000 if not addr == None: + if we == "WR": + self.SET_WE_PIN_WR() + elif we == "AUDIO": + self.SET_WE_PIN_AUDIO() self.CartWrite([[addr, data]]) + if we is not None: + if self.DEFAULT_WE == "WR": + self.SET_WE_PIN_WR() + elif self.DEFAULT_WE == "AUDIO": + self.SET_WE_PIN_AUDIO() + if self.CONFIG["commands"]["sector_erase_wait_for"][i][0] != None: addr = self.CONFIG["commands"]["sector_erase_wait_for"][i][0] data = self.CONFIG["commands"]["sector_erase_wait_for"][i][1] @@ -342,7 +390,18 @@ def SectorErase(self, pos=0, buffer_pos=0): for j in range(0, len(self.CONFIG["commands"]["read_status_register"])): sr_addr = self.CONFIG["commands"]["read_status_register"][j][0] sr_data = self.CONFIG["commands"]["read_status_register"][j][1] + + if we == "WR": + self.SET_WE_PIN_WR() + elif we == "AUDIO": + self.SET_WE_PIN_AUDIO() self.CartWrite([[sr_addr, sr_data]]) + if we is not None: + if self.DEFAULT_WE == "WR": + self.SET_WE_PIN_WR() + elif self.DEFAULT_WE == "AUDIO": + self.SET_WE_PIN_AUDIO() + self.CartRead(addr, 2) # dummy read (fixes some bootlegs) temp = self.CartRead(addr, 2) if len(temp) != 2: diff --git a/FlashGBX/Mapper.py b/FlashGBX/Mapper.py index 4b0f094..0a7acb3 100644 --- a/FlashGBX/Mapper.py +++ b/FlashGBX/Mapper.py @@ -517,7 +517,7 @@ def EraseFlashSector(self): sr = self.CartRead(0x4000) dprint("Status Register Check: 0x{:X} == 0x80? {:s}".format(sr, str(sr == 0x80))) if sr == 0x80: break - time.sleep(0.0001) + time.sleep(0.01) def GetFlashID(self): self.EnableFlash(enable=True) diff --git a/FlashGBX/Util.py b/FlashGBX/Util.py index 2041a15..4de0280 100644 --- a/FlashGBX/Util.py +++ b/FlashGBX/Util.py @@ -7,7 +7,7 @@ # Common constants APPNAME = "FlashGBX" -VERSION_PEP440 = "3.20" +VERSION_PEP440 = "3.21" VERSION = "v{:s}".format(VERSION_PEP440) DEBUG = False DEBUG_LOG = [] @@ -21,7 +21,7 @@ AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000 ] DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper' } -DMG_Mapper_Types = { "None":[ 0x00 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x06 ], "MBC3":[ 0x10, 0x13 ], "MBC5":[ 0x19, 0x1B, 0x1C, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "256M Multi Cart":[ 0x201 ], "Wisdom Tree":[ 0x202 ], "Xploder GB":[ 0x203 ], "Sachen":[ 0x204 ], "Datel Orbit V2":[ 0x205 ] } +DMG_Mapper_Types = { "None":[ 0x00 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x06 ], "MBC3":[ 0x10, 0x13 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "256M Multi Cart":[ 0x201 ], "Wisdom Tree":[ 0x202 ], "Xploder GB":[ 0x203 ], "Sachen":[ 0x204 ], "Datel Orbit V2":[ 0x205 ] } DMG_Header_ROM_Sizes = [ "32 KB", "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", "8 MB", "16 MB", "32 MB" ] DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A ] DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000 ] diff --git a/FlashGBX/config/db_AGB.json b/FlashGBX/config/db_AGB.json index 6dcc218..f256bcf 100644 --- a/FlashGBX/config/db_AGB.json +++ b/FlashGBX/config/db_AGB.json @@ -20222,11 +20222,32 @@ "st": 3, "gc": "RZWJ" }, + "1b58262c2bea83608faeb85db1ef1d9423b09b1b": { + "rc": 3149310304, + "rs": 2097152, + "ss": 512, + "st": 1, + "gc": "TCHK" + }, "569244c74f0f1c6d9bc1efefb6a2a1d0a5bdd77b": { "rc": 3494379487, "rs": 8388608, - "ss": 0, - "st": 0, + "ss": 512, + "st": 1, + "gc": "TCHK" + }, + "5dd63ac864ec954cb3ed58a51b34bec1eaa17cda": { + "rc": 778659949, + "rs": 2097152, + "ss": 512, + "st": 1, + "gc": "TCHK" + }, + "72ba2ff35d246d36482381588b80c8b2cddae76f": { + "rc": 2528463141, + "rs": 1326620, + "ss": 512, + "st": 1, "gc": "TCHK" }, "3fc931b3150d91bca7b4d525fc2894814d51a08c": { diff --git a/FlashGBX/config/fc_AGB_AR_SST39VF800A.txt b/FlashGBX/config/fc_AGB_AR_SST39VF800A.txt new file mode 100644 index 0000000..db41596 --- /dev/null +++ b/FlashGBX/config/fc_AGB_AR_SST39VF800A.txt @@ -0,0 +1,56 @@ +{ + "type":"AGB", + "names":[ + "Action Replay Ultimate Codes (SST39VF800A)" + ], + "flash_ids":[ + [ 0xBF, 0x00, 0x81, 0x27 ] + ], + "voltage":3.3, + "flash_size":0x100000, + "chip_erase_timeout":10, + "command_set":"AMD", + "commands":{ + "reset":[ + [ 0, 0xF0 ] + ], + "read_identifier":[ + [ 0xAAAA, 0xAA ], + [ 0x5555, 0x55 ], + [ 0xAAAA, 0x90 ] + ], + "read_cfi":[ + [ 0xAAAA, 0xAA ], + [ 0x5555, 0x55 ], + [ 0xAAAA, 0x98 ] + ], + "chip_erase":[ + [ 0xAAAA, 0xAA ], + [ 0x5555, 0x55 ], + [ 0xAAAA, 0x80 ], + [ 0xAAAA, 0xAA ], + [ 0x5555, 0x55 ], + [ 0xAAAA, 0x10 ] + ], + "chip_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ 0, 0xFF, 0xFF ] + ], + "single_write":[ + [ 0xAAAA, 0xAA ], + [ 0x5555, 0x55 ], + [ 0xAAAA, 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_AGB_Flash2Advance_128M.txt b/FlashGBX/config/fc_AGB_Flash2Advance_128M.txt index 115f6cb..6a2b5d3 100644 --- a/FlashGBX/config/fc_AGB_Flash2Advance_128M.txt +++ b/FlashGBX/config/fc_AGB_Flash2Advance_128M.txt @@ -14,15 +14,7 @@ "commands":{ "unlock":[ [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x1234, 5 ], - [ 0x000ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x002468A, 0x5678, 5 ], - [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x0ECA800, 0x5678, 1 ], - [ 0x00268A0, 0x1234, 1 ], - [ 0x002468A, 0xABCD, 5 ], + [ 0x1C2468A, 0xA55A, 1 ], [ 0x130ECA8, 0x5354, 1 ], [ 0x1E2468A, 0x9413, 1 ] ], diff --git a/FlashGBX/config/fc_AGB_Flash2Advance_256M.txt b/FlashGBX/config/fc_AGB_Flash2Advance_256M.txt index 325bc99..95cbf9d 100644 --- a/FlashGBX/config/fc_AGB_Flash2Advance_256M.txt +++ b/FlashGBX/config/fc_AGB_Flash2Advance_256M.txt @@ -14,15 +14,7 @@ "commands":{ "unlock":[ [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x1234, 5 ], - [ 0x000ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x002468A, 0x5678, 5 ], - [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x0ECA800, 0x5678, 1 ], - [ 0x00268A0, 0x1234, 1 ], - [ 0x002468A, 0xABCD, 5 ], + [ 0x1C2468A, 0xA55A, 1 ], [ 0x130ECA8, 0x5354, 1 ], [ 0x1E2468A, 0x9413, 1 ] ], diff --git a/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_256M.txt b/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_256M.txt new file mode 100644 index 0000000..f7eea77 --- /dev/null +++ b/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_256M.txt @@ -0,0 +1,73 @@ +{ + "type":"AGB", + "names":[ + "Flash2Advance Ultra 256M with 8× 3204C3B100" + ], + "flash_ids":[ + [ 0x89, 0x00, 0x89, 0x00, 0xC5, 0x88, 0xC5, 0x88 ] + ], + "voltage":3.3, + "flash_size":0x2000000, + "sector_size":[ + [0x04000, 8], + [0x20000, 63], + [0x04000, 8], + [0x20000, 63], + [0x04000, 8], + [0x20000, 63], + [0x04000, 8], + [0x20000, 63] + ], + "reset_every":0x800000, + "command_set":"INTEL", + "commands":{ + "unlock":[ + [ 0x130ECA8, 0x5354, 1 ], + [ 0x1C2468A, 0xA55A, 1 ], + [ 0x130ECA8, 0x5354, 1 ], + [ 0x1E2468A, 0x9413, 1 ] + ], + "reset":[ + [ 0, 0xFF ], + [ 2, 0xFF ] + ], + "read_identifier":[ + [ 0, 0x90 ], + [ 2, 0x90 ] + ], + "sector_erase":[ + [ "SA", 0x60 ], + [ "SA", 0xD0 ], + [ "SA", 0x20 ], + [ "SA", 0xD0 ], + [ "SA+2", 0x60 ], + [ "SA+2", 0xD0 ], + [ "SA+2", 0x20 ], + [ "SA+2", 0xD0 ], + [ null, null ], + [ null, null ] + ], + "sector_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", 0x80, 0xFFFF ], + [ "SA+2", 0x80, 0xFFFF ] + ], + "single_write":[ + [ 0, 0x70 ], + [ 0, 0x10 ], + [ "PA", "PD" ] + ], + "single_write_wait_for":[ + [ 0, 0x80, 0x80 ], + [ null, null, null ], + [ null, null, null ] + ] + } +} diff --git a/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_64M.txt b/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_64M.txt index 6006187..4338471 100644 --- a/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_64M.txt +++ b/FlashGBX/config/fc_AGB_Flash2Advance_Ultra_64M.txt @@ -16,15 +16,7 @@ "commands":{ "unlock":[ [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x1234, 5 ], - [ 0x000ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x002468A, 0x5678, 5 ], - [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x0ECA800, 0x5678, 1 ], - [ 0x00268A0, 0x1234, 1 ], - [ 0x002468A, 0xABCD, 5 ], + [ 0x1C2468A, 0xA55A, 1 ], [ 0x130ECA8, 0x5354, 1 ], [ 0x1E2468A, 0x9413, 1 ] ], diff --git a/FlashGBX/config/fc_AGB_FlashAdvance_64M.txt b/FlashGBX/config/fc_AGB_FlashAdvance_64M.txt index 8cae779..cba0676 100644 --- a/FlashGBX/config/fc_AGB_FlashAdvance_64M.txt +++ b/FlashGBX/config/fc_AGB_FlashAdvance_64M.txt @@ -15,15 +15,7 @@ "commands":{ "unlock":[ [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x1234, 5 ], - [ 0x000ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x002468A, 0x5678, 5 ], - [ 0x130ECA8, 0x5354, 1 ], - [ 0x002468A, 0x5354, 1 ], - [ 0x0ECA800, 0x5678, 1 ], - [ 0x00268A0, 0x1234, 1 ], - [ 0x002468A, 0xABCD, 5 ], + [ 0x1C2468A, 0xA55A, 1 ], [ 0x130ECA8, 0x5354, 1 ], [ 0x1E2468A, 0x9413, 1 ] ], diff --git a/FlashGBX/config/fc_AGB_M36L0R705.txt b/FlashGBX/config/fc_AGB_M36L0R705.txt index d4841a9..3f08d97 100644 --- a/FlashGBX/config/fc_AGB_M36L0R705.txt +++ b/FlashGBX/config/fc_AGB_M36L0R705.txt @@ -4,13 +4,15 @@ "4455_4400_4000_4350_36L0R_V3 with M36L0R7050T", "4050_4400_4000_4350_36L0R_V5 with M36L0R7050T", "4050_4400_4000_4350_36L0R_6108 with M36L0R7050B", - "4000L0ZBQ0 DRV with 3000L0YBQ0" + "4000L0ZBQ0 DRV with 3000L0YBQ0", + "F864-3 with M36L0R7050B" ], "flash_ids":[ [ 0x20, 0x00, 0xC4, 0x88 ], [ 0x20, 0x00, 0xC4, 0x88 ], [ 0x20, 0x00, 0xC6, 0x88 ], - [ 0x8A, 0x00, 0x0F, 0x88 ] + [ 0x8A, 0x00, 0x0F, 0x88 ], + [ 0x20, 0x00, 0xC5, 0x88 ] ], "voltage":3.3, "flash_size":0x1000000, diff --git a/FlashGBX/config/fc_DMG_BV_MBC3000.txt b/FlashGBX/config/fc_DMG_BV_MBC3000.txt new file mode 100644 index 0000000..8a79b99 --- /dev/null +++ b/FlashGBX/config/fc_DMG_BV_MBC3000.txt @@ -0,0 +1,75 @@ +{ + "type":"DMG", + "names":[ + "BennVenn MBC3000 RTC cart" + ], + "flash_ids":[ + [ 0xC2, 0xC2, 0xCB, 0xCB ] + ], + "voltage":5, + "flash_size":0x800000, + "start_addr":0, + "first_bank":1, + "write_pin":"WR", + "sector_size_from_cfi":true, + "chip_erase_timeout":200, + "mbc":"manual", + "command_set":"AMD", + "commands":{ + "reset":[ + [ 0, 0xF0 ] + ], + "read_identifier":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x90 ] + ], + "read_cfi":[ + [ 0xAAA, 0x98 ] + ], + "chip_erase":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 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, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ "SA", 0x30 ] + ], + "sector_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", 0xFF, 0xFF ] + ], + "single_write":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 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_FerranteCrafts_32KB.txt b/FlashGBX/config/fc_DMG_FerranteCrafts_32KB.txt index 7e9aa8d..ee793e5 100644 --- a/FlashGBX/config/fc_DMG_FerranteCrafts_32KB.txt +++ b/FlashGBX/config/fc_DMG_FerranteCrafts_32KB.txt @@ -5,7 +5,7 @@ "Catskull 32k Gameboy Flash Cart" ], "flash_ids":[ - [ 0xBF, 0xB5, 0x01, 0xFF ] + [ 0xBF, 0xB5 ] ], "voltage":5, "flash_size":0x8000, diff --git a/FlashGBX/config/fc_DMG_FerranteCrafts_32KB_2.txt b/FlashGBX/config/fc_DMG_FerranteCrafts_32KB_2.txt index f2ca8a4..7083929 100644 --- a/FlashGBX/config/fc_DMG_FerranteCrafts_32KB_2.txt +++ b/FlashGBX/config/fc_DMG_FerranteCrafts_32KB_2.txt @@ -6,7 +6,7 @@ ], "flash_ids":[ [ 0xBF, 0xFF, 0x25, 0xFF ], - [ 0xBF, 0xB7, 0xFF, 0xFF ] + [ 0xBF, 0xB7 ] ], "voltage":5, "flash_size":0x8000, diff --git a/FlashGBX/config/fc_DMG_FerranteCrafts_64KB.txt b/FlashGBX/config/fc_DMG_FerranteCrafts_64KB.txt index bf2a1db..04efc26 100644 --- a/FlashGBX/config/fc_DMG_FerranteCrafts_64KB.txt +++ b/FlashGBX/config/fc_DMG_FerranteCrafts_64KB.txt @@ -4,7 +4,7 @@ "Ferrante Crafts cart 64 KB" ], "flash_ids":[ - [ 0xBF, 0xB5, 0x01, 0xFF ] + [ 0xBF, 0xB5 ] ], "voltage":5, "flash_size":0x10000, diff --git a/FlashGBX/config/fc_DMG_M29W320DT.txt b/FlashGBX/config/fc_DMG_M29W320DT.txt index ab84c95..b4b0c6e 100644 --- a/FlashGBX/config/fc_DMG_M29W320DT.txt +++ b/FlashGBX/config/fc_DMG_M29W320DT.txt @@ -5,14 +5,16 @@ "SD007_TSOP_48BALL_V10 with M29W320DT", "SD007_TSOP_48BALL_V10 with GL032M10BFIR3", "SD007_TSOP_48BALL_V9 with 29LV320CBTC-70G", - "SD007_TSOP_48BALL_V10 with 29DL32TF-70" + "SD007_TSOP_48BALL_V10 with 29DL32TF-70", + "SD007_48BALL_SOP28 with M29W320ET" ], "flash_ids":[ [ 0x02, 0x02, 0x7D, 0x7D ], [ 0x20, 0x20, 0xC9, 0xC9 ], [ 0x02, 0x02, 0x7D, 0x7D ], [ 0x02, 0x02, 0x7D, 0x7D ], - [ 0x04, 0x04, 0x7D, 0x7D ] + [ 0x04, 0x04, 0x7D, 0x7D ], + [ 0x20, 0x20, 0x55, 0x55 ] ], "voltage":3.3, "voltage_variants":true, diff --git a/FlashGBX/config/fc_DMG_MX29LV640_AUDIO.txt b/FlashGBX/config/fc_DMG_MX29LV640_AUDIO.txt index 286954f..b91c64d 100644 --- a/FlashGBX/config/fc_DMG_MX29LV640_AUDIO.txt +++ b/FlashGBX/config/fc_DMG_MX29LV640_AUDIO.txt @@ -1,18 +1,20 @@ { "type":"DMG", "names":[ - "DIY cart with MBC3 and MX29LV640 @ AUDIO" + "DIY cart with MX29LV640 @ AUDIO" ], "flash_ids":[ [ 0xC2, 0xC2, 0xCB, 0xCB ] ], "voltage":3.3, + "voltage_variants":true, "flash_size":0x800000, "start_addr":0, "first_bank":1, - "mbc":0x13, "write_pin":"AUDIO", + "sector_size_from_cfi":true, "chip_erase_timeout":200, + "mbc":"manual", "command_set":"AMD", "commands":{ "reset":[ @@ -42,6 +44,22 @@ [ null, null, null ], [ 0, 0xFF, 0xFF ] ], + "sector_erase":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ "SA", 0x30 ] + ], + "sector_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", 0xFF, 0xFF ] + ], "single_write":[ [ 0xAAA, 0xAA ], [ 0x555, 0x55 ], diff --git a/FlashGBX/config/fc_DMG_MX29LV640_WR.txt b/FlashGBX/config/fc_DMG_MX29LV640_WR.txt new file mode 100644 index 0000000..54a8651 --- /dev/null +++ b/FlashGBX/config/fc_DMG_MX29LV640_WR.txt @@ -0,0 +1,76 @@ +{ + "type":"DMG", + "names":[ + "DIY cart with MX29LV640 @ WR" + ], + "flash_ids":[ + [ 0xC2, 0xC2, 0xCB, 0xCB ] + ], + "voltage":3.3, + "voltage_variants":true, + "flash_size":0x800000, + "start_addr":0, + "first_bank":1, + "write_pin":"WR", + "sector_size_from_cfi":true, + "chip_erase_timeout":200, + "mbc":"manual", + "command_set":"AMD", + "commands":{ + "reset":[ + [ 0, 0xF0 ] + ], + "read_identifier":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x90 ] + ], + "read_cfi":[ + [ 0xAAA, 0x98 ] + ], + "chip_erase":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 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, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 0x80 ], + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ "SA", 0x30 ] + ], + "sector_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ "SA", 0xFF, 0xFF ] + ], + "single_write":[ + [ 0xAAA, 0xAA ], + [ 0x555, 0x55 ], + [ 0xAAA, 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_SST39SF040_AUDIO.txt b/FlashGBX/config/fc_DMG_SST39SF040_AUDIO.txt index 56b3dba..47748f7 100644 --- a/FlashGBX/config/fc_DMG_SST39SF040_AUDIO.txt +++ b/FlashGBX/config/fc_DMG_SST39SF040_AUDIO.txt @@ -5,7 +5,7 @@ "Ferrante Crafts cart 512 KB" ], "flash_ids":[ - [ 0xBF, 0xB7, 0x01, 0xFF ] + [ 0xBF, 0xB7 ] ], "voltage":5, "flash_size":0x80000, diff --git a/FlashGBX/config/fc_DMG_Squareboi_4MB_2x2.txt b/FlashGBX/config/fc_DMG_Squareboi_4MB_2x2.txt new file mode 100644 index 0000000..94688ca --- /dev/null +++ b/FlashGBX/config/fc_DMG_Squareboi_4MB_2x2.txt @@ -0,0 +1,74 @@ +{ + "type":"DMG", + "names":[ + "Squareboi 4 MB (2× 2 MB)" + ], + "voltage":5, + "flash_size":0x400000, + "start_addr":0x4000, + "first_bank":0, + "write_pin":"AUDIO", + "chip_erase_timeout":120, + "mbc":0x1B, + "command_set":"AMD", + "commands":{ + "reset":[ + [ 0x4000, 0xF0 ] + ], + "read_identifier":[ + [ 0x4AAA, 0xAA ], + [ 0x4555, 0x55 ], + [ 0x4AAA, 0x90 ] + ], + "read_cfi":[ + [ 0x4AAA, 0x98 ] + ], + "chip_erase":[ + [ 0x2100, 0x00, "WR" ], + [ 0x4AAA, 0xAA ], + [ 0x4555, 0x55 ], + [ 0x4AAA, 0x80 ], + [ 0x4AAA, 0xAA ], + [ 0x4555, 0x55 ], + [ 0x4AAA, 0x10 ], + [ 0x2100, 0x80, "WR" ], + [ 0x4AAA, 0xAA ], + [ 0x4555, 0x55 ], + [ 0x4AAA, 0x80 ], + [ 0x4AAA, 0xAA ], + [ 0x4555, 0x55 ], + [ 0x4AAA, 0x10 ], + [ 0x2100, 0x00, "WR" ] + + ], + "chip_erase_wait_for":[ + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ null, null, null ], + [ 0x4000, 0xFF, 0xFF ], + [ 0x4000, 0xFF, 0xFF ] + ], + "single_write":[ + [ 0x4AAA, 0xAA ], + [ 0x4555, 0x55 ], + [ 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_TC58FVB016FT.txt b/FlashGBX/config/fc_DMG_TC58FVB016FT.txt index 42327f0..eb8acbf 100644 --- a/FlashGBX/config/fc_DMG_TC58FVB016FT.txt +++ b/FlashGBX/config/fc_DMG_TC58FVB016FT.txt @@ -1,10 +1,14 @@ { "type":"DMG", "names":[ - "SD007_T40_64BALL_TSOP28 with TC58FVB016FT-85" + "SD007_T40_64BALL_TSOP28 with TC58FVB016FT-85", + "SD007_T40_64BALL_S71_TV_TS28 with TC58FVB016FT-85", + "SD007_T40_64BALL_SOJ28 with 29LV016T" ], "flash_ids":[ - [ 0x98, 0x45, 0x00, 0x2A ] + [ 0x98, 0x45, 0x00, 0x2A ], + [ 0x98, 0x45, 0x00, 0x2A ], + [ 0x04, 0xC7, 0x00, 0x00 ] ], "voltage":5, "flash_size":0x200000, diff --git a/FlashGBX/fw_GBxCartRW_v1_4.py b/FlashGBX/fw_GBxCartRW_v1_4.py index 51814dc..7234125 100644 --- a/FlashGBX/fw_GBxCartRW_v1_4.py +++ b/FlashGBX/fw_GBxCartRW_v1_4.py @@ -309,7 +309,7 @@ def UpdateFirmware(self): self.reject() return True elif ret == 2: - text = "The firmware update is has failed. Please try again." + text = "The firmware update has failed. Please try again." self.btnUpdate.setEnabled(True) self.btnClose.setEnabled(True) self.optDevicePCBVer14.setEnabled(True) diff --git a/FlashGBX/hw_GBxCartRW.py b/FlashGBX/hw_GBxCartRW.py index ce06ceb..7697218 100644 --- a/FlashGBX/hw_GBxCartRW.py +++ b/FlashGBX/hw_GBxCartRW.py @@ -17,7 +17,7 @@ class GbxDevice: DEVICE_NAME = "GBxCart RW" DEVICE_MIN_FW = 1 DEVICE_MAX_FW = 8 - DEVICE_LATEST_FW_TS = { 4:1619427330, 5:1669112674, 6:1669112674 } + DEVICE_LATEST_FW_TS = { 4:1619427330, 5:1673788742, 6:1673788742 } DEVICE_CMD = { "NULL":0x30, @@ -113,7 +113,9 @@ class GbxDevice: FAST_READ = False SKIPPING = False BAUDRATE = 1000000 - MAX_BUFFER_LEN = 512 + MAX_BUFFER_LEN = 0x2000 + DEVICE_TIMEOUT = 1 + WRITE_DELAY = False def __init__(self): pass @@ -140,18 +142,21 @@ def Initialize(self, flashcarts, port=None, max_baud=1700000): if not self.LoadFirmwareVersion() and max_baud >= 1700000: dev.close() self.BAUDRATE = 1700000 + self.MAX_BUFFER_LEN = 0x2000 dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1) self.DEVICE = dev if not self.LoadFirmwareVersion(): dev.close() self.DEVICE = None self.BAUDRATE = 1000000 + self.MAX_BUFFER_LEN = 0x100 continue elif max_baud >= 1700000 and self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE < 1700000: # Switch to higher baud rate - self._write(self.DEVICE_CMD["OFW_USART_1_7M_SPEED"]) - self.BAUDRATE = 1700000 - dev.close() + #self._write(self.DEVICE_CMD["OFW_USART_1_7M_SPEED"]) + #self.BAUDRATE = 1700000 + #dev.close() + self.ChangeBaudRate(baudrate=1700000) dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1) self.DEVICE = dev @@ -186,7 +191,7 @@ def Initialize(self, flashcarts, port=None, max_baud=1700000): conn_msg.append([0, "For help please visit the insideGadgets Discord: https://gbxcart.com/discord"]) self.PORT = ports[i] - self.DEVICE.timeout = 1 + self.DEVICE.timeout = self.DEVICE_TIMEOUT # Load Flash Cartridge Handlers self.UpdateFlashCarts(flashcarts) @@ -242,6 +247,19 @@ def LoadFirmwareVersion(self): def GetBaudRate(self): return self.BAUDRATE + def ChangeBaudRate(self, baudrate): + if not self.IsConnected(): return + if baudrate == 1700000: + self._write(self.DEVICE_CMD["OFW_USART_1_7M_SPEED"]) + self.Close() + self.BAUDRATE = baudrate + self.MAX_BUFFER_LEN = 0x2000 + elif baudrate == 1000000: + self._write(self.DEVICE_CMD["OFW_USART_1_0M_SPEED"]) + self.Close() + self.BAUDRATE = baudrate + self.MAX_BUFFER_LEN = 0x100 + def CanSetVoltageManually(self): return False @@ -381,6 +399,10 @@ def GetPort(self): def GetFWBuildDate(self): return self.FW["fw_dt"] + def SetWriteDelay(self, enable=True): + dprint("Setting Write Delay to", enable) + self.WRITE_DELAY = enable + def wait_for_ack(self, values=None): if values is None: values = [0x01, 0x03] buffer = self._read(1) @@ -396,10 +418,10 @@ def wait_for_ack(self, values=None): self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A timeout error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)}) else: dprint("Communication error ({:s}(), line {:d})".format(stack.name, stack.lineno)) - #print(self.CANCEL_ARGS) self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A communication error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)}) self.ERROR = True self.CANCEL = True + self.SetWriteDelay(enable=True) return False return buffer @@ -417,7 +439,7 @@ def _write(self, data, wait=False): # On MacOS it’s possible not all bytes are transmitted successfully, # even though we’re using flush() which is the tcdrain function. # Still looking for a better solution than delaying here. - if platform.system() == "Darwin": + if platform.system() == "Darwin" or self.WRITE_DELAY is True: time.sleep(0.00125) if wait: return self.wait_for_ack() @@ -541,6 +563,7 @@ def _cart_write_flash(self, commands, flashcart=False): self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A critical communication error occured during a write. Please avoid passive USB hubs, try different USB ports/cables and re-connect the device."}) self.CANCEL = True self.ERROR = True + return False def _clk_toggle(self, num): if self.FW["pcb_ver"] not in (5, 6, 101): return False @@ -549,6 +572,13 @@ def _clk_toggle(self, num): self._write(self.DEVICE_CMD["CLK_LOW"]) return True + def _set_we_pin_wr(self): + if self.MODE == "DMG": + self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR + def _set_we_pin_audio(self): + if self.MODE == "DMG": + self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO + def CartPowerCycle(self, delay=0.1): if self.CanPowerCycleCart(): dprint("Power cycling cartridge with a delay of {:.1f} seconds".format(delay)) @@ -1457,7 +1487,7 @@ def AutoDetectFlash(self, limitVoltage=False): elif we == "WR+RESET": self._set_fw_variable("FLASH_WE_PIN", 0x03) # FLASH_WE_PIN_WR_RESET - flashcart = Flashcart(config=flashcart_meta, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle) + flashcart = Flashcart(config=flashcart_meta, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, set_we_pin_wr=self._set_we_pin_wr, set_we_pin_audio=self._set_we_pin_audio) flashcart.Reset(full_reset=False) if flashcart.Unlock() is False: return False if "flash_ids" in flashcart_meta and len(flashcart_meta["flash_ids"]) > 0: @@ -1501,7 +1531,24 @@ def AutoDetectFlash(self, limitVoltage=False): flash_type_id = flash_types[i] size_undetected = False break - + else: + if self.MODE == "AGB": + # Check where the ROM data repeats (for unlicensed carts) + header = self.ReadROM(0, 0x180) + size_check = header[0xA0:0xA0+16] + currAddr = 0x10000 + while currAddr < 0x2000000: + buffer = self.ReadROM(currAddr + 0xA0, 64)[:16] + if buffer == size_check: break + currAddr *= 2 + rom_size = currAddr + + for i in range(0, len(flash_types)): + if "flash_size" in supp_flash_types[1][flash_types[i]] and rom_size == supp_flash_types[1][flash_types[i]]["flash_size"]: + flash_type_id = flash_types[i] + size_undetected = False + break + else: (flash_id, cfi_s, cfi) = self.CheckFlashChip(limitVoltage=limitVoltage) @@ -1667,7 +1714,7 @@ def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most ho self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True) if cart_type is not None: # reset cartridge if method is known - flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=None) + flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=None, set_we_pin_wr=self._set_we_pin_wr, set_we_pin_audio=self._set_we_pin_audio) flashcart.Reset(full_reset=False) if "method" in cfi: @@ -1737,7 +1784,7 @@ def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most ho time.sleep(0.2) if cart_type is not None: # reset cartridge if method is known - flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=None) + flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=None, set_we_pin_wr=self._set_we_pin_wr, set_we_pin_audio=self._set_we_pin_audio) flashcart.Reset(full_reset=True) flash_id = "" @@ -1792,7 +1839,7 @@ def _BackupROM(self, args): file = open(args["path"], "wb") self.FAST_READ = True - + flashcart = False supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values()) cart_type = copy.deepcopy(supported_carts[args["cart_type"]]) @@ -1802,7 +1849,7 @@ def _BackupROM(self, args): if i == args["cart_type"]: try: cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i]) - flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress) + flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress, set_we_pin_wr=self._set_we_pin_wr, set_we_pin_audio=self._set_we_pin_audio) except: pass @@ -1890,11 +1937,12 @@ def _BackupROM(self, args): buffer = bytearray(size) max_length = self.MAX_BUFFER_LEN + dprint("Max buffer size: 0x{:X}".format(max_length)) if self.FAST_READ is True: if (self.MODE == "AGB" and "command_set" in cart_type and cart_type["command_set"] == "3DMEMORY"): - max_length = 0x1000 + max_length = min(max_length, 0x1000) else: - max_length = 0x2000 + max_length = min(max_length, 0x2000) self.INFO["dump_info"]["transfer_size"] = max_length pos_total = 0 start_address = 0 @@ -1968,21 +2016,31 @@ def _BackupROM(self, args): skip_init = True if len(temp) != buffer_len: + if "verify_write" in args: + self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total}) + else: + self.SetProgress({"action":"UPDATE_POS", "pos":pos_total}) + if (max_length >> 1) < 64: dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}!".format(len(temp), buffer_len, pos_total)) max_length = 64 + elif lives > 18: + dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}!".format(len(temp), buffer_len, pos_total)) else: - dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}! Decreasing maximum transfer buffer length to 0x{:X}.".format(len(temp), buffer_len, pos_total, max_length >> 1)) + dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}! Decreasing maximum transfer buffer size to 0x{:X}.".format(len(temp), buffer_len, pos_total, max_length >> 1)) max_length >>= 1 self.MAX_BUFFER_LEN = max_length + self.INFO["dump_info"]["transfer_size"] = max_length skip_init = False self.DEVICE.reset_input_buffer() self.DEVICE.reset_output_buffer() lives -= 1 if lives == 0: - self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An error occured while reading from the cartridge. When connecting the device, avoid passive USB hubs and try different USB ports/cables.", "abortable":False}) - return False + self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while reading from the cartridge. When connecting the device, avoid passive USB hubs and try different USB ports/cables."}) + self.CANCEL = True + self.ERROR = True + if "verify_write" in args: return False continue elif lives < 20: lives = 20 @@ -1992,8 +2050,12 @@ def _BackupROM(self, args): pos_total += len(temp) if "verify_write" in args: - if pos_total >= len(args["verify_write"]): break + #if pos_total >= len(args["verify_write"]): break check = args["verify_write"][pos_total-len(temp):pos_total] + if Util.DEBUG: + dprint("Writing 0x{:X} bytes to debug_verify.bin".format(len(temp))) + with open("debug_verify.bin", "ab") as f: f.write(temp) + if temp[:len(check)] != check: for i in range(0, pos_total): if (i < len(args["verify_write"]) - 1) and (i < pos_total - 1) and args["verify_write"][i] != buffer[i]: @@ -2001,11 +2063,9 @@ def _BackupROM(self, args): dprint("Skipping RTC area at 0x{:X}".format(i)) else: dprint("Mismatch during verification at 0x{:X}".format(i)) - if Util.DEBUG: - with open("debug_verify.bin", "wb") as f: f.write(temp) return i else: - dprint("Verification successful between 0x{:X} and 0x{:X}".format(pos_total-len(temp), pos_total-1)) + dprint("Verification successful between 0x{:X} and 0x{:X}".format(pos_total-len(temp), pos_total)) self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total}) else: self.SetProgress({"action":"UPDATE_POS", "pos":pos_total}) @@ -2644,7 +2704,7 @@ def _FlashROM(self, args): data_map_import = bytearray(data_map_import) dprint("Hidden sector data loaded") else: - flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress) + flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress, set_we_pin_wr=self._set_we_pin_wr, set_we_pin_audio=self._set_we_pin_audio) rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True @@ -2882,7 +2942,7 @@ def _FlashROM(self, args): sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size) if len(sector_offsets) > 0: flash_capacity = sector_offsets[-1][0] + sector_offsets[-1][1] - if flash_capacity < len(data_import): + if flash_capacity < len(data_import) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]): self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"There are not enough flash sectors available to write this ROM. The maximum capacity is {:s}.".format(Util.formatFileSize(flash_capacity, asInt=False)), "abortable":False}) return False @@ -3131,10 +3191,12 @@ def _FlashROM(self, args): time.sleep(delay) self.CartPowerOn() if self.MODE == "DMG" and _mbc.HasFlashBanks(): _mbc.SelectBankFlash(bank) - else: - time.sleep(delay) + time.sleep(delay) + self.DEVICE.reset_input_buffer() + self.DEVICE.reset_output_buffer() self._cart_write(pos, 0xF0) self._cart_write(pos, 0xFF) + flashcart.Unlock() continue self.CANCEL = True @@ -3178,6 +3240,8 @@ def _FlashROM(self, args): verified = False if "verify_write" in args and args["verify_write"] is True: self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE_VERIFY", "size":len(data_import)}) + if Util.DEBUG: + with open("debug_verify.bin", "wb") as f: pass for sector in write_sectors: verified = True if sector[0] >= len(data_import): break @@ -3192,8 +3256,8 @@ def _FlashROM(self, args): end_address = buffer_pos verified_size = self._BackupROM(verify_args) - if verified_size is not None: dprint("args[\"verify_len\"]=0x{:X}, verified_size=0x{:X}".format(verify_args["verify_len"], verified_size)) - if self.CANCEL: + if isinstance(verified_size, int): dprint("args[\"verify_len\"]=0x{:X}, verified_size=0x{:X}".format(verify_args["verify_len"], verified_size)) + if self.CANCEL or self.ERROR: cancel_args = {"action":"ABORT", "abortable":False} cancel_args.update(self.CANCEL_ARGS) self.CANCEL_ARGS = {} diff --git a/FlashGBX/hw_GBxCartRW_ofw.py b/FlashGBX/hw_GBxCartRW_ofw.py index 8ef00db..8274788 100644 --- a/FlashGBX/hw_GBxCartRW_ofw.py +++ b/FlashGBX/hw_GBxCartRW_ofw.py @@ -5,7 +5,7 @@ # This code is used for official firmware of GBxCart v1.3 only and this code is pure chaos, sorry # Refer to hw_GBxCartRW.py for the much cleaner rewrite (used for GBxCart RW v1.4 and firmware L1 on v1.3) -import time, math, struct, traceback, zlib, copy, hashlib, os, datetime +import time, math, struct, traceback, zlib, copy, hashlib, os, datetime, platform import serial, serial.tools.list_ports from serial import SerialException from .RomFileDMG import RomFileDMG @@ -139,6 +139,7 @@ class GbxDevice: NO_PROG_UPDATE = False FAST_READ = False BAUDRATE = 1000000 + WRITE_DELAY = False def __init__(self): pass @@ -281,6 +282,9 @@ def GetFirmwareVersion(self, more=False): def GetBaudRate(self): return self.BAUDRATE + def ChangeBaudRate(self, _): + return + def GetPCBVersion(self): _, pcb = self.FW if pcb in self.PCB_VERSIONS: @@ -407,6 +411,9 @@ def SetProgress(self, args): if args["action"] == "FINISHED": self.SIGNAL = None + def SetWriteDelay(self, enable=True): + self.WRITE_DELAY = enable + def wait_for_ack(self): buffer = self.read(1) if buffer == False: @@ -473,8 +480,9 @@ def write(self, data, wait_for_ack=False): data = bytearray(data, 'ascii') self.DEVICE.write(data) self.DEVICE.flush() - if wait_for_ack: - return self.wait_for_ack() + if platform.system() == "Darwin" or self.WRITE_DELAY is True: + time.sleep(0.00125) + if wait_for_ack: return self.wait_for_ack() def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=False): self.SIGNAL = None @@ -2498,7 +2506,6 @@ def _FlashROM(self, buffer=bytearray(), start_addr=0, cart_type=None, voltage=3. pos += 64 else: # super slow -- for testing purposes only! - #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Buffer writing for this flash chip is not supported with your device’s firmware version. You can try a newer firmware version which is available through the firmware updater in the “Tools” menu.\n\n{:s}".format(str(flashcart_meta["commands"]["buffer_write"])), "abortable":False}) self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge is currently not supported by {:s} using the current firmware version of the {:s} device. Please try a differnt firmware version or newer hardware revision.".format(APPNAME, self.GetFullName()), "abortable":False}) return False ''' diff --git a/FlashGBX/res/config.zip b/FlashGBX/res/config.zip index 97a2e17..3d6d67b 100644 Binary files a/FlashGBX/res/config.zip and b/FlashGBX/res/config.zip differ diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_4.zip b/FlashGBX/res/fw_GBxCart_RW_v1_4.zip index 85e5b6c..dfa3ad0 100644 Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_4.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_4.zip differ diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip b/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip index 889ac4b..a640f2e 100644 Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip differ diff --git a/README.md b/README.md index 03a2e23..cfef587 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ for [Windows](https://github.com/lesserkuma/FlashGBX/releases), [Linux](https://github.com/lesserkuma/FlashGBX#run-using-python-linux-macos-windows), [macOS](https://github.com/lesserkuma/FlashGBX#run-using-python-linux-macos-windows) -FlashGBX on Windows
GB Camera Album Viewer +FlashGBX on Windows 11
GB Camera Album Viewer ## Introduction @@ -53,6 +53,8 @@ Use this command in a Terminal or Command Prompt window to launch the installed *FlashGBX should work on pretty much any operating system that supports Qt-GUI applications built using [Python](https://www.python.org/downloads/) with [PySide2](https://pypi.org/project/PySide2/) or [PySide6](https://pypi.org/project/PySide6/), [pyserial](https://pypi.org/project/pyserial/), [Pillow](https://pypi.org/project/Pillow/), [setuptools](https://pypi.org/project/setuptools/), [requests](https://pypi.org/project/requests/) and [python-dateutil](https://pypi.org/project/python-dateutil/) packages. To run FlashGBX in portable mode without installing, you can also download the source code archive and call `python3 run.py` after installing the prerequisites yourself.* +*Note: On Linux systems, the `brltty` module may render GBxCart RW devices non-accessible. See the troubleshooting section for details.* + #### Upgrading from an older version 1. Open a Terminal or Command Prompt window @@ -93,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 + - BennVenn MBC3000 RTC cart - BLAZE Xploder GB - BUNG Doctor GB Card 64M - Catskull 32k Gameboy Flash Cart @@ -124,15 +127,18 @@ Use this command in a Terminal or Command Prompt window to launch the installed - insideGadgets 4 MB, 32 KB FRAM, MBC3+RTC - insideGadgets 4 MB (2× 2 MB), 32 KB FRAM, MBC5 - Mr Flash 64M + - Squareboi 4 MB (2× 2 MB) - Game Boy Advance + - Action Replay Ultimate Codes (with SST39VF800A) - Development AGB Cartridge 64M Flash S, E201843 - Development AGB Cartridge 128M Flash S, E201850 - Development AGB Cartridge 256M Flash S, E201868 - Flash2Advance 128M (with 2× 28F640J3A120) - Flash2Advance 256M (with 2× 28F128J3A150) - Flash2Advance Ultra 64M (with 2× 28F320C3B) + - Flash2Advance Ultra 256M (with 8× 3204C3B100) - Flash Advance Card 64M (with 28F640J3A120) - insideGadgets 16 MB, 64K EEPROM with Solar Sensor and RTC options - insideGadgets 32 MB, 1M FLASH with RTC option @@ -164,6 +170,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed - SD007_48BALL_64M_V6 with 36VF3204 - SD007_48BALL_64M_V6 with 29DL163BD-90 - SD007_48BALL_64M_V8 with M29W160ET + - SD007_48BALL_SOP28 with M29W320ET - SD007_BV5 with 29LV160TE-70PFTN - SD007_BV5_DRV with M29W320DT - SD007_BV5_DRV with S29GL032M90TFIR4 @@ -173,6 +180,8 @@ Use this command in a Terminal or Command Prompt window to launch the installed - SD007_BV5_V3 with HY29LV160BT-70 - SD007_BV5_V3 with AM29LV160MB - SD007_K8D3216_32M with MX29LV160CT + - SD007_T40_64BALL_S71_TV_TS28 with TC58FVB016FT-85 + - SD007_T40_64BALL_SOJ28 with 29LV016T - SD007_T40_64BALL_TSOP28 with 29LV016T - SD007_T40_64BALL_TSOP28 with TC58FVB016FT-85¹ - SD007_TSOP_29LV017D with L017D70VC @@ -245,6 +254,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed - BX2006_TSOPBGA_0106 with K8D6316UTM-PI07 - BX2006_TSOPBGA_6108 with M29W640 - DV15 with MSP55LV100G + - F864-3 with M36L0R7050B - GA-07 with unlabeled flash chip - GE28F128W30 with 128W30B0 - M5M29G130AN (no PCB text) @@ -272,6 +282,8 @@ Many different reproduction cartridges share their flash chip command set, so ev * On some Linux systems like Fedora, you may need to install the `python3-pillow-qt` package manually in order for the GUI mode to work. +* On some Linux systems you may see the message “No devices found.” even though you’re using a USB cable capable of data transfers. This may be caused by a module called `brltty` (a driver for refreshable braille displays) that is erroneously interfering and taking over control of any connected USB device that uses the CH340/341 chipset. The solution would be to uninstall or blacklist the `brltty` driver and then reboot the system. + * If you’re using macOS version 10.13 or older, there may be no driver for the *insideGadgets GBxCart RW* device installed on your system. You can either upgrade your macOS version to 10.14+ or manually install a driver which is available [here](https://github.com/adrianmihalko/ch340g-ch34g-ch34x-mac-os-x-driver). ## Miscellaneous @@ -285,6 +297,7 @@ The author would like to thank the following very kind people for their help and - 2358 (bug reports) - 90sFlav (flash chip info) - AcoVanConis (bug reports, flash chip info) +- ALXCO-Hardware (feature suggestions) - AdmirtheSableye (bug reports) - AlexiG (GBxCart RW hardware, bug reports, flash chip info) - AndehX (app icon, flash chip info) @@ -297,11 +310,12 @@ The author would like to thank the following very kind people for their help and - Davidish (flash chip info) - DevDavisNunez (bug reports) - Diddy_Kong (sample cartridge contribution) -- djedditt (testing) +- djedditt (testing, sample cartridge contribution) - Dr-InSide (bug reports) - dyf2007 (flash chip info) - easthighNerd (feature suggestions) - EchelonPrime (flash chip info) +- edo999 (flash chip info) - EmperorOfTigers (bug reports, flash chip info) - endrift (research, mGBA emulator) - ethanstrax (flash chip info) @@ -310,6 +324,7 @@ The author would like to thank the following very kind people for their help and - FerrantePescara (flash chip info) - frarees (bug reports) - Frost Clock (flash chip info) +- gboh (bug reports) - gekkio (bug reports, technical information) - Godan (flash chip info) - Grender (testing) @@ -335,7 +350,7 @@ The author would like to thank the following very kind people for their help and - manuelcm1 (flash chip info) - marv17 (flash chip info, testing, bug reports, feature suggestions) - Mr_V (flash chip info, testing) -- orangeglo (GB Memory Cartridge samples) +- orangeglo (GB Memory Cartridge samples, bug reports) - paarongiroux (bug reports) - Paradoxical (flash chip info) - Rairch (bug reports) @@ -343,12 +358,14 @@ The author would like to thank the following very kind people for their help and - redalchemy (bug reports, flash chip info) - RetroGorek (flash chip info) - RevZ (Linux help, testing, bug reports, flash chip info) +- s1cp (flash chip info) - Satumox (bug reports) - Sgt.DoudouMiel (flash chip info) - Shinichi999 (bug reports) - Sithdown (flash chip info) - skite2001 (flash chip info) - Smelly-Ghost (testing) +- Stitch (flash chip info) - Super Maker (flash chip info, testing) - Tauwasser (research) - t5b6_de (flash chip info) diff --git a/setup.py b/setup.py index 233f43e..d2c6ece 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setuptools.setup( name="FlashGBX", - version="3.20", + version="3.21", author="Lesserkuma", description="Reads and writes Game Boy and Game Boy Advance cartridge data. Supported hardware: GBxCart RW v1.3 and v1.4 by insideGadgets.", url="https://github.com/lesserkuma/FlashGBX",