mirror of
https://github.com/lesserkuma/FlashGBX.git
synced 2026-03-21 17:44:30 -05:00
3.31
This commit is contained in:
parent
007085b24c
commit
2951fdc7ce
|
|
@ -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)*
|
||||
|
|
|
|||
|
|
@ -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 doesn’t 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 doesn’t 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}Couldn’t 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}Couldn’t 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 it’s possible that it’s 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)
|
||||
|
|
|
|||
|
|
@ -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 doesn’t 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 doesn’t 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("Don’t 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 it’s 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 it’s 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 it’s possible that it’s 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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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: Couldn’t 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: Couldn’t 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.
|
|
@ -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
|
||||
|
||||
|
|
|
|||
2
setup.py
2
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.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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user