diff --git a/CHANGES.md b/CHANGES.md index 73fe6ea..0639ec2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,11 @@ # Release notes +### v3.29 (released 2023-05-14) +- Fixed an issue with writing to some flash cartridges using the old GBxCart RW v1.3 hardware revision *(thanks yosoo)* +- Fixed an issue with Batteryless SRAM save data restore *(thanks antPL)* +- Confirmed support for SD007_TSOP_64BALL_SOP28_V2 with unlabeled flash chip *(thanks DevDavisNunez)* +- Added support for SD007_BGA48_71TV_T28_DEEP with M29W640 *(thanks Cristóbal)* +- Minor bug fixes and improvements + ### v3.28 (released 2023-05-05) - Improved support for the BennVenn MBC3000 RTC cart; can now write to Real Time Clock registers - Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py index bb7a28f..f0ece60 100644 --- a/FlashGBX/FlashGBX_GUI.py +++ b/FlashGBX/FlashGBX_GUI.py @@ -202,7 +202,7 @@ class FlashGBX_GUI(QtWidgets.QWidget): self.mnuConfig.addAction("Check for &updates on application startup", lambda: [ self.EnableUpdateCheck() ]) self.mnuConfig.addAction("&Append date && time to filename of save data backups", lambda: self.SETTINGS.setValue("SaveFileNameAddDateTime", str(self.mnuConfig.actions()[1].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) self.mnuConfig.addAction("Prefer full &chip erase", lambda: self.SETTINGS.setValue("PreferChipErase", str(self.mnuConfig.actions()[2].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) - self.mnuConfig.addAction("Extra data &verification", lambda: self.SETTINGS.setValue("VerifyData", str(self.mnuConfig.actions()[3].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) + self.mnuConfig.addAction("&Verify written data", lambda: self.SETTINGS.setValue("VerifyData", 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("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"))) @@ -1613,7 +1613,7 @@ class FlashGBX_GUI(QtWidgets.QWidget): if self.CONN.GetMode() == "DMG" and mbc in (0x10, 0x110) and not self.CONN.IsClkConnected(): rtc = False elif erase or Util.save_size_includes_rtc(mode=self.CONN.GetMode(), mbc=mbc, save_size=filesize, save_type=save_type): - msg = "A Real Time Clock cartridge was detected. Do you want the Real Time Clock register values to be also written?" + msg = "A Real Time Clock cartridge was detected. Do you want the Real Time Clock register values to be written as well?" cb = QtWidgets.QCheckBox("&Adjust RTC", checked=True) msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel) msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes) diff --git a/FlashGBX/Flashcart.py b/FlashGBX/Flashcart.py index f2af9b4..c2f059b 100644 --- a/FlashGBX/Flashcart.py +++ b/FlashGBX/Flashcart.py @@ -129,8 +129,8 @@ class Flashcart: if "write_pin" not in self.CONFIG: return False return (self.CONFIG["write_pin"] == "WR+RESET") - def GetFlashSize(self): - if "flash_size" not in self.CONFIG: return False + def GetFlashSize(self, default=False): + if "flash_size" not in self.CONFIG: return default return self.CONFIG["flash_size"] def GetBufferSize(self): diff --git a/FlashGBX/Util.py b/FlashGBX/Util.py index 968c8fb..f08d571 100644 --- a/FlashGBX/Util.py +++ b/FlashGBX/Util.py @@ -7,9 +7,9 @@ from enum import Enum # Common constants APPNAME = "FlashGBX" -VERSION_PEP440 = "3.28" +VERSION_PEP440 = "3.29" VERSION = "v{:s}".format(VERSION_PEP440) -VERSION_TIMESTAMP = 1683283512 +VERSION_TIMESTAMP = 1684066518 DEBUG = False DEBUG_LOG = [] APP_PATH = "" @@ -841,13 +841,14 @@ def get_mbc_name(id): def save_size_includes_rtc(mode, mbc, save_size, save_type): rtc_size = 0x10 if mode == "DMG": + save_type = DMG_Header_RAM_Sizes_Map.index(save_type) if get_mbc_name(mbc) in ("MBC3", "MBC30"): rtc_size = 0x30 elif get_mbc_name(mbc) == "HuC-3": rtc_size = 0x0C elif get_mbc_name(mbc) == "TAMA5": rtc_size = 0x28 - return (((DMG_Header_RAM_Sizes_Flasher_Map[save_type] + rtc_size) % save_size) != rtc_size) + return (((DMG_Header_RAM_Sizes_Flasher_Map[save_type] + rtc_size) % save_size) == 0) elif mode == "AGB": rtc_size = 0x10 - return (((AGB_Header_Save_Sizes[save_type] + rtc_size) % save_size) != rtc_size) + return (((AGB_Header_Save_Sizes[save_type] + rtc_size) % save_size) == 0) return False def validate_datetime_format(string, format): diff --git a/FlashGBX/config/fc_DMG_GL032M11BAIR4.txt b/FlashGBX/config/fc_DMG_GL032M11BAIR4.txt index 78f018b..9797868 100644 --- a/FlashGBX/config/fc_DMG_GL032M11BAIR4.txt +++ b/FlashGBX/config/fc_DMG_GL032M11BAIR4.txt @@ -4,13 +4,15 @@ "SD007_48BALL_64M with GL032M11BAIR4", "SD007_48BALL_64M_V2 with GL032M11BAIR4", "S29GL032N90T and ALTERA CPLD (MBC5)", - "29LV320TE and ALTERA CPLD (no PCB text)" + "29LV320TE and ALTERA CPLD (no PCB text)", + "SD007_TSOP_64BALL_SOP28_V2 with unlabeled flash chip" ], "flash_ids":[ [ 0x02, 0x02, 0x7D, 0x7D ], [ 0x02, 0x02, 0x7D, 0x7D ], [ 0x02, 0x02, 0x7D, 0x7D ], - [ 0x04, 0x04, 0xF5, 0xF5 ] + [ 0x04, 0x04, 0xF5, 0xF5 ], + [ 0x02, 0x02, 0x7D, 0x7D ] ], "voltage":3.3, "flash_size":0x400000, diff --git a/FlashGBX/config/fc_DMG_M29W640.txt b/FlashGBX/config/fc_DMG_M29W640.txt index af85c2b..2a64e2d 100644 --- a/FlashGBX/config/fc_DMG_M29W640.txt +++ b/FlashGBX/config/fc_DMG_M29W640.txt @@ -1,9 +1,11 @@ { "type":"DMG", "names":[ - "SD007_48BALL_64M with M29W640" + "SD007_48BALL_64M with M29W640", + "SD007_BGA48_71TV_T28_DEEP with M29W640" ], "flash_ids":[ + [ 0x20, 0x00, 0x7D, 0x00 ], [ 0x20, 0x00, 0x7D, 0x00 ] ], "voltage":3.3, @@ -11,11 +13,8 @@ "start_addr":0, "first_bank":1, "write_pin":"WR", - "sector_size":[ - [0x02000, 8], - [0x10000, 127] - ], - "chip_erase_timeout":70, + "sector_size_from_cfi":true, + "_chip_erase_timeout":70, "command_set":"AMD", "commands":{ "reset":[ @@ -29,7 +28,7 @@ "read_cfi":[ [ 0xAA, 0x98 ] ], - "chip_erase":[ + "_chip_erase":[ [ 0xAAA, 0xA9 ], [ 0x555, 0x56 ], [ 0xAAA, 0x80 ], @@ -37,7 +36,7 @@ [ 0x555, 0x56 ], [ 0xAAA, 0x10 ] ], - "chip_erase_wait_for":[ + "_chip_erase_wait_for":[ [ null, null, null ], [ null, null, null ], [ null, null, null ], diff --git a/FlashGBX/hw_GBxCartRW.py b/FlashGBX/hw_GBxCartRW.py index bf1c034..862f2e1 100644 --- a/FlashGBX/hw_GBxCartRW.py +++ b/FlashGBX/hw_GBxCartRW.py @@ -683,26 +683,26 @@ class GbxDevice: if Util.DEBUG: with open("debug_header.bin", "wb") as f: f.write(header) if header is False or len(header) != 0x180: - print("{:s}\n{:s}Couldn’t read the cartridge information. Please try again.{:s}".format(str(header), ANSI.RED, ANSI.RESET)) return False # Parse ROM header if self.MODE == "DMG": data = RomFileDMG(header).GetHeader() - if data["game_title"] == "TETRIS" and hashlib.sha1(header).digest() != bytearray([0x1D, 0x69, 0x2A, 0x4B, 0x31, 0x7A, 0xA5, 0xE9, 0x67, 0xEE, 0xC2, 0x2F, 0xCC, 0x32, 0x43, 0x8C, 0xCB, 0xC5, 0x78, 0x0B]): # Sachen + if "game_title" in data and data["game_title"] == "TETRIS" and hashlib.sha1(header).digest() != bytearray([0x1D, 0x69, 0x2A, 0x4B, 0x31, 0x7A, 0xA5, 0xE9, 0x67, 0xEE, 0xC2, 0x2F, 0xCC, 0x32, 0x43, 0x8C, 0xCB, 0xC5, 0x78, 0x0B]): # Sachen header = self.ReadROM(0, 0x280) data = RomFileDMG(header).GetHeader() - if data["logo_correct"] is False and not b"Future Console Design" in header: # workaround for strange bootlegs + if "logo_correct" in data and data["logo_correct"] is False and not b"Future Console Design" in header: # workaround for strange bootlegs self._cart_write(0, 0xFF) time.sleep(0.1) header = self.ReadROM(0, 0x280) data = RomFileDMG(header).GetHeader() - if data["mapper_raw"] == 0x203 or b"Future Console Design" in header: # Xploder GB version number + if "mapper_raw" in data and data["mapper_raw"] == 0x203 or b"Future Console Design" in header: # Xploder GB version number self._cart_write(0x0006, 0) header[0:0x10] = self.ReadROM(0x4000, 0x10) header[0xD0:0xE0] = self.ReadROM(0x40D0, 0x10) data = RomFileDMG(header).GetHeader() - + if data == {}: return False + data["has_rtc"] = False data["rtc_string"] = "Not available" if data["logo_correct"] is True: @@ -1100,11 +1100,13 @@ class GbxDevice: elif self.MODE == "AGB": command = "AGB_CART_READ" - for _ in range(0, num): + for n in range(0, num): self._write(self.DEVICE_CMD[command]) temp = self._read(length) if isinstance(temp, int): temp = bytearray([temp]) - if temp is False or len(temp) != length: return bytearray() + if temp is False or len(temp) != length: + print("{:s}Error while trying to read 0x{:X} bytes from cartridge ROM at 0x{:X} in iteration {:d} of {:d}:{:s}\n{:s}".format(ANSI.RED, length, address, n, num, ANSI.RESET, str(temp))) + return bytearray() buffer += temp if self.INFO["action"] in (self.ACTIONS["ROM_READ"], self.ACTIONS["SAVE_READ"], self.ACTIONS["ROM_WRITE_VERIFY"]) and not self.NO_PROG_UPDATE: self.SetProgress({"action":"READ", "bytes_added":len(temp)}) @@ -1379,9 +1381,6 @@ class GbxDevice: if ret != 0x03: self._write(self.DEVICE_CMD["FLASH_PROGRAM"]) ret = self._write(data, wait=True) - if rumble_stop and i == 0: - dprint("Sending rumble stop command") - self._cart_write(address=0xC6, value=0x00, flashcart=True) if ret not in (0x01, 0x03): dprint("Flash error at 0x{:X} in iteration {:d} of {:d} while trying to write a total of 0x{:X} bytes (response = {:s})".format(address, i, num, len(buffer), str(ret))) @@ -1389,6 +1388,11 @@ class GbxDevice: self.SKIPPING = False return False pos += len(data) + + if rumble_stop and pos % flash_buffer_size == 0: + dprint("Sending rumble stop command") + self._cart_write(address=0xC6, value=0x00, flashcart=True) + rumble_stop = False address += length if ((pos % length) * 10 == 0) and (self.INFO["action"] in (self.ACTIONS["ROM_WRITE"], self.ACTIONS["SAVE_WRITE"]) and not self.NO_PROG_UPDATE): @@ -3292,7 +3296,7 @@ class GbxDevice: flash_capacity = len(data_import) if sector_map is not None and sector_map is not False: smallest_sector_size = flashcart.GetSmallestSectorSize() - sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size) + sector_offsets = flashcart.GetSectorOffsets(rom_size=flashcart.GetFlashSize(default=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) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]): @@ -3365,7 +3369,7 @@ class GbxDevice: # ↓↓↓ Chip erase chip_erase = False - if flashcart.SupportsChipErase(): + if flashcart.SupportsChipErase() and not flash_offset > 0: if flashcart.SupportsSectorErase() and args["prefer_chip_erase"] is False and sector_map is not False: chip_erase = False else: diff --git a/FlashGBX/res/config.zip b/FlashGBX/res/config.zip index 0289515..5f4f97f 100644 Binary files a/FlashGBX/res/config.zip and b/FlashGBX/res/config.zip differ diff --git a/README.md b/README.md index 8eb6b85..49801b7 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed - SD007_48BALL_64M_V6 with 29DL163BD-90 - SD007_48BALL_64M_V8 with M29W160ET - SD007_48BALL_SOP28 with M29W320ET + - SD007_BGA48_71TV_T28_DEEP with M29W640 - SD007_BV5 with 29LV160TE-70PFTN - SD007_BV5_DRV with M29W320DT - SD007_BV5_DRV with S29GL032M90TFIR4 @@ -207,6 +208,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed - SD007_TSOP_64BALL_SOJ28 with unlabeled flash chip - SD007_TSOP_64BALL_SOP28 with EN29LV160AB-70TCP - SD007_TSOP_64BALL_SOP28 with unlabeled flash chip + - SD007_TSOP_64BALL_SOP28_V2 with unlabeled flash chip - SD008-6810-512S with MSP55LV512 - SD008-6810-V4 with MX29GL256EL - SD008-6810-V5 with MX29CL256FH @@ -302,7 +304,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, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, 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, KOOORAY, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, 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, Zeii, Zelante, zvxr +2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, Cristóbal, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, 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, KOOORAY, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, 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, zvxr ## DISCLAIMER diff --git a/setup.py b/setup.py index 647d604..e7a7835 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read( setuptools.setup( name="FlashGBX", - version="3.28", + version="3.29", 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",