This commit is contained in:
Lesserkuma 2023-06-18 13:14:53 +02:00
parent 007085b24c
commit 2951fdc7ce
12 changed files with 202 additions and 168 deletions

View File

@ -1,4 +1,10 @@
# Release notes
### v3.31 (released 2023-06-18)
- Improved support for 32 MiB cartridges that have one of the EEPROM save types (fixes both backups and writes of B3CJ, B53E, B53P, BBAE, BBAP, BC2J, BFRP, BH3E, BH3P, BH8E, BJPP, BU7E, BU7P, BX3E, BX3P, BYUE, BYUJ, BYUP)
- Confirmed support for BGA64B-71-TV-DEEP with 256M29EML *(thanks Leitplanke)*
- Updated the Game Boy Advance lookup databases for save types, ROM sizes and checksums
- Minor bug fixes and improvements
### v3.30 (released 2023-06-06)
- Improved auto-detection of official GBA Video cartridges with the 3D Memory mapper including those that are less than 64 MiB
- Added support for Unknown 29LV320 variant (no PCB text) *(thanks Zoo)*

View File

@ -315,31 +315,32 @@ class FlashGBX_CLI():
else:
msg = "The ROM was dumped, but the checksum is not correct."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
msg += "\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += "\nThis may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
elif self.CONN.GetMode() == "AGB":
print("CRC32: {:08x}".format(self.CONN.INFO["file_crc32"]))
print("SHA-1: {:s}\n".format(self.CONN.INFO["file_sha1"]))
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
print("{:s}The ROM backup is complete and the checksum was verified successfully!{:s}".format(ANSI.GREEN, ANSI.RESET))
elif Util.AGB_Global_CRC32 == 0:
if "db" in self.CONN.INFO and self.CONN.INFO["db"] is not None:
if self.CONN.INFO["db"]["rc"] == self.CONN.INFO["file_crc32"]:
print("{:s}The ROM backup is complete and the checksum was verified successfully!{:s}".format(ANSI.GREEN, ANSI.RESET))
else:
msg = "The ROM backup is complete, but the checksum doesnt match the known database entry."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += "\nThis may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
else:
msg = "The ROM backup is complete! As there is no known checksum for this ROM in the database, verification was skipped."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\nNOTE: A data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
else:
msg = "The ROM backup is complete, but the checksum doesnt match the known database entry."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += "\nThis may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
msg += "\nNOTE: A data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
self.CONN.INFO["last_action"] = 0
if not "debug" in self.ARGS and self.CONN.GetMode() == "DMG" and self.CONN.INFO["mapper_raw"] == 252 and self.CONN.INFO["transferred"] == 131072: # Pocket Camera / 128 KB: # 128 KB
if not "debug" in self.ARGS and self.CONN.GetMode() == "DMG" and self.CONN.INFO["mapper_raw"] == 252 and self.CONN.INFO["transferred"] == 131072: # Pocket Camera / 128 KiB: # 128 KiB
answer = input("Would you like to extract Game Boy Camera pictures to “{:s}” now? [Y/n]: ".format(Util.formatPathOS(os.path.abspath(os.path.splitext(self.CONN.INFO["last_path"])[0]), end_sep=True) + "IMG_PC**.{:s}".format(self.ARGS["argparsed"].gbcamera_outfile_format))).strip().lower()
if answer != "n":
pc = PocketCamera()
@ -532,19 +533,19 @@ class FlashGBX_CLI():
bad_read = True
s += "ROM Checksum: "
Util.AGB_Global_CRC32 = 0
#Util.AGB_Global_CRC32 = 0
db_agb_entry = data["db"]
if db_agb_entry != None:
if data["rom_size_calc"] < 0x400000:
s += "In database (0x{:06X})\n".format(db_agb_entry['rc'])
Util.AGB_Global_CRC32 = db_agb_entry['rc']
s += "ROM Size: {:d} MB\n".format(int(db_agb_entry['rs']/1024/1024))
#Util.AGB_Global_CRC32 = db_agb_entry['rc']
s += "ROM Size: {:d} MiB\n".format(int(db_agb_entry['rs']/1024/1024))
data['rom_size'] = db_agb_entry['rs']
elif data["rom_size"] != 0:
s += "Not in database\n"
if not data["rom_size"] in Util.AGB_Header_ROM_Sizes_Map:
data["rom_size"] = 0x2000000
s += "ROM Size: {:d} MB\n".format(int(data["rom_size"]/1024/1024))
s += "ROM Size: {:d} MiB\n".format(int(data["rom_size"]/1024/1024))
else:
s += "Not in database\n"
s += "ROM Size: Not detected\n"
@ -640,7 +641,7 @@ class FlashGBX_CLI():
if "flash_size" in supp_cart_types[1][cart_type_id]:
size = supp_cart_types[1][cart_type_id]["flash_size"]
msg_flash_size_s = "ROM Size: {:s}\n".format(Util.formatFileSize(size, asInt=True))
msg_flash_size_s = "ROM Size: {:s}\n".format(Util.formatFileSize(size=size, asInt=True))
if self.CONN.GetMode() == "DMG":
if "mbc" in supp_cart_types[1][cart_type_id]:
@ -724,7 +725,7 @@ class FlashGBX_CLI():
try:
rom_size = Util.DMG_Header_ROM_Sizes_Flasher_Map[header["rom_size_raw"]]
except:
print("{:s}Couldnt determine ROM size, will use 8 MB. It can also be manually set with the “--dmg-romsize” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
print("{:s}Couldnt determine ROM size, will use 8 MiB. It can also be manually set with the “--dmg-romsize” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
rom_size = 8 * 1024 * 1024
else:
sizes = [ "auto", "32kb", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb" ]
@ -849,10 +850,10 @@ class FlashGBX_CLI():
try:
if os.path.getsize(path) > 0x10000000: # reject too large files to avoid exploding RAM
print("{:s}ROM files bigger than 256 MB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
print("{:s}ROM files bigger than 256 MiB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
return
elif os.path.getsize(path) < 0x400:
print("{:s}ROM files smaller than 1 KB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
print("{:s}ROM files smaller than 1 KiB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
return
#with open(path, "rb") as file: buffer = bytearray(file.read())
@ -866,7 +867,7 @@ class FlashGBX_CLI():
rom_size = os.stat(path).st_size
if "flash_size" in carts[cart_type]:
if rom_size > carts[cart_type]['flash_size']:
msg = "The selected flash cartridge type seems to support ROMs that are up to {:s} in size, but the file you selected is {:s}.".format(Util.formatFileSize(carts[cart_type]['flash_size']), Util.formatFileSize(os.path.getsize(path), roundUp=True))
msg = "The selected flash cartridge type seems to support ROMs that are up to {:s} in size, but the file you selected is {:s}.".format(Util.formatFileSize(size=carts[cart_type]['flash_size']), Util.formatFileSize(size=os.path.getsize(path)))
msg += " You can still give it a try, but its possible that its too large which may cause the ROM writing to fail."
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
answer = input("Do you want to continue? [y/N]: ").strip().lower()
@ -1187,9 +1188,9 @@ class FlashGBX_CLI():
found_length = len(test2) - found_offset
if self.CONN.GetMode() == "DMG":
print("\n{:s}Done! The writable save data size is {:s} out of {:s} checked.{:s}".format(ANSI.GREEN, Util.formatFileSize(found_length), Util.formatFileSize(Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(save_type)]), ANSI.RESET))
print("\n{:s}Done! The writable save data size is {:s} out of {:s} checked.{:s}".format(ANSI.GREEN, Util.formatFileSize(size=found_length), Util.formatFileSize(size=Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(save_type)]), ANSI.RESET))
elif self.CONN.GetMode() == "AGB":
print("\n{:s}Done! The writable save data size using save type “{:s}” is {:s}.{:s}".format(ANSI.GREEN, Util.AGB_Header_Save_Types[save_type], Util.formatFileSize(found_length), ANSI.RESET))
print("\n{:s}Done! The writable save data size using save type “{:s}” is {:s}.{:s}".format(ANSI.GREEN, Util.AGB_Header_Save_Types[save_type], Util.formatFileSize(size=found_length), ANSI.RESET))
try:
(_, _, cfi) = self.CONN.CheckFlashChip(limitVoltage=False)

View File

@ -511,7 +511,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.SETTINGS.setValue("UpdateCheck", "disabled")
if update_check and update_check.lower() == "enabled":
print("")
url = "https://api.github.com/repos/lesserkuma/FlashGBX/releases?per_page=1"
url = "https://api.github.com/repos/lesserkuma/FlashGBX/releases/latest"
site = "https://github.com/lesserkuma/FlashGBX/releases/latest"
try:
ret = requests.get(url, allow_redirects=True, timeout=1.5)
@ -527,7 +527,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if ret is not False and ret.status_code == 200:
ret = ret.content
try:
ret = json.loads(ret)[0]
ret = json.loads(ret)
if 'tag_name' in ret:
latest_version = str(ret['tag_name'])
if pkg_resources.parse_version(latest_version) == pkg_resources.parse_version(VERSION_PEP440):
@ -933,7 +933,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
msg = "The ROM was dumped, but the checksum is not correct."
button_gmmc1 = None
if self.CONN.INFO["loop_detected"] is not False:
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
if self.CONN.GetMode() == "DMG" and self.cmbDMGHeaderMapperResult.currentText() == "MBC1":
@ -959,35 +959,36 @@ class FlashGBX_GUI(QtWidgets.QWidget):
QtCore.QTimer.singleShot(1, lambda: [ self.CONN.BackupROM(fncSetProgress=self.PROGRESS.SetProgress, args=self.STATUS["args"]) ])
return
elif self.CONN.GetMode() == "AGB":
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
self.lblAGBHeaderROMChecksumResult.setText("Valid (0x{:06X})".format(Util.AGB_Global_CRC32))
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
self.lblStatus4a.setText("Done!")
msg = "The ROM backup is complete and the checksum was verified successfully!"
msgbox.setText(msg + msg_te)
msgbox.exec()
elif Util.AGB_Global_CRC32 == 0:
self.lblAGBHeaderROMChecksumResult.setText("0x{:06X}".format(self.CONN.INFO["rom_checksum_calc"]))
if "db" in self.CONN.INFO and self.CONN.INFO["db"] is not None:
if self.CONN.INFO["db"]["rc"] == self.CONN.INFO["file_crc32"]:
self.lblAGBHeaderROMChecksumResult.setText("Valid (0x{:06X})".format(self.CONN.INFO["db"]["rc"]))
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
self.lblStatus4a.setText("Done!")
msg = "The ROM backup is complete and the checksum was verified successfully!"
msgbox.setText(msg + msg_te)
msgbox.exec()
else:
self.lblAGBHeaderROMChecksumResult.setText("Invalid (0x{:06X}≠0x{:06X})".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["db"]["rc"]))
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
self.lblStatus4a.setText("Done.")
msg = "The ROM backup is complete, but the checksum doesnt match the known database entry."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
msgbox.setText(msg + msg_te)
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
msgbox.exec()
else:
self.lblAGBHeaderROMChecksumResult.setText("0x{:06X}".format(self.CONN.INFO["file_crc32"]))
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
self.lblStatus4a.setText("Done!")
msg = "The ROM backup is complete! As there is no known checksum for this ROM in the database, verification was skipped."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\n\nNOTE: A data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
msg += "\n\nNOTE: A data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
msgbox.setText(msg + msg_te)
msgbox.exec()
else:
self.lblAGBHeaderROMChecksumResult.setText("Invalid (0x{:06X}≠0x{:06X})".format(self.CONN.INFO["rom_checksum_calc"], Util.AGB_Global_CRC32))
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
self.lblStatus4a.setText("Done.")
msg = "The ROM backup is complete, but the checksum doesnt match the known database entry."
if self.CONN.INFO["loop_detected"] is not False:
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
msgbox.setText(msg + msg_te)
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
msgbox.exec()
if msgbox.clickedButton() == button_dump_report:
if not (dump_report is not False and dumpinfo_file != "" and temp is True):
@ -1007,7 +1008,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
dontShowAgainCameraSavePopup = str(self.SETTINGS.value("SkipCameraSavePopup", default="disabled")).lower() == "enabled"
if not dontShowAgainCameraSavePopup:
if self.CONN.GetMode() == "DMG" and self.CONN.INFO["mapper_raw"] == 252 and self.CONN.INFO["transferred"] == 131072: # Pocket Camera / 128 KB
if self.CONN.GetMode() == "DMG" and self.CONN.INFO["mapper_raw"] == 252 and self.CONN.INFO["transferred"] == 131072: # Pocket Camera / 128 KiB
cbCameraSavePopup = QtWidgets.QCheckBox("Dont show this message again", checked=dontShowAgain)
msgboxCameraPopup = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Would you like to load your save data with the GB Camera Viewer now?")
msgboxCameraPopup.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
@ -1055,14 +1056,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
elif self.CONN.GetMode() == "AGB":
cart_types = self.CONN.GetSupportedCartridgesAGB()[0]
cart_type_str = " ({:s})".format(cart_types[self.CONN.INFO["dump_info"]["cart_type"]])
msg_v += "\n\nTips:\n- Clean cartridge contacts\n- Check soldering if its a DIY cartridge\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}\n- Check cartridge ROM storage size (at least {:s} is required)".format(cart_type_str, Util.formatFileSize(self.CONN.INFO["verify_error_params"]["rom_size"]))
msg_v += "\n\nTips:\n- Clean cartridge contacts\n- Check soldering if its a DIY cartridge\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}\n- Check cartridge ROM storage size (at least {:s} is required)".format(cart_type_str, Util.formatFileSize(size=self.CONN.INFO["verify_error_params"]["rom_size"]))
if "mapper_selection_type" in self.CONN.INFO["verify_error_params"]:
if self.CONN.INFO["verify_error_params"]["mapper_selection_type"] == 1: # manual
msg_v += "\n- Check mapper type used: {:s} (manual selection)".format(self.CONN.INFO["verify_error_params"]["mapper_name"])
elif self.CONN.INFO["verify_error_params"]["mapper_selection_type"] == 2: # forced by cart type
msg_v += "\n- Check mapper type used: {:s} (forced by selected cartridge type)".format(self.CONN.INFO["verify_error_params"]["mapper_name"])
if self.CONN.INFO["verify_error_params"]["rom_size"] > self.CONN.INFO["verify_error_params"]["mapper_max_size"]:
msg_v += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(self.CONN.INFO["verify_error_params"]["mapper_max_size"]))
msg_v += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(size=self.CONN.INFO["verify_error_params"]["mapper_max_size"]))
msg_v += "\n\nDo you want to try and write the sectors again that failed verification?"
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg_v, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
@ -1132,7 +1133,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if cart_types[1][index] == "RETAIL": # special keyword
pass
else:
if "flash_size" in cart_types[1][index]:
if "flash_size" in cart_types[1][index] and cart_types[1][index]["flash_size"] in Util.AGB_Header_ROM_Sizes_Map:
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(Util.AGB_Header_ROM_Sizes_Map.index(cart_types[1][index]["flash_size"]))
self.STATUS["cart_type"] = cart_types[1][index]
@ -1260,7 +1261,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
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)
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "ROM files bigger than 256 MiB are not supported.", QtWidgets.QMessageBox.Ok)
return
with open(path, "rb") as file:
@ -1273,7 +1274,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
rom_size = os.stat(path).st_size
if "flash_size" in carts[cart_type]:
if rom_size > carts[cart_type]['flash_size']:
msg = "The selected flash cartridge type seems to support ROMs that are up to {:s} in size, but the file you selected is {:s}.".format(Util.formatFileSize(carts[cart_type]['flash_size']), Util.formatFileSize(os.path.getsize(path), roundUp=True))
msg = "The selected flash cartridge type seems to support ROMs that are up to {:s} in size, but the file you selected is {:s}.".format(Util.formatFileSize(size=carts[cart_type]['flash_size']), Util.formatFileSize(size=os.path.getsize(path)))
msg += " You can still give it a try, but its possible that its too large which may cause the ROM writing to fail."
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
if answer == QtWidgets.QMessageBox.Cancel: return
@ -1514,7 +1515,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if save_type == 0:
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
return
save_size = Util.AGB_Header_Save_Sizes[save_type]
#save_size = Util.AGB_Header_Save_Sizes[save_type]
else:
return
if not self.CheckHeader(): return
@ -1908,7 +1909,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
"params": [
# ID, Type, Value(s), Default Index
[ "loc", "cmb_e", "Location:", [ "0x{:X}".format(l) for l in locs ], loc_index ],
[ "len", "cmb", "Size:", [ Util.formatFileSize(s, asInt=True) for s in lens ], len_index ],
[ "len", "cmb", "Size:", [ Util.formatFileSize(size=s, asInt=True) for s in lens ], len_index ],
]
}
dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
@ -2208,7 +2209,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
self.lblAGBHeaderROMChecksumResult.setText("Not available")
Util.AGB_Global_CRC32 = 0
if data["db"] is None:
self.lblAGBHeaderROMChecksumResult.setText("(Not in database)")
@ -2216,7 +2216,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(Util.AGB_Header_ROM_Sizes_Map.index(data["db"]['rs']))
if data["rom_size_calc"] < 0x400000:
self.lblAGBHeaderROMChecksumResult.setText("In database (0x{:06X})".format(data["db"]['rc']))
Util.AGB_Global_CRC32 = data["db"]['rc']
elif data["rom_size"] != 0:
if not data["rom_size"] in Util.AGB_Header_ROM_Sizes_Map:
data["rom_size"] = 0x2000000
@ -2355,11 +2354,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
try:
if "Batteryless SRAM" in Util.AGB_Header_Save_Types[save_type]:
if save_size == 0:
temp += " (unknown size)<br><b>Batteryless SRAM Location:</b> 0x{:X}0x{:X} ({:s})".format(header["batteryless_sram"]["bl_offset"], header["batteryless_sram"]["bl_offset"]+header["batteryless_sram"]["bl_size"]-1, Util.formatFileSize(header["batteryless_sram"]["bl_size"], asInt=True))
temp += " (unknown size)<br><b>Batteryless SRAM Location:</b> 0x{:X}0x{:X} ({:s})".format(header["batteryless_sram"]["bl_offset"], header["batteryless_sram"]["bl_offset"]+header["batteryless_sram"]["bl_size"]-1, Util.formatFileSize(size=header["batteryless_sram"]["bl_size"], asInt=True))
elif save_size == header["batteryless_sram"]["bl_size"]:
temp += " ({:s})<br><b>Batteryless SRAM Location:</b> 0x{:X}0x{:X} ({:s})".format(Util.formatFileSize(save_size, asInt=True), header["batteryless_sram"]["bl_offset"], header["batteryless_sram"]["bl_offset"]+header["batteryless_sram"]["bl_size"]-1, Util.formatFileSize(header["batteryless_sram"]["bl_size"], asInt=True))
temp += " ({:s})<br><b>Batteryless SRAM Location:</b> 0x{:X}0x{:X} ({:s})".format(Util.formatFileSize(size=save_size, asInt=True), header["batteryless_sram"]["bl_offset"], header["batteryless_sram"]["bl_offset"]+header["batteryless_sram"]["bl_size"]-1, Util.formatFileSize(size=header["batteryless_sram"]["bl_size"], asInt=True))
else:
temp += " ({:s})<br><b>Batteryless SRAM Location:</b> 0x{:X}0x{:X} ({:s})".format(Util.formatFileSize(save_size, asInt=True), header["batteryless_sram"]["bl_offset"], header["batteryless_sram"]["bl_offset"]+header["batteryless_sram"]["bl_size"]-1, Util.formatFileSize(header["batteryless_sram"]["bl_size"], asInt=True))
temp += " ({:s})<br><b>Batteryless SRAM Location:</b> 0x{:X}0x{:X} ({:s})".format(Util.formatFileSize(size=save_size, asInt=True), header["batteryless_sram"]["bl_offset"], header["batteryless_sram"]["bl_offset"]+header["batteryless_sram"]["bl_size"]-1, Util.formatFileSize(size=header["batteryless_sram"]["bl_size"], asInt=True))
except:
pass
@ -2391,7 +2390,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if "flash_size" in supp_cart_types[1][cart_type_id]:
size = supp_cart_types[1][cart_type_id]["flash_size"]
msg_flash_size_s = "<b>ROM Size:</b> {:s}<br>".format(Util.formatFileSize(size, asInt=True))
msg_flash_size_s = "<b>ROM Size:</b> {:s}<br>".format(Util.formatFileSize(size=size, asInt=True))
if self.CONN.GetMode() == "DMG":
if "mbc" in supp_cart_types[1][cart_type_id]:
@ -2675,7 +2674,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.SetProgressBars(min=0, max=size, value=pos)
elif args["action"] == "FINISHED":
if pos > 0:
self.lblStatus1aResult.setText(Util.formatFileSize(pos))
self.lblStatus1aResult.setText(Util.formatFileSize(size=pos))
self.FinishOperation()
elif args["action"] == "ABORT":
wd = 10
@ -2727,7 +2726,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.btnCancel.setEnabled(args["abortable"])
else:
self.btnCancel.setEnabled(True)
self.lblStatus1aResult.setText("{:s}".format(Util.formatFileSize(pos)))
self.lblStatus1aResult.setText("{:s}".format(Util.formatFileSize(size=pos)))
if speed > 0:
self.lblStatus2aResult.setText("{:.2f} KiB/s".format(speed))
else:

View File

@ -148,15 +148,9 @@ class DMG_MBC:
def SelectBankROM(self, index):
dprint(self.GetName(), "|", index)
if index == 0 or index >= 256:
commands = [
[ 0x3000, ((index >> 8) & 0xFF) ],
[ 0x2100, index & 0xFF ],
]
else:
commands = [
[ 0x2100, index & 0xFF ],
]
commands = [
[ 0x2100, index & 0xFF ],
]
start_address = 0 if index == 0 else 0x4000
@ -459,6 +453,18 @@ class DMG_MBC5(DMG_MBC):
]
self.CartWrite(commands)
def SelectBankROM(self, index):
dprint(self.GetName(), "|", index)
commands = [
[ 0x3000, ((index >> 8) & 0xFF) ],
[ 0x2100, index & 0xFF ],
]
start_address = 0 if index == 0 else 0x4000
self.CartWrite(commands)
return (start_address, self.ROM_BANK_SIZE)
def GetMaxROMSize(self):
return 8*1024*1024
@ -703,15 +709,10 @@ class DMG_GMMC1(DMG_MBC5):
def SelectBankROM(self, index):
dprint(self.GetName(), "|", index)
if index == 0 or index >= 256:
commands = [
[ 0x2100, index & 0xFF ],
]
else:
commands = [
[ 0x2100, index & 0xFF ],
]
commands = [
[ 0x2100, index & 0xFF ],
]
start_address = 0 if index == 0 else 0x4000
self.CartWrite(commands)

View File

@ -7,9 +7,9 @@ from enum import Enum
# Common constants
APPNAME = "FlashGBX"
VERSION_PEP440 = "3.30"
VERSION_PEP440 = "3.31"
VERSION = "v{:s}".format(VERSION_PEP440)
VERSION_TIMESTAMP = 1686057604
VERSION_TIMESTAMP = 1687091551
DEBUG = False
DEBUG_LOG = []
APP_PATH = ""
@ -19,7 +19,6 @@ AGB_Header_ROM_Sizes = [ "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2
AGB_Header_ROM_Sizes_Map = [ 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000 ]
AGB_Header_Save_Types = [ "None", "4K EEPROM (512 Bytes)", "64K EEPROM (8 KiB)", "256K SRAM/FRAM (32 KiB)", "512K FLASH (64 KiB)", "1M FLASH (128 KiB)", "8M DACS (1 MiB)", "Unlicensed 512K SRAM (64 KiB)", "Unlicensed 1M SRAM (128 KiB)", "Unlicensed Batteryless SRAM" ]
AGB_Header_Save_Sizes = [ 0, 512, 8192, 32768, 65536, 131072, 1048576, 65536, 131072, 0 ]
AGB_Global_CRC32 = 0
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS" }
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000 ]
@ -282,30 +281,26 @@ def isx2bin(buffer):
break
return data_output[:temp]
def roundup(x):
# https://stackoverflow.com/questions/50405017/
d = 10 ** 2
if x < 0:
return math.floor(x * d) / d
else:
return math.ceil(x * d) / d
def round2(num, decimals=2):
x = (pow(10, decimals))
return int(num * x) / x
def formatFileSize(size, asInt=False, roundUp=False, nobr=True):
def formatFileSize(size, asInt=False, nobr=True):
space = " " if nobr else " "
if size == 1:
return "{:d}{:s}Byte".format(size, space)
elif size < 1024:
return "{:d}{:s}Bytes".format(size, space)
elif size < 1024 * 1024:
val = size/1024
if roundUp: val = roundup(val)
val = size / 1024
val = round2(val)
if asInt:
return "{:d}{:s}KiB".format(int(val), space)
else:
return "{:.1f}{:s}KiB".format(val, space)
else:
val = size/1024/1024
if roundUp: val = roundup(val)
val = size / 1024 / 1024
val = round2(val)
if asInt:
return "{:d}{:s}MiB".format(int(val), space)
else:
@ -509,7 +504,7 @@ def GetDumpReport(di, device):
di["file_name"] = ""
else:
di["file_name"] = os.path.split(di["file_name"])[1]
di["file_size"] = "{:s} ({:d} bytes)".format(formatFileSize(di["file_size"]), di["file_size"])
di["file_size"] = "{:s} ({:d} bytes)".format(formatFileSize(size=di["file_size"]), di["file_size"])
s = "" \
"= FlashGBX Dump Report =\n" \
@ -659,7 +654,7 @@ def GetDumpReport(di, device):
timestamp=di["gbmem_parsed"][i]["timestamp"],
kiosk_id=di["gbmem_parsed"][i]["kiosk_id"],
location="0x{:06X}0x{:06X}".format(di["gbmem_parsed"][i]["rom_offset"], di["gbmem_parsed"][i]["rom_offset"]+di["gbmem_parsed"][i]["rom_size"]-1),
size="{:s} ({:d} bytes)".format(formatFileSize(di["gbmem_parsed"][i]["rom_size"]), di["gbmem_parsed"][i]["rom_size"]),
size="{:s} ({:d} bytes)".format(formatFileSize(size=di["gbmem_parsed"][i]["rom_size"]), di["gbmem_parsed"][i]["rom_size"]),
)
if "crc32" in di["gbmem_parsed"][i]: s += "* CRC32: {:08x}\n".format(di["gbmem_parsed"][i]["crc32"])
if "md5" in di["gbmem_parsed"][i]: s += "* MD5: {:s}\n".format(di["gbmem_parsed"][i]["md5"])
@ -702,7 +697,7 @@ def GetDumpReport(di, device):
if "rv" in db: s += "* Revision: {:s}\n".format(db["rv"])
if "gc" in db: s += "* Game Code: {:s}\n".format(db["gc"])
if "rc" in db: s += "* ROM CRC32: {:08x}\n".format(db["rc"])
if "rs" in db: s += "* ROM Size: {:s}\n".format(formatFileSize(db["rs"], asInt=True))
if "rs" in db: s += "* ROM Size: {:s}\n".format(formatFileSize(size=db["rs"], asInt=True))
if mode == "AGB":
header["game_code_raw"] = header["game_code_raw"].replace("\0", "")
@ -738,7 +733,10 @@ def GetDumpReport(di, device):
s += "" \
"* Save Flash Chip: {agb_save_flash_chip_name:s} (0x{agb_save_flash_chip_id:04X})\n" \
.format(agb_save_flash_chip_name=di["agb_save_flash_id"][1], agb_save_flash_chip_id=di["agb_save_flash_id"][0])
if "eeprom_data" in di:
s += "* EEPROM area: {:s}\n".format(''.join(format(x, '02X') for x in di["eeprom_data"]))
if header["db"] is not None and header["db"]["rc"] == di["hash_crc32"]:
db = header["db"]
s += "\n== Database Match ==\n"
@ -749,10 +747,10 @@ def GetDumpReport(di, device):
if "rv" in db: s += "* Revision: {:s}\n".format(db["rv"])
if "gc" in db: s += "* Game Code: {:s}\n".format(db["gc"])
if "rc" in db: s += "* ROM CRC32: {:08x}\n".format(db["rc"])
if "rs" in db: s += "* ROM Size: {:s}\n".format(formatFileSize(db["rs"], asInt=True))
if "rs" in db: s += "* ROM Size: {:s}\n".format(formatFileSize(size=db["rs"], asInt=True))
if "st" in db: s += "* Save Type: {:s}\n".format(AGB_Header_Save_Types[db["st"]])
#if "ss" in db: s += "* Save Size: {:s}\n".format(formatFileSize(db["ss"], asInt=True))
#if "ss" in db: s += "* Save Size: {:s}\n".format(formatFileSize(size=db["ss"], asInt=True))
return s
def GenerateFileName(mode, header, settings=None):

View File

@ -17310,7 +17310,7 @@
"gn": "Crash & Spyro Superpack - Spyro Orange - The Cortex Conspiracy + Crash Bandicoot Purple - Ripto's Rampage",
"ne": "(USA)",
"gc": "B53E",
"rc": 1497403502,
"rc": 2608249099,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -17321,7 +17321,7 @@
"gn": "Crash & Spyro Super Pack Volume 3",
"ne": "(Europe) (En,Fr,De,Es,It)",
"gc": "B53P",
"rc": 84981519,
"rc": 3341196906,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -18678,7 +18678,7 @@
"gn": "Shamu's Deep Sea Adventures",
"ne": "(USA)",
"gc": "BBAE",
"rc": 1318332139,
"rc": 2127263017,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -19107,7 +19107,7 @@
"gn": "Crayon Shin-chan - Densetsu o Yobu Omake no Miyako Shockgaan!",
"ne": "(Japan)",
"gc": "BC2J",
"rc": 1170752614,
"rc": 1391748094,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -21162,7 +21162,7 @@
"gn": "My Animal Centre in Africa",
"ne": "(Europe) (En,Fr,De,Es,It)",
"gc": "BFRP",
"rc": 381739048,
"rc": 3572943181,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -21869,7 +21869,7 @@
"gn": "Happy Feet",
"ne": "(USA) (En,Fr)",
"gc": "BH3E",
"rc": 1747326927,
"rc": 620782579,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -21880,7 +21880,7 @@
"gn": "Happy Feet",
"ne": "(Europe) (En,Fr,De,Es,It)",
"gc": "BH3P",
"rc": 2052265788,
"rc": 3093642841,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -23247,7 +23247,7 @@
"gn": "Harry Potter Collection",
"ne": "(Europe) (En,Fr,De,Es,It,Nl,Pt,Sv,No,Da)",
"gc": "BJPP",
"rc": 4289328379,
"rc": 1329185140,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -28788,7 +28788,7 @@
"gn": "Legend of Spyro, The - The Eternal Night",
"ne": "(USA) (En,Fr)",
"gc": "BU7E",
"rc": 3173470694,
"rc": 2373452324,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -28799,7 +28799,7 @@
"gn": "Legend of Spyro, The - The Eternal Night",
"ne": "(Europe) (En,Fr,De,Es,It,Nl)",
"gc": "BU7P",
"rc": 2477884243,
"rc": 2750337169,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -29804,7 +29804,7 @@
"gn": "2 in 1 Game Pack - Spider-Man + Spider-Man 2",
"ne": "(USA) (En,Fr,De+En,Fr,De,Es)",
"gc": "BX3E",
"rc": 182674303,
"rc": 985411773,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -29815,7 +29815,7 @@
"gn": "2 in 1 Game Pack - Spider-Man & Spider-Man 2",
"ne": "(Europe) (En,Fr,De+En,Fr,De,Es,It)",
"gc": "BX3P",
"rc": 3610436909,
"rc": 3882727151,
"rs": 33554432,
"st": 1,
"ss": 512,
@ -30355,7 +30355,7 @@
"gn": "Yggdra Union - We'll Never Fight Alone",
"ne": "(USA)",
"gc": "BYUE",
"rc": 1006266615,
"rc": 2226480748,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -30366,7 +30366,7 @@
"gn": "Yggdra Union",
"ne": "(Japan)",
"gc": "BYUJ",
"rc": 2825880650,
"rc": 3209929682,
"rs": 33554432,
"st": 2,
"ss": 8192,
@ -30377,7 +30377,7 @@
"gn": "Yggdra Union - We'll Never Fight Alone",
"ne": "(Europe)",
"gc": "BYUP",
"rc": 1041101104,
"rc": 691132072,
"rs": 33554432,
"st": 2,
"ss": 8192,

View File

@ -9,7 +9,8 @@
"AGB-E05-02 with M29W128FH",
"2006_TSOP_64BALL_6106 with W29GL128SH9B",
"AGB-E05-02 with JS28F128",
"AGB-E05-03H with M29W128GH"
"AGB-E05-03H with M29W128GH",
"BGA64B-71-TV-DEEP with 256M29EML"
],
"flash_ids":[
[ 0x02, 0x00, 0x7D, 0x22 ],
@ -20,7 +21,8 @@
[ 0x20, 0x00, 0x7D, 0x22 ],
[ 0xEF, 0x00, 0x7D, 0x22 ],
[ 0x8A, 0x00, 0x7D, 0x22 ],
[ 0x20, 0x00, 0x7D, 0x22 ]
[ 0x20, 0x00, 0x7D, 0x22 ],
[ 0x8A, 0x00, 0x7D, 0x22 ]
],
"voltage":3.3,
"flash_size":0x1000000,

View File

@ -110,7 +110,7 @@ try:
self.DEVICE = device
else:
self.APP.QT_APP.processEvents()
text = "This Firmware Updater is for insideGadgets GBxCart RW v1.4/v1.4a devices only. Please only proceed if your device matches this hardware revision.\n\nOlder GBxCart RW revisions can be updated only after connecting to them first."
text = "This Firmware Updater is for insideGadgets GBxCart RW v1.4 devices only. Please only proceed if your device matches this hardware revision.\n\nOlder GBxCart RW revisions can be updated only after connecting to them first."
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
answer = msgbox.exec()
@ -148,7 +148,7 @@ try:
self.lblDevicePCBVer.setMinimumWidth(120)
self.optDevicePCBVer14 = QtWidgets.QRadioButton("v1.4")
self.connect(self.optDevicePCBVer14, QtCore.SIGNAL("clicked()"), self.SetPCBVersion)
self.optDevicePCBVer14a = QtWidgets.QRadioButton("v1.4a")
self.optDevicePCBVer14a = QtWidgets.QRadioButton("v1.4a/b")
self.connect(self.optDevicePCBVer14a, QtCore.SIGNAL("clicked()"), self.SetPCBVersion)
rowDeviceInfo2.addWidget(self.lblDevicePCBVer)
rowDeviceInfo2.addWidget(self.optDevicePCBVer14)
@ -217,7 +217,7 @@ try:
self.lblDeviceFWVerResult.setText(self.FW_VER)
if self.PCB_VER == "v1.4":
self.optDevicePCBVer14.setChecked(True)
elif self.PCB_VER == "v1.4a":
elif self.PCB_VER == "v1.4a/b":
self.optDevicePCBVer14a.setChecked(True)
self.SetPCBVersion()
@ -275,7 +275,7 @@ try:
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip"
led = "Done"
elif self.optDevicePCBVer14a.isChecked():
device_name = "v1.4a"
device_name = "v1.4a/b"
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4a.zip"
led = "Status"
else:

View File

@ -92,7 +92,7 @@ class GbxDevice:
"AGB_READ_METHOD":[8, 0x0C],
}
PCB_VERSIONS = {4:'v1.3', 5:'v1.4', 6:'v1.4a', 101:'Mini v1.0d'}
PCB_VERSIONS = {4:'v1.3', 5:'v1.4', 6:'v1.4a/b', 101:'Mini v1.0d'}
ACTIONS = {"ROM_READ":1, "SAVE_READ":2, "SAVE_WRITE":3, "ROM_WRITE":4, "ROM_WRITE_VERIFY":4, "SAVE_WRITE_VERIFY":3}
SUPPORTED_CARTS = {}
@ -114,7 +114,8 @@ class GbxDevice:
FAST_READ = False
SKIPPING = False
BAUDRATE = 1000000
MAX_BUFFER_LEN = 0x800
MAX_BUFFER_READ = 0x2000
MAX_BUFFER_WRITE = 0x400
DEVICE_TIMEOUT = 0.75
WRITE_DELAY = False
READ_ERRORS = 0
@ -172,14 +173,16 @@ class GbxDevice:
elif self.FW["fw_ts"] > self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
conn_msg.append([0, "Note: The GBxCart RW device on port " + ports[i] + " is running a firmware version that is newer than what this version of FlashGBX was developed to work with, so errors may occur."])
if self.FW["pcb_ver"] not in (4, 5, 6, 101): # only the v1.3, v1.4, v1.4a, Mini v1.1 PCB revisions are supported
if self.FW["pcb_ver"] not in (4, 5, 6, 101): # only the v1.3, v1.4, v1.4a/b, Mini v1.1 PCB revisions are supported
dev.close()
self.DEVICE = None
continue
elif self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE > 1000000:
self.MAX_BUFFER_LEN = 0x2000
self.MAX_BUFFER_READ = 0x2000
self.MAX_BUFFER_WRITE = 0x400
else:
self.MAX_BUFFER_LEN = 0x1000
self.MAX_BUFFER_READ = 0x1000
self.MAX_BUFFER_WRITE = 0x100
conn_msg.append([0, "For help please visit the insideGadgets Discord: https://gbxcart.com/discord"])
@ -383,7 +386,7 @@ class GbxDevice:
return (None, fw_GBxCartRW_v1_3.FirmwareUpdaterWindow)
except:
return False
elif self.FW["pcb_ver"] in (5, 6): # v1.4 / v1.4a
elif self.FW["pcb_ver"] in (5, 6): # v1.4 / v1.4a/b
try:
from . import fw_GBxCartRW_v1_4
return (fw_GBxCartRW_v1_4.FirmwareUpdater, fw_GBxCartRW_v1_4.FirmwareUpdaterWindow)
@ -613,7 +616,7 @@ class GbxDevice:
self._write(self.DEVICE_CMD["OFW_CART_PWR_ON"])
time.sleep(delay)
self.DEVICE.reset_input_buffer() # bug workaround
def GetMode(self):
return self.MODE
@ -1017,7 +1020,7 @@ class GbxDevice:
addr_shift = batteryless_loader[base_addr - 3] << 1
address = (addr_value >> addr_rotate_right) | (addr_value << (32 - addr_rotate_right)) & 0xFFFFFFFF
address = (address << addr_shift)
if address < 32*1024*1024:
if address < 32*1024*1024 and address > 0x1000:
bl_offset = address
dprint("Detected Chinese bootleg Batteryless SRAM ROM")
else:
@ -1338,12 +1341,8 @@ class GbxDevice:
self.NO_PROG_UPDATE = npu
def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False):
def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False, max_length=MAX_BUFFER_WRITE):
length = len(buffer)
if self.FW["pcb_ver"] not in (5, 6, 101) or self.BAUDRATE == 1000000:
max_length = 256
else:
max_length = 1024
num = math.ceil(length / max_length)
dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
if length == 0:
@ -2153,7 +2152,7 @@ class GbxDevice:
dprint("Pullups disabled")
buffer = bytearray(size)
max_length = self.MAX_BUFFER_LEN
max_length = self.MAX_BUFFER_READ
dprint("Max buffer size: 0x{:X}".format(max_length))
if self.FAST_READ is True:
if is_3dmemory:
@ -2275,7 +2274,7 @@ class GbxDevice:
else:
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}. Decreasing maximum transfer buffer size to 0x{:X}.".format(buffer_len, pos_temp, max_length >> 1))
max_length >>= 1
self.MAX_BUFFER_LEN = max_length
self.MAX_BUFFER_READ = max_length
err_text += "\nBuffer size adjusted to {:d} bytes...".format(max_length)
if ".dev" in Util.VERSION_PEP440 and not Util.DEBUG: print(err_text)
@ -2322,8 +2321,6 @@ class GbxDevice:
bank += 1
if file is not None: file.close()
if "verify_write" in args:
return min(pos_total, len(args["verify_write"]))
@ -2353,23 +2350,16 @@ class GbxDevice:
if "gbmem_parsed" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem_parsed"])
# Calculate Global Checksum
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
self.INFO["file_sha256"] = hashlib.sha256(buffer).hexdigest()
self.INFO["file_md5"] = hashlib.md5(buffer).hexdigest()
self.INFO["dump_info"]["hash_crc32"] = self.INFO["file_crc32"]
self.INFO["dump_info"]["hash_sha1"] = self.INFO["file_sha1"]
self.INFO["dump_info"]["hash_sha256"] = self.INFO["file_sha256"]
self.INFO["dump_info"]["hash_md5"] = self.INFO["file_md5"]
if self.MODE == "DMG":
if _mbc.GetName() == "MMM01":
self.INFO["dump_info"]["header"] = RomFileDMG(buffer[-0x8000:-0x8000+0x180]).GetHeader(unchanged=True)
else:
self.INFO["dump_info"]["header"] = RomFileDMG(buffer[:0x180]).GetHeader()
chk = _mbc.CalcChecksum(buffer)
#chk = _mbc.CalcChecksum(buffer)
self.INFO["rom_checksum_calc"] = _mbc.CalcChecksum(buffer)
elif self.MODE == "AGB":
self.INFO["dump_info"]["header"] = RomFileAGB(buffer[:0x180]).GetHeader()
chk = self.INFO["file_crc32"]
#chk = self.INFO["file_crc32"]
temp_ver = "N/A"
ids = [ b"SRAM_", b"EEPROM_V", b"FLASH_V", b"FLASH512_V", b"FLASH1M_V", b"AGB_8MDACS_DL_V" ]
@ -2390,8 +2380,23 @@ class GbxDevice:
print("Error querying the flash save chip.")
self.DEVICE.reset_input_buffer()
self.DEVICE.reset_output_buffer()
self.INFO["rom_checksum_calc"] = chk
if "EEPROM" in temp_ver and len(buffer) == 0x2000000:
padding_byte = buffer[0x1FFFEFF]
dprint("Replacing unmapped ROM data of cartridge (32 MiB ROM + EEPROM save type) with the original padding byte of 0x{:02X}.".format(padding_byte))
self.INFO["dump_info"]["eeprom_data"] = buffer[0x1FFFF00:0x1FFFF10]
buffer[0x1FFFF00:0x2000000] = bytearray([padding_byte] * 0x100)
file.seek(0x1FFFF00)
file.write(buffer[0x1FFFF00:0x2000000])
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
self.INFO["file_sha256"] = hashlib.sha256(buffer).hexdigest()
self.INFO["file_md5"] = hashlib.md5(buffer).hexdigest()
self.INFO["dump_info"]["hash_crc32"] = self.INFO["file_crc32"]
self.INFO["dump_info"]["hash_sha1"] = self.INFO["file_sha1"]
self.INFO["dump_info"]["hash_sha256"] = self.INFO["file_sha256"]
self.INFO["dump_info"]["hash_md5"] = self.INFO["file_md5"]
# Check for ROM loops
self.INFO["loop_detected"] = False
@ -2404,6 +2409,8 @@ class GbxDevice:
else:
break
if file is not None: file.close()
# ↓↓↓ Switch to first ROM bank
if self.MODE == "DMG":
if _mbc.ResetBeforeBankChange(0) is True:
@ -2959,6 +2966,20 @@ class GbxDevice:
data_import += bytearray([0xFF] * (0x400 - len(data_import)))
if len(data_import) % 0x8000 > 0:
data_import += bytearray([0xFF] * (0x8000 - len(data_import) % 0x8000))
# Skip writing the last 256 bytes of 32 MiB ROMs with EEPROM save type
if self.MODE == "AGB" and len(data_import) == 0x2000000:
temp_ver = "N/A"
ids = [ b"SRAM_", b"EEPROM_V", b"FLASH_V", b"FLASH512_V", b"FLASH1M_V", b"AGB_8MDACS_DL_V" ]
for id in ids:
temp_pos = data_import.find(id)
if temp_pos > 0:
temp_ver = data_import[temp_pos:temp_pos+0x20]
temp_ver = temp_ver[:temp_ver.index(0x00)].decode("ascii", "replace")
break
if "EEPROM" in temp_ver:
print("Note: The last 256 bytes of this 32 MiB ROM will not be written as this area is reserved by the EEPROM save type.")
data_import = data_import[:0x1FFFF00]
# Fix header
if "fix_header" in args and args["fix_header"]:
@ -3096,7 +3117,7 @@ class GbxDevice:
else:
errmsg_mbc_selection += "\n- Check mapper type used: {:s} (forced by selected cartridge type)".format(_mbc.GetName())
if len(data_import) > _mbc.GetMaxROMSize():
errmsg_mbc_selection += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(_mbc.GetMaxROMSize()))
errmsg_mbc_selection += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(size=_mbc.GetMaxROMSize()))
elif self.MODE == "AGB":
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
@ -3303,7 +3324,7 @@ class GbxDevice:
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"]):
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})
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(size=flash_capacity, asInt=False)), "abortable":False})
return False
sector_offsets_hash = base64.urlsafe_b64encode(hashlib.sha1(str(sector_offsets).encode("UTF-8")).digest()).decode("ASCII", "ignore")[:4]
@ -3533,7 +3554,12 @@ class GbxDevice:
elif command_set_type == "DATEL_ORBITV2":
status = self.WriteROM_DMG_DatelOrbitV2(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
else:
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble)
max_buffer_write = self.MAX_BUFFER_WRITE
if (len(data_import) == 0x1FFFF00) and (buffer_pos+buffer_len > len(data_import)):
# 32 MiB ROM + EEPROM cart
max_buffer_write = 256
buffer_len = (buffer_pos+buffer_len - len(data_import))
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble, max_length=max_buffer_write)
if status is False or se_ret is False:
self.CANCEL = True
@ -3553,7 +3579,7 @@ class GbxDevice:
else:
retry_hp -= 10
if retry_hp <= 0:
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(len(data_import), asInt=False), errmsg_mbc_selection), "abortable":False})
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(size=len(data_import), asInt=False), errmsg_mbc_selection), "abortable":False})
continue
rev_buffer_pos = sector_offsets[sector_pos - 1][0]
@ -3600,13 +3626,6 @@ class GbxDevice:
if status is not False:
bank += 1
if delta_state_new is not None and not chip_erase:
try:
with open(json_file, "wb") as f:
f.write(json.dumps(delta_state_new).encode("UTF-8-SIG"))
except PermissionError:
print("Error: Couldnt update write-protected file “{:s}".format(json_file))
self.SetProgress({"action":"UPDATE_POS", "pos":len(data_import)})
# ↑↑↑ Flash write
@ -3677,6 +3696,13 @@ class GbxDevice:
verified = False
# ↑↑↑ Flash verify
if delta_state_new is not None and not chip_erase and not "broken_sectors" in self.INFO:
try:
with open(json_file, "wb") as f:
f.write(json.dumps(delta_state_new).encode("UTF-8-SIG"))
except PermissionError:
print("Error: Couldnt update write-protected file “{:s}".format(json_file))
# ↓↓↓ Switch to first ROM bank
if self.MODE == "DMG":
if _mbc.ResetBeforeBankChange(0) is True:

Binary file not shown.

View File

@ -256,6 +256,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- B104 with MSP55LV128
- B11 with 26L6420MC-90
- B54 with MX29LV320ET
- BGA64B-71-TV-DEEP with 256M29EML
- BX2006_0106_NEW with S29GL128N10TFI01
- BX2006_TSOP_64BALL with GL128S
- BX2006_TSOP_64BALL with GL256S
@ -307,7 +308,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, 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, Zoo, 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, Leitplanke, 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, Zoo, zvxr
## DISCLAIMER

View File

@ -8,7 +8,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
setuptools.setup(
name="FlashGBX",
version="3.30",
version="3.31",
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",