mirror of
https://github.com/lesserkuma/FlashGBX.git
synced 2026-04-26 02:23:22 -05:00
3.25
This commit is contained in:
parent
305e343290
commit
c475e722c4
10
CHANGES.md
10
CHANGES.md
|
|
@ -1,4 +1,14 @@
|
|||
# Release notes
|
||||
### v3.25 (released 2023-04-14)
|
||||
- Bundles GBxCart RW v1.4/v1.4a firmware version R41+L9 (minor improvements)
|
||||
- Fixed an issue with the Game Boy Camera Album Viewer *(thanks CodyWick13)*
|
||||
- Switched from PyInstaller to providing Windows Setup/Portable packages with embedded Python runtimes, so FlashGBX can be run from source as well (requires 64-bit Windows installation)
|
||||
- Added support for DRV with AM29LV160DT and ALTERA CPLD *(thanks Corborg)*
|
||||
- Verification after writing a ROM is now a lot faster (requires firmware R41+L9)
|
||||
- Now shows the No-Intro game title in the main window if the cartridge is in the database
|
||||
- Cleaned up the list of Game Boy mappers in the GUI to only list the unique types; detailed info is still available in dump reports
|
||||
- Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums
|
||||
|
||||
### v3.24 (released 2023-04-09)
|
||||
- Improved support for Batteryless SRAM save data backup and restore for Game Boy Advance reproduction cartridges *(thanks metroid-maniac and LucentW)*
|
||||
- When checking for available updates, version information is now read via the GitHub API instead of the PyPI API
|
||||
|
|
|
|||
|
|
@ -157,8 +157,28 @@ def main(portableMode=False):
|
|||
Util.DEBUG = True
|
||||
|
||||
args = {"app_path":app_path, "config_path":config_path, "argparsed":args}
|
||||
while True:
|
||||
try:
|
||||
if not os.path.exists(config_path):
|
||||
os.mkdir(config_path)
|
||||
tf = "{:s}/settings.ini".format(config_path)
|
||||
f = open(tf, "ab")
|
||||
f.close()
|
||||
break
|
||||
except PermissionError:
|
||||
print("\n{:s}Error: This program has no permission to use the configuration directory “{:s}”!{:s}".format(Util.ANSI.RED, config_path, Util.ANSI.RESET))
|
||||
if "appdata" in cp and args["argparsed"].cfgdir == "subdir":
|
||||
answer = input("Use directory “{:s}” instead? [y/N] ".format(cp["appdata"])).strip().lower()
|
||||
if answer != "y":
|
||||
return
|
||||
config_path = cp["appdata"]
|
||||
args["config_path"] = config_path
|
||||
continue
|
||||
else:
|
||||
input("")
|
||||
return
|
||||
args.update(LoadConfig(args))
|
||||
|
||||
|
||||
app = None
|
||||
exc = None
|
||||
if not args["argparsed"].cli:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# FlashGBX
|
||||
# Author: Lesserkuma (github.com/lesserkuma)
|
||||
|
||||
import datetime, shutil, platform, os, json, math, traceback, re, time, serial, zipfile
|
||||
import datetime, shutil, platform, os, math, traceback, re, time, serial, zipfile
|
||||
try:
|
||||
# pylint: disable=import-error
|
||||
import readline
|
||||
|
|
@ -1177,11 +1177,6 @@ class FlashGBX_CLI():
|
|||
except:
|
||||
pass
|
||||
|
||||
os.unlink(Util.CONFIG_PATH + "/test1.bin")
|
||||
os.unlink(Util.CONFIG_PATH + "/test2.bin")
|
||||
os.unlink(Util.CONFIG_PATH + "/test3.bin")
|
||||
os.unlink(Util.CONFIG_PATH + "/test4.bin")
|
||||
|
||||
def UpdateFirmwareGBxCartRW_PrintText(self, text, enableUI=False, setProgress=None):
|
||||
if setProgress is not None:
|
||||
self.FWUPD_R = True
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
rowActionsGeneral2.addWidget(self.btnBackupRAM)
|
||||
|
||||
self.cmbDMGCartridgeTypeResult.currentIndexChanged.connect(self.CartridgeTypeChanged)
|
||||
self.cmbHeaderMapperResult.currentIndexChanged.connect(self.DMGMapperTypeChanged)
|
||||
self.cmbDMGHeaderMapperResult.currentIndexChanged.connect(self.DMGMapperTypeChanged)
|
||||
|
||||
rowActionsGeneral3 = QtWidgets.QHBoxLayout()
|
||||
self.btnFlashROM = QtWidgets.QPushButton("&Write ROM")
|
||||
|
|
@ -274,96 +274,96 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
def GuiCreateGroupBoxDMGCartInfo(self):
|
||||
self.grpDMGCartridgeInfo = QtWidgets.QGroupBox("Game Boy Cartridge Information")
|
||||
self.grpDMGCartridgeInfo.setMinimumWidth(364)
|
||||
self.grpDMGCartridgeInfo.setMinimumWidth(375)
|
||||
group_layout = QtWidgets.QVBoxLayout()
|
||||
group_layout.setContentsMargins(-1, 5, -1, -1)
|
||||
|
||||
rowHeaderTitle = QtWidgets.QHBoxLayout()
|
||||
lblHeaderTitle = QtWidgets.QLabel("Game Title:")
|
||||
lblHeaderTitle.setContentsMargins(0, 1, 0, 1)
|
||||
rowHeaderTitle.addWidget(lblHeaderTitle)
|
||||
self.lblHeaderTitleResult = QtWidgets.QLabel("")
|
||||
rowHeaderTitle.addWidget(self.lblHeaderTitleResult)
|
||||
group_layout.addLayout(rowHeaderTitle)
|
||||
rowDMGGameName = QtWidgets.QHBoxLayout()
|
||||
self.lblDMGGameName = QtWidgets.QLabel("Game Name:")
|
||||
self.lblDMGGameName.setContentsMargins(0, 1, 0, 1)
|
||||
rowDMGGameName.addWidget(self.lblDMGGameName)
|
||||
self.lblDMGGameNameResult = QtWidgets.QLabel("")
|
||||
rowDMGGameName.addWidget(self.lblDMGGameNameResult)
|
||||
group_layout.addLayout(rowDMGGameName)
|
||||
|
||||
rowHeaderRevision = QtWidgets.QHBoxLayout()
|
||||
self.lblHeaderRevision = QtWidgets.QLabel("Game Code / Revision:")
|
||||
self.lblHeaderRevision.setContentsMargins(0, 1, 0, 1)
|
||||
rowHeaderRevision.addWidget(self.lblHeaderRevision)
|
||||
self.lblHeaderRevisionResult = QtWidgets.QLabel("")
|
||||
rowHeaderRevision.addWidget(self.lblHeaderRevisionResult)
|
||||
group_layout.addLayout(rowHeaderRevision)
|
||||
rowDMGRomTitle = QtWidgets.QHBoxLayout()
|
||||
self.lblDMGRomTitle = QtWidgets.QLabel("ROM Title:")
|
||||
self.lblDMGRomTitle.setContentsMargins(0, 1, 0, 1)
|
||||
rowDMGRomTitle.addWidget(self.lblDMGRomTitle)
|
||||
self.lblDMGRomTitleResult = QtWidgets.QLabel("")
|
||||
rowDMGRomTitle.addWidget(self.lblDMGRomTitleResult)
|
||||
group_layout.addLayout(rowDMGRomTitle)
|
||||
|
||||
rowHeaderRtc = QtWidgets.QHBoxLayout()
|
||||
lblHeaderRtc = QtWidgets.QLabel("Real Time Clock:")
|
||||
lblHeaderRtc.setContentsMargins(0, 1, 0, 1)
|
||||
rowHeaderRtc.addWidget(lblHeaderRtc)
|
||||
self.lblHeaderRtcResult = QtWidgets.QLabel("")
|
||||
self.lblHeaderRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
|
||||
self.lblHeaderRtcResult.setToolTip("This shows the internal register values; in-game clock may use an offset")
|
||||
rowHeaderRtc.addWidget(self.lblHeaderRtcResult)
|
||||
group_layout.addLayout(rowHeaderRtc)
|
||||
rowDMGGameCodeRevision = QtWidgets.QHBoxLayout()
|
||||
self.lblDMGGameCodeRevision = QtWidgets.QLabel("Game Code and Revision:")
|
||||
self.lblDMGGameCodeRevision.setContentsMargins(0, 1, 0, 1)
|
||||
rowDMGGameCodeRevision.addWidget(self.lblDMGGameCodeRevision)
|
||||
self.lblDMGGameCodeRevisionResult = QtWidgets.QLabel("")
|
||||
rowDMGGameCodeRevision.addWidget(self.lblDMGGameCodeRevisionResult)
|
||||
group_layout.addLayout(rowDMGGameCodeRevision)
|
||||
|
||||
rowHeaderLogoValid = QtWidgets.QHBoxLayout()
|
||||
lblHeaderLogoValid = QtWidgets.QLabel("Boot Logo:")
|
||||
lblHeaderLogoValid.setContentsMargins(0, 1, 0, 1)
|
||||
rowHeaderLogoValid.addWidget(lblHeaderLogoValid)
|
||||
self.lblHeaderLogoValidResult = QtWidgets.QLabel("")
|
||||
rowHeaderLogoValid.addWidget(self.lblHeaderLogoValidResult)
|
||||
group_layout.addLayout(rowHeaderLogoValid)
|
||||
rowDMGHeaderRtc = QtWidgets.QHBoxLayout()
|
||||
lblDMGHeaderRtc = QtWidgets.QLabel("Real Time Clock:")
|
||||
lblDMGHeaderRtc.setContentsMargins(0, 1, 0, 1)
|
||||
rowDMGHeaderRtc.addWidget(lblDMGHeaderRtc)
|
||||
self.lblDMGHeaderRtcResult = QtWidgets.QLabel("")
|
||||
self.lblDMGHeaderRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
|
||||
self.lblDMGHeaderRtcResult.setToolTip("This shows the internal register values; in-game clock may use an offset")
|
||||
rowDMGHeaderRtc.addWidget(self.lblDMGHeaderRtcResult)
|
||||
group_layout.addLayout(rowDMGHeaderRtc)
|
||||
|
||||
rowHeaderChecksum = QtWidgets.QHBoxLayout()
|
||||
lblHeaderChecksum = QtWidgets.QLabel("Header Checksum:")
|
||||
lblHeaderChecksum.setContentsMargins(0, 1, 0, 1)
|
||||
rowHeaderChecksum.addWidget(lblHeaderChecksum)
|
||||
self.lblHeaderChecksumResult = QtWidgets.QLabel("")
|
||||
rowHeaderChecksum.addWidget(self.lblHeaderChecksumResult)
|
||||
group_layout.addLayout(rowHeaderChecksum)
|
||||
rowDMGHeaderBootlogo = QtWidgets.QHBoxLayout()
|
||||
lblDMGHeaderBootlogo = QtWidgets.QLabel("Boot Logo:")
|
||||
lblDMGHeaderBootlogo.setContentsMargins(0, 1, 0, 1)
|
||||
rowDMGHeaderBootlogo.addWidget(lblDMGHeaderBootlogo)
|
||||
self.lblDMGHeaderBootlogoResult = QtWidgets.QLabel("")
|
||||
rowDMGHeaderBootlogo.addWidget(self.lblDMGHeaderBootlogoResult)
|
||||
group_layout.addLayout(rowDMGHeaderBootlogo)
|
||||
|
||||
rowHeaderROMChecksum = QtWidgets.QHBoxLayout()
|
||||
lblHeaderROMChecksum = QtWidgets.QLabel("ROM Checksum:")
|
||||
lblHeaderROMChecksum.setContentsMargins(0, 1, 0, 1)
|
||||
rowHeaderROMChecksum.addWidget(lblHeaderROMChecksum)
|
||||
self.lblHeaderROMChecksumResult = QtWidgets.QLabel("")
|
||||
rowHeaderROMChecksum.addWidget(self.lblHeaderROMChecksumResult)
|
||||
group_layout.addLayout(rowHeaderROMChecksum)
|
||||
rowDMGHeaderROMChecksum = QtWidgets.QHBoxLayout()
|
||||
lblDMGHeaderROMChecksum = QtWidgets.QLabel("ROM Checksum:")
|
||||
lblDMGHeaderROMChecksum.setContentsMargins(0, 1, 0, 1)
|
||||
rowDMGHeaderROMChecksum.addWidget(lblDMGHeaderROMChecksum)
|
||||
self.lblDMGHeaderROMChecksumResult = QtWidgets.QLabel("")
|
||||
rowDMGHeaderROMChecksum.addWidget(self.lblDMGHeaderROMChecksumResult)
|
||||
group_layout.addLayout(rowDMGHeaderROMChecksum)
|
||||
|
||||
rowHeaderROMSize = QtWidgets.QHBoxLayout()
|
||||
lblHeaderROMSize = QtWidgets.QLabel("ROM Size:")
|
||||
rowHeaderROMSize.addWidget(lblHeaderROMSize)
|
||||
self.cmbHeaderROMSizeResult = QtWidgets.QComboBox()
|
||||
self.cmbHeaderROMSizeResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbHeaderROMSizeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowHeaderROMSize.addWidget(self.cmbHeaderROMSizeResult)
|
||||
group_layout.addLayout(rowHeaderROMSize)
|
||||
rowDMGHeaderROMSize = QtWidgets.QHBoxLayout()
|
||||
lblDMGHeaderROMSize = QtWidgets.QLabel("ROM Size:")
|
||||
rowDMGHeaderROMSize.addWidget(lblDMGHeaderROMSize)
|
||||
self.cmbDMGHeaderROMSizeResult = QtWidgets.QComboBox()
|
||||
self.cmbDMGHeaderROMSizeResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbDMGHeaderROMSizeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowDMGHeaderROMSize.addWidget(self.cmbDMGHeaderROMSizeResult)
|
||||
group_layout.addLayout(rowDMGHeaderROMSize)
|
||||
|
||||
rowHeaderRAMSize = QtWidgets.QHBoxLayout()
|
||||
lblHeaderRAMSize = QtWidgets.QLabel("Save Type:")
|
||||
rowHeaderRAMSize.addWidget(lblHeaderRAMSize)
|
||||
self.cmbHeaderRAMSizeResult = QtWidgets.QComboBox()
|
||||
self.cmbHeaderRAMSizeResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbHeaderRAMSizeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowHeaderRAMSize.addWidget(self.cmbHeaderRAMSizeResult)
|
||||
group_layout.addLayout(rowHeaderRAMSize)
|
||||
rowDMGHeaderSaveType = QtWidgets.QHBoxLayout()
|
||||
lblDMGHeaderSaveType = QtWidgets.QLabel("Save Type:")
|
||||
rowDMGHeaderSaveType.addWidget(lblDMGHeaderSaveType)
|
||||
self.cmbDMGHeaderSaveTypeResult = QtWidgets.QComboBox()
|
||||
self.cmbDMGHeaderSaveTypeResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbDMGHeaderSaveTypeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowDMGHeaderSaveType.addWidget(self.cmbDMGHeaderSaveTypeResult)
|
||||
group_layout.addLayout(rowDMGHeaderSaveType)
|
||||
|
||||
rowHeaderMapper = QtWidgets.QHBoxLayout()
|
||||
lblHeaderMapper = QtWidgets.QLabel("Mapper Type:")
|
||||
rowHeaderMapper.addWidget(lblHeaderMapper)
|
||||
self.cmbHeaderMapperResult = QtWidgets.QComboBox()
|
||||
self.cmbHeaderMapperResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbHeaderMapperResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowHeaderMapper.addWidget(self.cmbHeaderMapperResult)
|
||||
group_layout.addLayout(rowHeaderMapper)
|
||||
rowDMGHeaderMapper = QtWidgets.QHBoxLayout()
|
||||
lblDMGHeaderMapper = QtWidgets.QLabel("Mapper Type:")
|
||||
rowDMGHeaderMapper.addWidget(lblDMGHeaderMapper)
|
||||
self.cmbDMGHeaderMapperResult = QtWidgets.QComboBox()
|
||||
self.cmbDMGHeaderMapperResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbDMGHeaderMapperResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowDMGHeaderMapper.addWidget(self.cmbDMGHeaderMapperResult)
|
||||
group_layout.addLayout(rowDMGHeaderMapper)
|
||||
|
||||
rowCartridgeType = QtWidgets.QHBoxLayout()
|
||||
lblCartridgeType = QtWidgets.QLabel("Cart:")
|
||||
rowCartridgeType.addWidget(lblCartridgeType)
|
||||
rowDMGCartridgeType = QtWidgets.QHBoxLayout()
|
||||
lblDMGCartridgeType = QtWidgets.QLabel("Cart:")
|
||||
rowDMGCartridgeType.addWidget(lblDMGCartridgeType)
|
||||
self.cmbDMGCartridgeTypeResult = QtWidgets.QComboBox()
|
||||
self.cmbDMGCartridgeTypeResult.setStyleSheet("max-width: 260px;")
|
||||
self.cmbDMGCartridgeTypeResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbDMGCartridgeTypeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
rowCartridgeType.addWidget(self.cmbDMGCartridgeTypeResult)
|
||||
group_layout.addLayout(rowCartridgeType)
|
||||
rowDMGCartridgeType.addWidget(self.cmbDMGCartridgeTypeResult)
|
||||
group_layout.addLayout(rowDMGCartridgeType)
|
||||
|
||||
self.grpDMGCartridgeInfo.setLayout(group_layout)
|
||||
|
||||
|
|
@ -371,33 +371,33 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
def GuiCreateGroupBoxAGBCartInfo(self):
|
||||
self.grpAGBCartridgeInfo = QtWidgets.QGroupBox("Game Boy Advance Cartridge Information")
|
||||
self.grpAGBCartridgeInfo.setMinimumWidth(364)
|
||||
self.grpAGBCartridgeInfo.setMinimumWidth(375)
|
||||
group_layout = QtWidgets.QVBoxLayout()
|
||||
group_layout.setContentsMargins(-1, 5, -1, -1)
|
||||
|
||||
rowAGBHeaderTitle = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderTitle = QtWidgets.QLabel("Game Title:")
|
||||
lblAGBHeaderTitle.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBHeaderTitle.addWidget(lblAGBHeaderTitle)
|
||||
self.lblAGBHeaderTitleResult = QtWidgets.QLabel("")
|
||||
rowAGBHeaderTitle.addWidget(self.lblAGBHeaderTitleResult)
|
||||
group_layout.addLayout(rowAGBHeaderTitle)
|
||||
rowAGBGameName = QtWidgets.QHBoxLayout()
|
||||
lblAGBGameName = QtWidgets.QLabel("Game Name:")
|
||||
lblAGBGameName.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBGameName.addWidget(lblAGBGameName)
|
||||
self.lblAGBGameNameResult = QtWidgets.QLabel("")
|
||||
rowAGBGameName.addWidget(self.lblAGBGameNameResult)
|
||||
group_layout.addLayout(rowAGBGameName)
|
||||
|
||||
rowAGBHeaderCode = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderCode = QtWidgets.QLabel("Game Code:")
|
||||
lblAGBHeaderCode.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBHeaderCode.addWidget(lblAGBHeaderCode)
|
||||
self.lblAGBHeaderCodeResult = QtWidgets.QLabel("")
|
||||
rowAGBHeaderCode.addWidget(self.lblAGBHeaderCodeResult)
|
||||
group_layout.addLayout(rowAGBHeaderCode)
|
||||
rowAGBRomTitle = QtWidgets.QHBoxLayout()
|
||||
lblAGBRomTitle = QtWidgets.QLabel("ROM Title:")
|
||||
lblAGBRomTitle.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBRomTitle.addWidget(lblAGBRomTitle)
|
||||
self.lblAGBRomTitleResult = QtWidgets.QLabel("")
|
||||
rowAGBRomTitle.addWidget(self.lblAGBRomTitleResult)
|
||||
group_layout.addLayout(rowAGBRomTitle)
|
||||
|
||||
rowAGBHeaderRevision = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderRevision = QtWidgets.QLabel("Revision:")
|
||||
lblAGBHeaderRevision.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBHeaderRevision.addWidget(lblAGBHeaderRevision)
|
||||
self.lblAGBHeaderRevisionResult = QtWidgets.QLabel("")
|
||||
rowAGBHeaderRevision.addWidget(self.lblAGBHeaderRevisionResult)
|
||||
group_layout.addLayout(rowAGBHeaderRevision)
|
||||
rowAGBHeaderGameCodeRevision = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderGameCodeRevision = QtWidgets.QLabel("Game Code and Revision:")
|
||||
lblAGBHeaderGameCodeRevision.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBHeaderGameCodeRevision.addWidget(lblAGBHeaderGameCodeRevision)
|
||||
self.lblAGBHeaderGameCodeRevisionResult = QtWidgets.QLabel("")
|
||||
rowAGBHeaderGameCodeRevision.addWidget(self.lblAGBHeaderGameCodeRevisionResult)
|
||||
group_layout.addLayout(rowAGBHeaderGameCodeRevision)
|
||||
|
||||
rowAGBGpioRtc = QtWidgets.QHBoxLayout()
|
||||
lblAGBGpioRtc = QtWidgets.QLabel("Real Time Clock:")
|
||||
|
|
@ -405,17 +405,17 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
rowAGBGpioRtc.addWidget(lblAGBGpioRtc)
|
||||
self.lblAGBGpioRtcResult = QtWidgets.QLabel("")
|
||||
self.lblAGBGpioRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
|
||||
self.lblAGBGpioRtcResult.setToolTip(self.lblHeaderRtcResult.toolTip())
|
||||
self.lblAGBGpioRtcResult.setToolTip(self.lblDMGHeaderRtcResult.toolTip())
|
||||
rowAGBGpioRtc.addWidget(self.lblAGBGpioRtcResult)
|
||||
group_layout.addLayout(rowAGBGpioRtc)
|
||||
|
||||
rowAGBHeaderLogoValid = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderLogoValid = QtWidgets.QLabel("Boot Logo:")
|
||||
lblAGBHeaderLogoValid.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBHeaderLogoValid.addWidget(lblAGBHeaderLogoValid)
|
||||
rowAGBHeaderBootlogo = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderBootlogo = QtWidgets.QLabel("Boot Logo:")
|
||||
lblAGBHeaderBootlogo.setContentsMargins(0, 1, 0, 1)
|
||||
rowAGBHeaderBootlogo.addWidget(lblAGBHeaderBootlogo)
|
||||
self.lblAGBHeaderLogoValidResult = QtWidgets.QLabel("")
|
||||
rowAGBHeaderLogoValid.addWidget(self.lblAGBHeaderLogoValidResult)
|
||||
group_layout.addLayout(rowAGBHeaderLogoValid)
|
||||
rowAGBHeaderBootlogo.addWidget(self.lblAGBHeaderLogoValidResult)
|
||||
group_layout.addLayout(rowAGBHeaderBootlogo)
|
||||
|
||||
rowAGBHeaderChecksum = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderChecksum = QtWidgets.QLabel("Header Checksum:")
|
||||
|
|
@ -444,16 +444,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
rowAGBHeaderROMSize.addWidget(self.cmbAGBHeaderROMSizeResult)
|
||||
group_layout.addLayout(rowAGBHeaderROMSize)
|
||||
|
||||
rowAGBHeaderRAMSize = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderRAMSize = QtWidgets.QLabel("Save Type:")
|
||||
rowAGBHeaderRAMSize.addWidget(lblAGBHeaderRAMSize)
|
||||
rowAGBHeaderSaveType = QtWidgets.QHBoxLayout()
|
||||
lblAGBHeaderSaveType = QtWidgets.QLabel("Save Type:")
|
||||
rowAGBHeaderSaveType.addWidget(lblAGBHeaderSaveType)
|
||||
self.cmbAGBSaveTypeResult = QtWidgets.QComboBox()
|
||||
self.cmbAGBSaveTypeResult.setStyleSheet("combobox-popup: 0;")
|
||||
self.cmbAGBSaveTypeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
self.cmbAGBSaveTypeResult.addItems(Util.AGB_Header_Save_Types)
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(self.cmbAGBSaveTypeResult.count() - 1)
|
||||
rowAGBHeaderRAMSize.addWidget(self.cmbAGBSaveTypeResult)
|
||||
group_layout.addLayout(rowAGBHeaderRAMSize)
|
||||
rowAGBHeaderSaveType.addWidget(self.cmbAGBSaveTypeResult)
|
||||
group_layout.addLayout(rowAGBHeaderSaveType)
|
||||
|
||||
rowAGBCartridgeType = QtWidgets.QHBoxLayout()
|
||||
lblAGBCartridgeType = QtWidgets.QLabel("Cart:")
|
||||
|
|
@ -487,7 +487,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
return
|
||||
new_value = str(self.mnuConfig.actions()[0].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")
|
||||
if new_value == "enabled":
|
||||
answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), "Would you like to automatically check for new versions at application startup? This will make use of the GitHub API (<a href=\"https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement\">privacy policy</a>).", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
|
||||
answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), "Would you like to automatically check for new versions at application startup? This will make use of the GitHub API (<a href=\"https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement\">privacy policy</a>).", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
|
||||
if answer == QtWidgets.QMessageBox.Yes:
|
||||
self.SETTINGS.setValue("UpdateCheck", "enabled")
|
||||
self.mnuConfig.actions()[0].setChecked(True)
|
||||
|
|
@ -544,7 +544,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if msgbox.clickedButton() == button_open:
|
||||
webbrowser.open(site)
|
||||
else:
|
||||
print("This version of {:s} ({:s}) seems to be newer than the latest release ({:s}). Please check for updates manually.".format(APPNAME, VERSION_PEP440, latest_version))
|
||||
print("This version of {:s} ({:s}) seems to be newer than the latest public release ({:s}).".format(APPNAME, VERSION_PEP440, latest_version))
|
||||
else:
|
||||
print("Error: Update check failed due to missing version information in JSON data from GitHub.")
|
||||
except json.decoder.JSONDecodeError:
|
||||
|
|
@ -602,7 +602,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.SETTINGS.setValue("SkipCameraSavePopup", "disabled")
|
||||
|
||||
def OpenPath(self, path=None):
|
||||
if path is None: path = Util.CONFIG_PATH
|
||||
if path is None:
|
||||
path = Util.CONFIG_PATH
|
||||
self.WriteDebugLogOnShiftKey()
|
||||
path = 'file://{0:s}'.format(path)
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
|
|
@ -613,7 +615,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
subprocess.Popen(["xdg-open", path])
|
||||
except:
|
||||
QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "The path is:\n{:s}".format(path), QtWidgets.QMessageBox.Ok)
|
||||
self.WriteDebugLogOnShiftKey()
|
||||
|
||||
def WriteDebugLogOnShiftKey(self, event=None):
|
||||
kbmod = QtWidgets.QApplication.keyboardModifiers()
|
||||
|
|
@ -909,10 +910,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
else:
|
||||
button_dump_report = msgbox.addButton(" Generate Dump &Report ", QtWidgets.QMessageBox.ActionRole)
|
||||
|
||||
button_open_dir = msgbox.addButton(" Open &Directory ", QtWidgets.QMessageBox.ActionRole)
|
||||
|
||||
if self.CONN.GetMode() == "DMG":
|
||||
if self.CONN.INFO["rom_checksum"] == self.CONN.INFO["rom_checksum_calc"]:
|
||||
self.lblHeaderROMChecksumResult.setText("Valid (0x{:04X})".format(self.CONN.INFO["rom_checksum"]))
|
||||
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
|
||||
self.lblDMGHeaderROMChecksumResult.setText("Valid (0x{:04X})".format(self.CONN.INFO["rom_checksum"]))
|
||||
self.lblDMGHeaderROMChecksumResult.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)
|
||||
|
|
@ -924,15 +927,15 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
msgbox.setText(msg + msg_te)
|
||||
msgbox.exec()
|
||||
else:
|
||||
self.lblHeaderROMChecksumResult.setText("Invalid (0x{:04X}≠0x{:04X})".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["rom_checksum"]))
|
||||
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||
self.lblDMGHeaderROMChecksumResult.setText("Invalid (0x{:04X}≠0x{:04X})".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["rom_checksum"]))
|
||||
self.lblDMGHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||
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))
|
||||
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 (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderMapperResult.currentIndex()])[0] in (1, 2, 3): # If MBC1
|
||||
if self.CONN.GetMode() == "DMG" and self.cmbDMGHeaderMapperResult.currentText() == "MBC1":
|
||||
msg += "\n\nIf this is a NP GB-Memory Cartridge, please use the “Retry as G-MMC1” button."
|
||||
button_gmmc1 = msgbox.addButton(" Retry as G-MMC1 ", QtWidgets.QMessageBox.ActionRole)
|
||||
msgbox.setText(msg + msg_te)
|
||||
|
|
@ -940,10 +943,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
msgbox.exec()
|
||||
if msgbox.clickedButton() == button_gmmc1:
|
||||
if self.CheckDeviceAlive():
|
||||
mappers = list(Util.DMG_Header_Mapper.keys())
|
||||
for i in range(0, len(mappers)):
|
||||
if mappers[i] == 0x105:
|
||||
self.cmbHeaderMapperResult.setCurrentIndex(i)
|
||||
self.cmbDMGHeaderMapperResult.setCurrentIndex(Util.ConvertMapperToMapperType(0x105)[2])
|
||||
self.cmbDMGHeaderROMSizeResult.setCurrentIndex(5)
|
||||
cart_type = 0
|
||||
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
||||
for i in range(0, len(cart_types[0])):
|
||||
|
|
@ -966,7 +967,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
msgbox.exec()
|
||||
elif Util.AGB_Global_CRC32 == 0:
|
||||
self.lblAGBHeaderROMChecksumResult.setText("0x{:06X}".format(self.CONN.INFO["rom_checksum_calc"]))
|
||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
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:
|
||||
|
|
@ -996,6 +997,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
except Exception as e:
|
||||
print("ERROR: {:s}".format(str(e)))
|
||||
self.OpenPath(dumpinfo_file)
|
||||
elif msgbox.clickedButton() == button_open_dir:
|
||||
self.OpenPath(os.path.split(dumpinfo_file)[0])
|
||||
|
||||
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
|
||||
self.lblStatus4a.setText("Done!")
|
||||
|
|
@ -1020,8 +1023,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.CAMWIN.run()
|
||||
return
|
||||
|
||||
if "last_path" in self.CONN.INFO:
|
||||
button_open_dir = msgbox.addButton(" Open &Directory ", QtWidgets.QMessageBox.ActionRole)
|
||||
msgbox.setText("The save data backup is complete!" + msg_te)
|
||||
msgbox.exec()
|
||||
if "last_path" in self.CONN.INFO and msgbox.clickedButton() == button_open_dir:
|
||||
self.OpenPath(os.path.split(self.CONN.INFO["last_path"])[0])
|
||||
|
||||
elif self.CONN.INFO["last_action"] == 3: # Restore RAM
|
||||
self.lblStatus4a.setText("Done!")
|
||||
|
|
@ -1079,16 +1086,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if index in (-1, 0): return
|
||||
|
||||
def SetDMGMapperResult(self, cart_type):
|
||||
mappers = list(Util.DMG_Header_Mapper.keys())
|
||||
mbc = 0
|
||||
if "mbc" in cart_type:
|
||||
if isinstance(cart_type["mbc"], int):
|
||||
mbc = cart_type["mbc"]
|
||||
elif self.cmbHeaderMapperResult.currentIndex() > 0:
|
||||
mbc = mappers[self.cmbHeaderMapperResult.currentIndex()]
|
||||
for i in range(0, len(mappers)):
|
||||
if mbc == mappers[i]:
|
||||
self.cmbHeaderMapperResult.setCurrentIndex(i)
|
||||
elif self.cmbDMGHeaderMapperResult.currentIndex() > 0:
|
||||
mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
|
||||
self.cmbDMGHeaderMapperResult.setCurrentIndex(Util.ConvertMapperToMapperType(mbc)[2])
|
||||
|
||||
def CartridgeTypeChanged(self, index):
|
||||
self.STATUS["cart_type"] = {}
|
||||
|
|
@ -1101,7 +1105,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if "flash_size" in cart_types[1][index] and not "dmg-mmsa-jpn" in cart_types[1][index]:
|
||||
for i in range(0, len(Util.DMG_Header_ROM_Sizes_Flasher_Map)):
|
||||
if cart_types[1][index]["flash_size"] == (Util.DMG_Header_ROM_Sizes_Flasher_Map[i]):
|
||||
self.cmbHeaderROMSizeResult.setCurrentIndex(i)
|
||||
self.cmbDMGHeaderROMSizeResult.setCurrentIndex(i)
|
||||
self.STATUS["cart_type"] = cart_types[1][index]
|
||||
self.SetDMGMapperResult(cart_types[1][index])
|
||||
|
||||
|
|
@ -1126,7 +1130,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if not self.CheckDeviceAlive(): return
|
||||
if not self.CheckHeader(): return
|
||||
|
||||
mbc = (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderMapperResult.currentIndex()])[0]
|
||||
mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
|
||||
|
||||
rom_size = 0
|
||||
cart_type = 0
|
||||
|
|
@ -1138,7 +1142,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
path = QtWidgets.QFileDialog.getSaveFileName(self, "Backup ROM", last_dir + "/" + path, "Game Boy ROM File (*.gb *.sgb *.gbc);;All Files (*.*)")[0]
|
||||
cart_type = self.cmbDMGCartridgeTypeResult.currentIndex()
|
||||
rom_size = Util.DMG_Header_ROM_Sizes_Flasher_Map[self.cmbHeaderROMSizeResult.currentIndex()]
|
||||
rom_size = Util.DMG_Header_ROM_Sizes_Flasher_Map[self.cmbDMGHeaderROMSizeResult.currentIndex()]
|
||||
|
||||
elif self.CONN.GetMode() == "AGB":
|
||||
setting_name = "LastDirRomAGB"
|
||||
|
|
@ -1152,8 +1156,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if (path == ""): return
|
||||
|
||||
self.SETTINGS.setValue(setting_name, os.path.dirname(path))
|
||||
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblDMGHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
|
||||
self.grpDMGCartridgeInfo.setEnabled(False)
|
||||
self.grpAGBCartridgeInfo.setEnabled(False)
|
||||
|
|
@ -1214,7 +1218,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
if self.CONN.GetMode() == "DMG":
|
||||
self.SetDMGMapperResult(carts[cart_type])
|
||||
mbc = (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderMapperResult.currentIndex()])[0]
|
||||
mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
|
||||
else:
|
||||
mbc = 0
|
||||
|
||||
|
|
@ -1291,7 +1295,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if not Util.compare_mbc(hdr["mapper_raw"], mbc):
|
||||
mbc1 = Util.get_mbc_name(mbc)
|
||||
mbc2 = Util.get_mbc_name(hdr["mapper_raw"])
|
||||
compatible_mbc = [ "None", "MBC2", "MBC3", "MBC5", "MBC7", "GBD", "G-MMC1", "HuC-1", "HuC-3" ]
|
||||
compatible_mbc = [ "None", "MBC2", "MBC3", "MBC30", "MBC5", "MBC7", "MAC-GBD", "G-MMC1", "HuC-1", "HuC-3" ]
|
||||
if (mbc2 == "None") or (mbc1 == "G-MMC1" and mbc2 == "MBC1") or (mbc2 == "G-MMC1" and mbc1 == "MBC1"):
|
||||
pass
|
||||
elif mbc2 != "None" and not (mbc1 in compatible_mbc and mbc2 in compatible_mbc):
|
||||
|
|
@ -1336,14 +1340,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
elif msgbox.clickedButton() == button_2:
|
||||
pass
|
||||
|
||||
# flash_offset = self.PROGRESS.GetLastPosition()
|
||||
# if flash_offset > 0:
|
||||
# answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), "The previous Write ROM operation seems to have been interrupted. Do you want to resume from address 0x{:X}?".format(flash_offset), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Yes)
|
||||
# if answer == QtWidgets.QMessageBox.No:
|
||||
# flash_offset = 0
|
||||
# elif answer == QtWidgets.QMessageBox.Cancel:
|
||||
# return
|
||||
# self.PROGRESS.ResetLastPosition()
|
||||
flash_offset = 0
|
||||
|
||||
self.grpDMGCartridgeInfo.setEnabled(False)
|
||||
|
|
@ -1371,7 +1367,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if not self.CheckDeviceAlive(): return
|
||||
|
||||
rtc = False
|
||||
features = []
|
||||
add_date_time = self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled")
|
||||
path_datetime = ""
|
||||
if add_date_time and add_date_time.lower() == "enabled":
|
||||
|
|
@ -1384,12 +1379,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
setting_name = "LastDirSaveDataDMG"
|
||||
last_dir = self.SETTINGS.value(setting_name)
|
||||
if last_dir is None: last_dir = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.DocumentsLocation)
|
||||
mbc = (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderMapperResult.currentIndex()])[0]
|
||||
try:
|
||||
features = list(Util.DMG_Header_Mapper.keys())[self.cmbHeaderMapperResult.currentIndex()]
|
||||
except:
|
||||
features = []
|
||||
save_type = Util.DMG_Header_RAM_Sizes_Map[self.cmbHeaderRAMSizeResult.currentIndex()]
|
||||
mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
|
||||
save_type = Util.DMG_Header_RAM_Sizes_Map[self.cmbDMGHeaderSaveTypeResult.currentIndex()]
|
||||
if save_type == 0:
|
||||
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
|
||||
return
|
||||
|
|
@ -1419,8 +1410,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
verify_read = False
|
||||
|
||||
rtc = False
|
||||
if self.CONN.INFO["has_rtc"]: # features in (0x10, 0xFD, 0xFE): # RTC of MBC3, TAMA5, HuC-3
|
||||
if self.CONN.GetMode() == "DMG" and features == 0x10 and not self.CONN.IsClkConnected():
|
||||
if self.CONN.INFO["has_rtc"]:
|
||||
if self.CONN.GetMode() == "DMG" and mbc == 0x10 and not self.CONN.IsClkConnected():
|
||||
rtc = False
|
||||
else:
|
||||
msg = "A Real Time Clock cartridge was detected. Do you want the cartridge’s Real Time Clock register values also to be saved?"
|
||||
|
|
@ -1479,8 +1470,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
def WriteRAM(self, dpath="", erase=False, test=False):
|
||||
if not self.CheckDeviceAlive(): return
|
||||
|
||||
features = 0
|
||||
|
||||
if dpath == "":
|
||||
path = Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=self.SETTINGS)
|
||||
path = os.path.splitext(path)[0]
|
||||
|
|
@ -1490,12 +1479,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
setting_name = "LastDirSaveDataDMG"
|
||||
last_dir = self.SETTINGS.value(setting_name)
|
||||
if last_dir is None: last_dir = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.DocumentsLocation)
|
||||
mbc = (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderMapperResult.currentIndex()])[0]
|
||||
try:
|
||||
features = list(Util.DMG_Header_Mapper.keys())[self.cmbHeaderMapperResult.currentIndex()]
|
||||
except:
|
||||
features = []
|
||||
save_type = Util.DMG_Header_RAM_Sizes_Map[self.cmbHeaderRAMSizeResult.currentIndex()]
|
||||
mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
|
||||
save_type = Util.DMG_Header_RAM_Sizes_Map[self.cmbDMGHeaderSaveTypeResult.currentIndex()]
|
||||
if save_type == 0:
|
||||
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
|
||||
return
|
||||
|
|
@ -1560,10 +1545,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
rtc = False
|
||||
rtc_advance = False
|
||||
if not test and self.CONN.INFO["has_rtc"]: # features in (0x10, 0xFD, 0xFE): # RTC of MBC3, TAMA5, HuC-3
|
||||
if self.CONN.GetMode() == "DMG" and features == 0x10 and not self.CONN.IsClkConnected():
|
||||
if not test and self.CONN.INFO["has_rtc"]:
|
||||
if self.CONN.GetMode() == "DMG" and mbc == 0x10 and not self.CONN.IsClkConnected():
|
||||
rtc = False
|
||||
elif (self.CONN.GetMode() == "DMG" and ((features == 0xFD and (filesize == save_size + 0x28 or erase)) or (features == 0xFE and (filesize == save_size + 0xC or erase)) or (self.CONN.IsClkConnected() and features == 0x10 and filesize == save_size + 0x30 or erase))) or \
|
||||
elif (self.CONN.GetMode() == "DMG" and ((mbc == 0xFD and (filesize == save_size + 0x28 or erase)) or (mbc == 0xFE and (filesize == save_size + 0xC or erase)) or (self.CONN.IsClkConnected() and mbc == 0x10 and filesize == save_size + 0x30 or erase))) or \
|
||||
(self.CONN.GetMode() == "AGB" and (filesize == save_size + 0x10 or erase)):
|
||||
msg = "A Real Time Clock cartridge was detected. Do you want the Real Time Clock register values to be also written?"
|
||||
cb = QtWidgets.QCheckBox("&Adjust RTC", checked=True)
|
||||
|
|
@ -1628,7 +1613,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
test_ok = 0
|
||||
save1 = bytearray([0])
|
||||
save2 = bytearray([1])
|
||||
backup_fn = Util.CONFIG_PATH + "/backup_test_{:s}.sav".format(datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
||||
backup_fn = Util.CONFIG_PATH + "/backup_stress_test.bin"
|
||||
|
||||
try:
|
||||
self.lblStatus4a.setText("Testing ({:s} 1/2)...".format(test_patterns_names[0]))
|
||||
|
|
@ -1664,7 +1649,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
except:
|
||||
save1 = None
|
||||
|
||||
if save1 is not None and save1 == save2:
|
||||
if save1 is not None and save1 != save2:
|
||||
msg = "Test {:d} ({:s}) failed!\nNote: SRAM requires a working battery to retain save data.\n\nContinue anyway?".format(test_ok+1, test_patterns_names[test_ok])
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.No:
|
||||
save1 = None
|
||||
save2 = None
|
||||
|
||||
if save1 is not None:
|
||||
with open(backup_fn, "wb") as f: f.write(save1)
|
||||
test_ok += 1
|
||||
for i in range(0, len(test_patterns)):
|
||||
|
|
@ -1679,7 +1673,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
qt_app.processEvents()
|
||||
time.sleep(0.02)
|
||||
t.join()
|
||||
if i == 0:
|
||||
if i == 0 \
|
||||
and not (save1 != save2): # user "continued anyway"
|
||||
self.CONN.CartPowerOff()
|
||||
time.sleep(0.5)
|
||||
self.CONN.CartPowerOn()
|
||||
|
|
@ -1712,10 +1707,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
qt_app.processEvents()
|
||||
time.sleep(0.02)
|
||||
t.join()
|
||||
save3 = self.CONN.INFO["data"]
|
||||
if save3 == save2:
|
||||
if os.path.exists(backup_fn):
|
||||
os.unlink(backup_fn)
|
||||
|
||||
time_elapsed = time.time() - time_start
|
||||
msg_te = "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed, asFloat=True))
|
||||
|
|
@ -1725,6 +1716,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
qt_app.processEvents()
|
||||
if test_ok == len(test_patterns)+1:
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="All tests completed successfully!" + msg_te, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
msgbox.exec()
|
||||
else:
|
||||
try:
|
||||
if test_ok == 0:
|
||||
|
|
@ -1734,13 +1726,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
with open("debug_savetest_2.bin", "wb") as f: f.write(readback)
|
||||
except:
|
||||
pass
|
||||
msg = "Test {:d} ({:s}) failed!".format(test_ok+1, test_patterns_names[test_ok])
|
||||
if test_ok == 0:
|
||||
if self.CONN.GetMode() == "DMG" or self.CONN.GetMode() == "AGB" and save_type in (3, 7, 8):
|
||||
msg += "\nNote: SRAM requires a working battery to retain save data."
|
||||
msg += msg_te
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
msgbox.exec()
|
||||
if test_ok > 0:
|
||||
msg = "Test {:d} ({:s}) failed!".format(test_ok+1, test_patterns_names[test_ok])
|
||||
msg += msg_te
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
msgbox.exec()
|
||||
|
||||
self.grpDMGCartridgeInfo.setEnabled(True)
|
||||
self.grpAGBCartridgeInfo.setEnabled(True)
|
||||
|
|
@ -1880,7 +1870,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
def CheckDeviceAlive(self, setMode=False):
|
||||
if self.CONN is not None:
|
||||
mode = self.CONN.GetMode()
|
||||
if self.CONN.DEVICE is not None:
|
||||
if self.CONN.DEVICE is None:
|
||||
self.DisconnectDevice()
|
||||
else:
|
||||
if not self.CONN.IsConnected():
|
||||
self.DisconnectDevice()
|
||||
self.CONN = None
|
||||
|
|
@ -1995,78 +1987,73 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The cartridge connection is unstable!\nPlease clean the cartridge pins, carefully realign the cartridge and then try again.", QtWidgets.QMessageBox.Ok)
|
||||
|
||||
if self.CONN.GetMode() == "DMG":
|
||||
self.cmbHeaderMapperResult.clear()
|
||||
self.cmbHeaderMapperResult.addItems(list(Util.DMG_Header_Mapper.values()))
|
||||
self.cmbHeaderMapperResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cmbDMGHeaderMapperResult.clear()
|
||||
self.cmbDMGHeaderMapperResult.addItems(list(Util.DMG_Mapper_Types.keys()))
|
||||
self.cmbDMGHeaderMapperResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cmbDMGCartridgeTypeResult.clear()
|
||||
self.cmbDMGCartridgeTypeResult.addItems(self.CONN.GetSupportedCartridgesDMG()[0])
|
||||
self.cmbDMGCartridgeTypeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
if "flash_type" in data:
|
||||
self.cmbDMGCartridgeTypeResult.setCurrentIndex(data["flash_type"])
|
||||
self.cmbHeaderROMSizeResult.clear()
|
||||
self.cmbHeaderROMSizeResult.addItems(Util.DMG_Header_ROM_Sizes)
|
||||
self.cmbHeaderROMSizeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cmbHeaderRAMSizeResult.clear()
|
||||
self.cmbHeaderRAMSizeResult.addItems(Util.DMG_Header_RAM_Sizes)
|
||||
self.cmbHeaderRAMSizeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cmbDMGHeaderROMSizeResult.clear()
|
||||
self.cmbDMGHeaderROMSizeResult.addItems(Util.DMG_Header_ROM_Sizes)
|
||||
self.cmbDMGHeaderROMSizeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cmbDMGHeaderSaveTypeResult.clear()
|
||||
self.cmbDMGHeaderSaveTypeResult.addItems(Util.DMG_Header_RAM_Sizes)
|
||||
self.cmbDMGHeaderSaveTypeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
|
||||
self.lblHeaderTitleResult.setText(data['game_title'])
|
||||
if len(data['game_code']) > 0:
|
||||
self.lblHeaderRevision.setText("Game Code / Revision:")
|
||||
self.lblHeaderRevisionResult.setText("{:s}-{:s}".format(data['game_code'], str(data['version'])))
|
||||
self.lblDMGRomTitleResult.setText(data['game_title'])
|
||||
self.lblDMGGameCodeRevision.setText("Game Code and Revision:")
|
||||
if data["db"] is not None:
|
||||
self.lblDMGGameCodeRevisionResult.setText("{:s}-{:s}".format(data["db"]["gc"], str(data["version"])))
|
||||
temp = data["db"]["gn"]
|
||||
self.lblDMGGameNameResult.setText(temp)
|
||||
while self.lblDMGGameNameResult.fontMetrics().boundingRect(self.lblDMGGameNameResult.text()).width() > 170:
|
||||
temp = temp[:-1]
|
||||
self.lblDMGGameNameResult.setText(temp + "…")
|
||||
if temp != data["db"]["gn"]:
|
||||
self.lblDMGGameNameResult.setToolTip(data["db"]["gn"])
|
||||
else:
|
||||
self.lblDMGGameNameResult.setToolTip("")
|
||||
else:
|
||||
self.lblHeaderRevision.setText("Revision:")
|
||||
self.lblHeaderRevisionResult.setText(str(data['version']))
|
||||
self.lblDMGGameNameResult.setText("(Not in database)")
|
||||
if len(data['game_code']) > 0:
|
||||
self.lblDMGGameCodeRevisionResult.setText("{:s}-{:s}".format(data["game_code"], str(data["version"])))
|
||||
else:
|
||||
self.lblDMGGameCodeRevision.setText("Revision:")
|
||||
self.lblDMGGameCodeRevisionResult.setText(str(data['version']))
|
||||
|
||||
self.lblHeaderRtcResult.setText(data["rtc_string"])
|
||||
self.lblDMGHeaderRtcResult.setText(data["rtc_string"])
|
||||
|
||||
if data['logo_correct']:
|
||||
self.lblHeaderLogoValidResult.setText("OK")
|
||||
self.lblHeaderLogoValidResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
if data['logo_correct'] and data['header_checksum_correct']:
|
||||
self.lblDMGHeaderBootlogoResult.setText("OK")
|
||||
self.lblDMGHeaderBootlogoResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
else:
|
||||
self.lblHeaderLogoValidResult.setText("Invalid")
|
||||
self.lblHeaderLogoValidResult.setStyleSheet("QLabel { color: red; }")
|
||||
if data['header_checksum_correct']:
|
||||
self.lblHeaderChecksumResult.setText("Valid (0x{:02X})".format(data['header_checksum']))
|
||||
self.lblHeaderChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
else:
|
||||
self.lblHeaderChecksumResult.setText("Invalid (0x{:02X})".format(data['header_checksum']))
|
||||
self.lblHeaderChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||
self.lblHeaderROMChecksumResult.setText("0x{:04X}".format(data['rom_checksum']))
|
||||
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblDMGHeaderBootlogoResult.setText("Invalid")
|
||||
self.lblDMGHeaderBootlogoResult.setStyleSheet("QLabel { color: red; }")
|
||||
|
||||
self.cmbHeaderROMSizeResult.setCurrentIndex(data["rom_size_raw"])
|
||||
self.lblDMGHeaderROMChecksumResult.setText("0x{:04X}".format(data['rom_checksum']))
|
||||
self.lblDMGHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
self.cmbDMGHeaderROMSizeResult.setCurrentIndex(data["rom_size_raw"])
|
||||
for i in range(0, len(Util.DMG_Header_RAM_Sizes_Map)):
|
||||
if data["ram_size_raw"] == Util.DMG_Header_RAM_Sizes_Map[i]:
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(i)
|
||||
i = 0
|
||||
for k in Util.DMG_Header_Mapper.keys():
|
||||
if data["mapper_raw"] == k:
|
||||
self.cmbHeaderMapperResult.setCurrentIndex(i)
|
||||
if k == 0x06: # MBC2
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(1)
|
||||
elif k == 0x22 and data["game_title"] in ("KORO2 KIRBY", "KIRBY TNT"): # MBC7 Kirby
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(Util.DMG_Header_RAM_Sizes_Map.index(0x101))
|
||||
elif k == 0x22 and data["game_title"] in ("CMASTER"): # MBC7 Command Master
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(Util.DMG_Header_RAM_Sizes_Map.index(0x102))
|
||||
elif k == 0xFD: # TAMA5
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(Util.DMG_Header_RAM_Sizes_Map.index(0x103))
|
||||
elif k == 0x20: # MBC6
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(Util.DMG_Header_RAM_Sizes_Map.index(0x104))
|
||||
self.cmbDMGHeaderSaveTypeResult.setCurrentIndex(i)
|
||||
temp = Util.ConvertMapperToMapperType(data["mapper_raw"])
|
||||
mapper_type = temp[2]
|
||||
self.cmbDMGHeaderMapperResult.setCurrentIndex(mapper_type)
|
||||
|
||||
i += 1
|
||||
|
||||
if data['empty'] == True: # defaults
|
||||
if data['empty_nocart'] == True:
|
||||
self.lblHeaderTitleResult.setText("(No cartridge connected)")
|
||||
self.lblDMGGameNameResult.setText("(No cartridge connected)")
|
||||
else:
|
||||
self.lblHeaderTitleResult.setText("(No ROM data detected)")
|
||||
self.lblHeaderTitleResult.setStyleSheet("QLabel { color: red; }")
|
||||
self.cmbHeaderROMSizeResult.setCurrentIndex(0)
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(0)
|
||||
self.cmbHeaderMapperResult.setCurrentIndex(0)
|
||||
self.lblDMGGameNameResult.setText("(No ROM data detected)")
|
||||
self.lblDMGGameNameResult.setStyleSheet("QLabel { color: red; }")
|
||||
self.cmbDMGHeaderROMSizeResult.setCurrentIndex(0)
|
||||
self.cmbDMGHeaderSaveTypeResult.setCurrentIndex(0)
|
||||
self.cmbDMGHeaderMapperResult.setCurrentIndex(0)
|
||||
else:
|
||||
self.lblHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblDMGGameNameResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
|
||||
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["mapper_raw"]) and resetStatus:
|
||||
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates in the Tools menu or the maker’s website.".format(APPNAME, self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
|
||||
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU "):
|
||||
|
|
@ -2076,37 +2063,36 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.cmbDMGCartridgeTypeResult.setCurrentIndex(i)
|
||||
|
||||
if data["mapper_raw"] == 0x203: # Xploder GB
|
||||
self.lblHeaderRtcResult.setText("")
|
||||
self.lblHeaderLogoValidResult.setText("")
|
||||
self.lblHeaderLogoValidResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblHeaderChecksumResult.setText("")
|
||||
self.lblHeaderChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblHeaderROMChecksumResult.setText("")
|
||||
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblDMGHeaderRtcResult.setText("")
|
||||
self.lblDMGHeaderBootlogoResult.setText("")
|
||||
self.lblDMGHeaderBootlogoResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
self.lblDMGHeaderROMChecksumResult.setText("")
|
||||
self.lblDMGHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
elif data["mapper_raw"] == 0x205: # Datel Orbit V2
|
||||
self.lblHeaderRtcResult.setText("")
|
||||
self.lblHeaderRevisionResult.setText("")
|
||||
self.lblHeaderLogoValidResult.setText("")
|
||||
self.lblHeaderLogoValidResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblHeaderChecksumResult.setText("")
|
||||
self.lblHeaderChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblHeaderROMChecksumResult.setText("")
|
||||
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblDMGHeaderRtcResult.setText("")
|
||||
self.lblDMGHeaderBootlogoResult.setText("")
|
||||
self.lblDMGHeaderBootlogoResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
self.lblDMGGameCodeRevisionResult.setText("")
|
||||
self.lblDMGGameCodeRevisionResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
self.lblDMGHeaderROMChecksumResult.setText("")
|
||||
self.lblDMGHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
elif data["mapper_raw"] == 0x204: # Sachen
|
||||
self.lblHeaderRtcResult.setText("")
|
||||
self.lblHeaderRevisionResult.setText("")
|
||||
self.lblHeaderLogoValidResult.setText("")
|
||||
self.lblHeaderLogoValidResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblDMGGameNameResult.setText(data["game_title"])
|
||||
self.lblDMGHeaderRtcResult.setText("")
|
||||
self.lblDMGRomTitleResult.setText("")
|
||||
self.lblDMGGameCodeRevisionResult.setText("")
|
||||
self.lblDMGHeaderBootlogoResult.setText("")
|
||||
self.lblDMGHeaderBootlogoResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
if "logo_sachen" in data:
|
||||
data["logo_sachen"].putpalette([ 255, 255, 255, 128, 128, 128 ])
|
||||
self.lblHeaderLogoValidResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo_sachen"].convert("RGBA"))))
|
||||
self.lblDMGHeaderBootlogoResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo_sachen"].convert("RGBA"))))
|
||||
else:
|
||||
if "logo" in data:
|
||||
if data['logo_correct']:
|
||||
data["logo"].putpalette([ 255, 255, 255, self.TEXT_COLOR[0], self.TEXT_COLOR[1], self.TEXT_COLOR[2] ])
|
||||
else:
|
||||
data["logo"].putpalette([ 255, 255, 255, 251, 0, 24 ])
|
||||
self.lblHeaderLogoValidResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo"].convert("RGBA"))))
|
||||
self.lblDMGHeaderBootlogoResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo"].convert("RGBA"))))
|
||||
|
||||
self.grpAGBCartridgeInfo.setVisible(False)
|
||||
self.grpDMGCartridgeInfo.setVisible(True)
|
||||
|
|
@ -2119,12 +2105,28 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if "flash_type" in data:
|
||||
self.cmbAGBCartridgeTypeResult.setCurrentIndex(data["flash_type"])
|
||||
|
||||
self.lblAGBHeaderTitleResult.setText(data['game_title'])
|
||||
self.lblAGBHeaderCodeResult.setText(data['game_code'])
|
||||
self.lblAGBHeaderRevisionResult.setText(str(data['version']))
|
||||
self.lblAGBRomTitleResult.setText(data['game_title'])
|
||||
if data["db"] is not None:
|
||||
self.lblAGBHeaderGameCodeRevisionResult.setText("{:s}-{:s}".format(data["db"]["gc"], str(data["version"])))
|
||||
temp = data["db"]["gn"]
|
||||
self.lblAGBGameNameResult.setText(temp)
|
||||
while self.lblAGBGameNameResult.fontMetrics().boundingRect(self.lblAGBGameNameResult.text()).width() > 170:
|
||||
temp = temp[:-1]
|
||||
self.lblAGBGameNameResult.setText(temp + "…")
|
||||
if temp != data["db"]["gn"]:
|
||||
self.lblAGBGameNameResult.setToolTip(data["db"]["gn"])
|
||||
else:
|
||||
self.lblAGBGameNameResult.setToolTip("")
|
||||
else:
|
||||
if len(data["game_code"]) > 0:
|
||||
self.lblAGBHeaderGameCodeRevisionResult.setText("{:s}-{:s}".format(data['game_code'], str(data['version'])))
|
||||
else:
|
||||
self.lblAGBHeaderGameCodeRevisionResult.setText("")
|
||||
self.lblAGBGameNameResult.setText("(Not in database)")
|
||||
|
||||
if data['logo_correct']:
|
||||
self.lblAGBHeaderLogoValidResult.setText("OK")
|
||||
self.lblAGBHeaderLogoValidResult.setStyleSheet(self.lblAGBHeaderCodeResult.styleSheet())
|
||||
self.lblAGBHeaderLogoValidResult.setStyleSheet(self.lblAGBRomTitleResult.styleSheet())
|
||||
else:
|
||||
self.lblAGBHeaderLogoValidResult.setText("Invalid")
|
||||
self.lblAGBHeaderLogoValidResult.setStyleSheet("QLabel { color: red; }")
|
||||
|
|
@ -2133,23 +2135,22 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
if data['header_checksum_correct']:
|
||||
self.lblAGBHeaderChecksumResult.setText("Valid (0x{:02X})".format(data['header_checksum']))
|
||||
self.lblAGBHeaderChecksumResult.setStyleSheet(self.lblAGBHeaderCodeResult.styleSheet())
|
||||
self.lblAGBHeaderChecksumResult.setStyleSheet(self.lblAGBRomTitleResult.styleSheet())
|
||||
else:
|
||||
self.lblAGBHeaderChecksumResult.setText("Invalid (0x{:02X})".format(data['header_checksum']))
|
||||
self.lblAGBHeaderChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||
|
||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
self.lblAGBHeaderROMChecksumResult.setText("Not available")
|
||||
Util.AGB_Global_CRC32 = 0
|
||||
|
||||
db_agb_entry = data["db"]
|
||||
if db_agb_entry is None:
|
||||
self.lblAGBHeaderROMChecksumResult.setText("Not in database")
|
||||
if db_agb_entry != None:
|
||||
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(Util.AGB_Header_ROM_Sizes_Map.index(db_agb_entry['rs']))
|
||||
if data["db"] is None:
|
||||
self.lblAGBHeaderROMChecksumResult.setText("(Not in database)")
|
||||
if data["db"] != None:
|
||||
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(db_agb_entry['rc']))
|
||||
Util.AGB_Global_CRC32 = db_agb_entry['rc']
|
||||
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
|
||||
|
|
@ -2162,21 +2163,21 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
if data["save_type"] == None:
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(0)
|
||||
if db_agb_entry != None:
|
||||
if db_agb_entry['st'] < len(Util.AGB_Header_Save_Types):
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(db_agb_entry['st'])
|
||||
if data["db"] != None:
|
||||
if data["db"]['st'] < len(Util.AGB_Header_Save_Types):
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(data["db"]['st'])
|
||||
if data["dacs_8m"] is True:
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(6)
|
||||
|
||||
if data['empty'] == True: # defaults
|
||||
if data['empty_nocart'] == True:
|
||||
self.lblAGBHeaderTitleResult.setText("(No cartridge connected)")
|
||||
self.lblAGBGameNameResult.setText("(No cartridge connected)")
|
||||
else:
|
||||
self.lblAGBHeaderTitleResult.setText("(No ROM data detected)")
|
||||
self.lblAGBHeaderTitleResult.setStyleSheet("QLabel { color: red; }")
|
||||
self.lblAGBGameNameResult.setText("(No ROM data detected)")
|
||||
self.lblAGBGameNameResult.setStyleSheet("QLabel { color: red; }")
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(0)
|
||||
else:
|
||||
self.lblAGBHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||
self.lblAGBGameNameResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
|
||||
if data['logo_correct'] and data['3d_memory'] is True:
|
||||
cart_types = self.CONN.GetSupportedCartridgesAGB()
|
||||
for i in range(0, len(cart_types[0])):
|
||||
|
|
@ -2186,7 +2187,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.grpDMGCartridgeInfo.setVisible(False)
|
||||
self.grpAGBCartridgeInfo.setVisible(True)
|
||||
|
||||
if data['logo_correct'] and isinstance(db_agb_entry, dict) and "rs" in db_agb_entry and db_agb_entry['rs'] == 0x4000000 and not self.CONN.IsSupported3dMemory() and resetStatus:
|
||||
if data['logo_correct'] and isinstance(data["db"], dict) and "rs" in data["db"] and data["db"]['rs'] == 0x4000000 and not self.CONN.IsSupported3dMemory() and resetStatus:
|
||||
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a Memory Bank Controller that may not be completely supported by the firmware of the {:s} device. Please check for firmware updates in the Tools menu or the maker’s website.".format(self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
|
||||
|
||||
if resetStatus:
|
||||
|
|
@ -2234,7 +2235,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
try:
|
||||
if save_type is not None and save_type is not False:
|
||||
if self.CONN.GetMode() == "DMG":
|
||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(save_type)
|
||||
self.cmbDMGHeaderSaveTypeResult.setCurrentIndex(save_type)
|
||||
elif self.CONN.GetMode() == "AGB":
|
||||
self.cmbAGBSaveTypeResult.setCurrentIndex(save_type)
|
||||
except:
|
||||
|
|
@ -2272,7 +2273,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
# Messages
|
||||
# Header
|
||||
msg_header_s = "<b>Game Title:</b> {:s}<br>".format(header["game_title"])
|
||||
msg_header_s = "<b>ROM Title:</b> {:s}<br>".format(header["game_title"])
|
||||
|
||||
# Save Type
|
||||
msg_save_type_s = ""
|
||||
|
|
@ -2290,11 +2291,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>Possible Batteryless SRAM Location:</b> 0x{:X} (expecting {:s} of SRAM)".format(header["batteryless_sram"]["bl_offset"], 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(header["batteryless_sram"]["bl_size"], asInt=True))
|
||||
elif save_size == header["batteryless_sram"]["bl_size"]:
|
||||
temp += " ({:s})<br><b>Possible Batteryless SRAM Location:</b> 0x{:X}".format(Util.formatFileSize(save_size, asInt=True), header["batteryless_sram"]["bl_offset"])
|
||||
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))
|
||||
else:
|
||||
temp += " ({:s})<br><b>Possible Batteryless SRAM Location:</b> 0x{:X} (expecting {:s} of SRAM)".format(Util.formatFileSize(save_size, asInt=True), header["batteryless_sram"]["bl_offset"], 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(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))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
|
@ -2684,7 +2685,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if self.CONN.GetMode() == "DMG":
|
||||
header = self.CONN.ReadInfo(setPinsAsInputs=True)
|
||||
if header["mapper_raw"] == 252: # GBD
|
||||
args = { "path":None, "mbc":252, "save_type":128*1024, "rtc":False }
|
||||
args = { "path":None, "mbc":252, "save_type":4, "rtc":False }
|
||||
self.CONN.BackupRAM(fncSetProgress=False, args=args)
|
||||
data = self.CONN.INFO["data"]
|
||||
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ class DMG_MBC:
|
|||
mbc_id = args["mbc"]
|
||||
if mbc_id in (0x01, 0x02, 0x03): # 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY',
|
||||
return DMG_MBC1(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id == 0x06: # 0x06:'MBC2+SRAM+BATTERY',
|
||||
elif mbc_id in (0x05, 0x06): # 0x06:'MBC2+SRAM+BATTERY',
|
||||
return DMG_MBC2(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id in (0x10, 0x13): # 0x10:'MBC3+RTC+SRAM+BATTERY', 0x13:'MBC3+SRAM+BATTERY',
|
||||
elif mbc_id in (0x10, 0x11, 0x12, 0x13, 0x110): # 0x10:'MBC3+RTC+SRAM+BATTERY', 0x13:'MBC3+SRAM+BATTERY',
|
||||
return DMG_MBC3(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id in (0x19, 0x1B, 0x1C, 0x1E): # 0x19:'MBC5', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY',
|
||||
elif mbc_id in (0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E): # 0x19:'MBC5', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY',
|
||||
return DMG_MBC5(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id == 0x20: # 0x20:'MBC6+FLASH+SRAM+BATTERY',
|
||||
return DMG_MBC6(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
|
|
@ -53,7 +53,7 @@ class DMG_MBC:
|
|||
return DMG_MBC1M(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id in (0x0B, 0x0D): # 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY',
|
||||
return DMG_MMM01(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id == 0xFC: # 0xFC:'GBD+SRAM+BATTERY',
|
||||
elif mbc_id == 0xFC: # 0xFC:'MAC-GBD+SRAM+BATTERY',
|
||||
return DMG_GBD(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
elif mbc_id == 0x105: # 0x105:'G-MMC1+SRAM+BATTERY',
|
||||
return DMG_GMMC1(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||
|
|
@ -462,23 +462,6 @@ class DMG_MBC5(DMG_MBC):
|
|||
]
|
||||
self.CartWrite(commands)
|
||||
|
||||
# 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 ],
|
||||
# ]
|
||||
|
||||
# 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
|
||||
|
||||
|
|
@ -679,7 +662,7 @@ class DMG_MMM01(DMG_MBC):
|
|||
|
||||
class DMG_GBD(DMG_MBC5):
|
||||
def GetName(self):
|
||||
return "GBD"
|
||||
return "MAC-GBD"
|
||||
|
||||
def GetMaxROMSize(self):
|
||||
return 1*1024*1024
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ class RomFileAGB:
|
|||
if (data["game_title"] == "NGC-HIKARU3" and data["game_code"] == "GHTJ" and data["header_checksum"] == 0xB3):
|
||||
data["dacs_8m"] = True
|
||||
|
||||
data["unchanged"] = data
|
||||
self.DATA = data
|
||||
data["db"] = self.GetDatabaseEntry()
|
||||
return data
|
||||
|
|
@ -115,6 +116,16 @@ class RomFileAGB:
|
|||
db = json.loads(db)
|
||||
if data["header_sha1"] in db.keys():
|
||||
db_entry = db[data["header_sha1"]]
|
||||
if db_entry["gc"] in ("ZMAJ", "ZMBJ", "ZMDE"):
|
||||
db_entry["gc"] = "AGS-{:s}".format(db_entry["gc"])
|
||||
elif db_entry["gc"] == "ZBBJ":
|
||||
db_entry["gc"] = "NTR-{:s}".format(db_entry["gc"])
|
||||
elif db_entry["gc"] == "PEAJ":
|
||||
db_entry["gc"] = "PEC-{:s}".format(db_entry["gc"])
|
||||
elif db_entry["gc"] in ("PSAJ", "PSAE"):
|
||||
db_entry["gc"] = "PES-{:s}".format(db_entry["gc"])
|
||||
else:
|
||||
db_entry["gc"] = "AGB-{:s}".format(db_entry["gc"])
|
||||
else:
|
||||
print("FAIL: Database for Game Boy Advance titles not found at {0:s}/db_AGB.json".format(Util.CONFIG_PATH))
|
||||
return db_entry
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# FlashGBX
|
||||
# Author: Lesserkuma (github.com/lesserkuma)
|
||||
|
||||
import hashlib, re, string, struct, os, json
|
||||
import hashlib, re, string, struct, os, json, copy
|
||||
from . import Util
|
||||
|
||||
try:
|
||||
|
|
@ -139,8 +139,31 @@ class RomFileDMG:
|
|||
data["rom_checksum"] = int(256 * buffer[0x14E] + buffer[0x14F])
|
||||
data["rom_checksum_calc"] = self.CalcChecksumGlobal()
|
||||
data["rom_checksum_correct"] = data["rom_checksum"] == data["rom_checksum_calc"]
|
||||
|
||||
|
||||
data["unchanged"] = copy.copy(data)
|
||||
if not unchanged:
|
||||
# MBC2
|
||||
if data["mapper_raw"] == 0x06:
|
||||
data["ram_size_raw"] = 0x100
|
||||
|
||||
# MBC30
|
||||
if data["mapper_raw"] == 0x10 and data["ram_size_raw"] == 0x05:
|
||||
data["mapper_raw"] += 0x100
|
||||
|
||||
# MBC6
|
||||
if data["mapper_raw"] == 0x20:
|
||||
data["ram_size_raw"] = 0x104
|
||||
|
||||
# MBC7
|
||||
if data["mapper_raw"] == 0x22 and data["game_title"] in ("KORO2 KIRBY", "KIRBY TNT"):
|
||||
data["ram_size_raw"] = 0x101
|
||||
elif data["mapper_raw"] == 0x22 and data["game_title"] == "CMASTER":
|
||||
data["ram_size_raw"] = 0x102
|
||||
|
||||
# TAMA5
|
||||
if data["mapper_raw"] == 0xFD:
|
||||
data["ram_size_raw"] = 0x103
|
||||
|
||||
# MBC1M
|
||||
if data["mapper_raw"] == 0x03 and data["game_title"] == "MOMOCOL" and data["header_checksum"] == 0x28 or \
|
||||
data["mapper_raw"] == 0x01 and data["game_title"] == "BOMCOL" and data["header_checksum"] == 0x86 or \
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ from .pyside import QtCore, QtWidgets, QtGui
|
|||
class UserInputDialog(QtWidgets.QDialog):
|
||||
APP = None
|
||||
|
||||
def greetings(self):
|
||||
print("hi")
|
||||
|
||||
def __init__(self, app, icon=None, args=None):
|
||||
super(UserInputDialog, self).__init__(app)
|
||||
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
|
||||
|
|
|
|||
123
FlashGBX/Util.py
123
FlashGBX/Util.py
|
|
@ -7,9 +7,9 @@ from enum import Enum
|
|||
|
||||
# Common constants
|
||||
APPNAME = "FlashGBX"
|
||||
VERSION_PEP440 = "3.24"
|
||||
VERSION_PEP440 = "3.25"
|
||||
VERSION = "v{:s}".format(VERSION_PEP440)
|
||||
VERSION_TIMESTAMP = 1680999807
|
||||
VERSION_TIMESTAMP = 1681494424
|
||||
DEBUG = False
|
||||
DEBUG_LOG = []
|
||||
APP_PATH = ""
|
||||
|
|
@ -23,14 +23,14 @@ 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 ]
|
||||
|
||||
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x12:'MBC3+SRAM', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper' }
|
||||
DMG_Mapper_Types = { "None":[ 0x00 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x06 ], "MBC3/MBC30":[ 0x10, 0x12, 0x13 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "256M Multi Cart":[ 0x201 ], "Wisdom Tree":[ 0x202 ], "Xploder GB":[ 0x203 ], "Sachen":[ 0x204 ], "Datel Orbit V2":[ 0x205 ] }
|
||||
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x110:'MBC30+RTC+SRAM+BATTERY', 0x12:'MBC3+SRAM', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'MAC-GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper' }
|
||||
DMG_Mapper_Types = { "None":[ 0x00, 0x08, 0x09 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x05, 0x06 ], "MBC3":[ 0x10, 0x11, 0x12, 0x13 ], "MBC30":[ 0x110 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "MAC-GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "Unlicensed 256M Multi Cart Mapper":[ 0x201 ], "Unlicensed Wisdom Tree Mapper":[ 0x202 ], "Unlicensed Xploder GB Mapper":[ 0x203 ], "Unlicensed Sachen Mapper":[ 0x204 ], "Unlicensed Datel Orbit V2 Mapper":[ 0x205 ] }
|
||||
DMG_Header_ROM_Sizes = [ "32 KiB", "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB", "8 MiB", "16 MiB", "32 MiB" ]
|
||||
DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A ]
|
||||
DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000 ]
|
||||
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "64K SRAM (8 KiB)", "256K SRAM (32 KiB)", "512K SRAM (64 KiB)", "1M SRAM (128 KiB)", "MBC6 SRAM+FLASH (1.03 MiB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KiB)", "Unlicensed 1M EEPROM (128 KiB)" ]
|
||||
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201, 0x203, 0x204 ]
|
||||
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000, 0x20000 ] # RAM size in bytes
|
||||
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "16K SRAM (2 KiB)", "64K SRAM (8 KiB)", "256K SRAM (32 KiB)", "512K SRAM (64 KiB)", "1M SRAM (128 KiB)", "MBC6 SRAM+FLASH (1.03 MiB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KiB)", "Unlicensed 1M EEPROM (128 KiB)" ]
|
||||
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x100, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201, 0x203, 0x204 ]
|
||||
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x800, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000, 0x20000 ] # RAM size in bytes
|
||||
DMG_Header_SGB = { 0x00:'No support', 0x03:'Supported' }
|
||||
DMG_Header_CGB = { 0x00:'No support', 0x80:'Supported', 0xC0:'Required' }
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ class IniSettings():
|
|||
else:
|
||||
with open(path, "w+", encoding="UTF-8") as f: f.close()
|
||||
except:
|
||||
print("Error accessing the configuration directory or settings file.")
|
||||
print("Can’t access the configuration directory or settings file.")
|
||||
return
|
||||
self.FILENAME = path
|
||||
self.SETTINGS = configparser.RawConfigParser()
|
||||
|
|
@ -66,7 +66,7 @@ class IniSettings():
|
|||
self.Reload()
|
||||
except configparser.MissingSectionHeaderError:
|
||||
print("Resetting invalid configuration file...")
|
||||
os.unlink(path)
|
||||
with open(path, "w+", encoding="UTF-8") as f: f.close()
|
||||
path = ""
|
||||
|
||||
if path == "":
|
||||
|
|
@ -460,7 +460,29 @@ def ParseCFI(buffer):
|
|||
|
||||
return info
|
||||
|
||||
def ConvertMapperToMapperType(mapper_raw):
|
||||
i = 0
|
||||
for (k, v) in DMG_Mapper_Types.items():
|
||||
if i == 0:
|
||||
retval = (k, v, i)
|
||||
if mapper_raw in v:
|
||||
retval = (k, v, i)
|
||||
break
|
||||
i += 1
|
||||
# (string, list of ids, index in DMG_Mapper_Types)
|
||||
return retval
|
||||
|
||||
def ConvertMapperTypeToMapper(mapper_type):
|
||||
i = 0
|
||||
for (_, v) in DMG_Mapper_Types.items():
|
||||
if mapper_type == i:
|
||||
return v[0]
|
||||
i += 1
|
||||
return 0
|
||||
|
||||
def GetDumpReport(di, device):
|
||||
header = di["header"]["unchanged"]
|
||||
if "db" in di["header"]: header["db"] = di["header"]["db"]
|
||||
if di["system"] == "DMG":
|
||||
mode = "DMG"
|
||||
di["system"] = "Game Boy"
|
||||
|
|
@ -470,7 +492,7 @@ def GetDumpReport(di, device):
|
|||
di["rom_size"] = "{:,} bytes".format(di["rom_size"])
|
||||
|
||||
if di["mapper_type"] in DMG_Header_Mapper:
|
||||
di["mapper_type"] = "{:s}".format(DMG_Header_Mapper[di["mapper_type"]])
|
||||
di["mapper_type"] = "{:s}".format(ConvertMapperToMapperType(di["mapper_type"])[0])
|
||||
else:
|
||||
di["mapper_type"] = "0x{:02X}".format(di["mapper_type"])
|
||||
|
||||
|
|
@ -530,41 +552,41 @@ def GetDumpReport(di, device):
|
|||
"* Cartridge Type: {cart_type:s}\n" \
|
||||
.format(system=di["system"], rom_size=di["rom_size"], cart_type=di["cart_type"])
|
||||
|
||||
di["hdr_logo"] = "OK" if di["header"]["logo_correct"] else "Invalid"
|
||||
di["header"]["game_title_raw"] = di["header"]["game_title_raw"].replace("\0", "␀")
|
||||
di["hdr_logo"] = "OK" if header["logo_correct"] else "Invalid"
|
||||
header["game_title_raw"] = header["game_title_raw"].replace("\0", "␀")
|
||||
if mode == "DMG":
|
||||
game_title = di["header"]["game_title"]
|
||||
game_title = header["game_title"]
|
||||
game_code = ""
|
||||
if di["header"]["cgb"] in (0xC0, 0x80):
|
||||
if "game_code" in di["header"] and len(di["header"]["game_code"]) > 0:
|
||||
game_code = "* Game Code: {:s}\n".format(di["header"]["game_code"])
|
||||
if header["cgb"] in (0xC0, 0x80):
|
||||
if "game_code" in header and len(header["game_code"]) > 0:
|
||||
game_code = "* Game Code: {:s}\n".format(header["game_code"])
|
||||
di["hdr_target_platform"] = "Game Boy Color"
|
||||
elif di["header"]["old_lic"] == 0x33 and di["header"]["sgb"] == 0x03:
|
||||
elif header["old_lic"] == 0x33 and header["sgb"] == 0x03:
|
||||
di["hdr_target_platform"] = "Super Game Boy"
|
||||
else:
|
||||
di["hdr_target_platform"] = "Game Boy"
|
||||
|
||||
if di["header"]["old_lic"] == 0x33 and di["header"]["sgb"] == 0x03:
|
||||
if header["old_lic"] == 0x33 and header["sgb"] == 0x03:
|
||||
di["hdr_sgb"] = "Supported"
|
||||
else:
|
||||
di["hdr_sgb"] = "No support" # (146h=0x{:02X}, 14Bh=0x{:02X})".format(di["header"]["sgb"], di["header"]["old_lic"])
|
||||
if di["header"]["cgb"] in DMG_Header_CGB:
|
||||
di["hdr_cgb"] = "{:s}".format(DMG_Header_CGB[di["header"]["cgb"]])
|
||||
di["hdr_sgb"] = "No support" # (146h=0x{:02X}, 14Bh=0x{:02X})".format(header["sgb"], header["old_lic"])
|
||||
if header["cgb"] in DMG_Header_CGB:
|
||||
di["hdr_cgb"] = "{:s}".format(DMG_Header_CGB[header["cgb"]])
|
||||
else:
|
||||
di["hdr_cgb"] = "Unknown (0x{:02X})".format(di["header"]["cgb"])
|
||||
di["hdr_cgb"] = "Unknown (0x{:02X})".format(header["cgb"])
|
||||
|
||||
di["hdr_header_checksum"] = "OK (0x{:02X})".format(di["header"]["header_checksum"]) if di["header"]["header_checksum_correct"] else "Invalid (0x{:02X}≠0x{:02X})".format(di["header"]["header_checksum_calc"], di["header"]["header_checksum"])
|
||||
di["header"]["rom_checksum_calc"] = device.INFO["rom_checksum_calc"]
|
||||
di["header"]["rom_checksum_correct"] = di["header"]["rom_checksum_calc"] == di["header"]["rom_checksum"]
|
||||
di["hdr_rom_checksum"] = "OK (0x{:04X})".format(di["header"]["rom_checksum"]) if di["header"]["rom_checksum_correct"] else "Invalid (0x{:04X}≠0x{:04X})".format(di["header"]["rom_checksum_calc"], di["header"]["rom_checksum"])
|
||||
di["hdr_header_checksum"] = "OK (0x{:02X})".format(header["header_checksum"]) if header["header_checksum_correct"] else "Invalid (0x{:02X}≠0x{:02X})".format(header["header_checksum_calc"], header["header_checksum"])
|
||||
header["rom_checksum_calc"] = device.INFO["rom_checksum_calc"]
|
||||
header["rom_checksum_correct"] = header["rom_checksum_calc"] == header["rom_checksum"]
|
||||
di["hdr_rom_checksum"] = "OK (0x{:04X})".format(header["rom_checksum"]) if header["rom_checksum_correct"] else "Invalid (0x{:04X}≠0x{:04X})".format(header["rom_checksum_calc"], header["rom_checksum"])
|
||||
|
||||
di["hdr_rom_size"] = di["header"]["rom_size_raw"]
|
||||
di["hdr_rom_size"] = header["rom_size_raw"]
|
||||
if di["hdr_rom_size"] in DMG_Header_ROM_Sizes_Map:
|
||||
di["hdr_rom_size"] = "{:s} (0x{:02X})".format(DMG_Header_ROM_Sizes[DMG_Header_ROM_Sizes_Map.index(di["hdr_rom_size"])], di["hdr_rom_size"])
|
||||
else:
|
||||
di["hdr_rom_size"] = "Unknown (0x{:02X})".format(di["hdr_rom_size"])
|
||||
|
||||
di["hdr_save_type"] = di["header"]["ram_size_raw"]
|
||||
di["hdr_save_type"] = header["ram_size_raw"]
|
||||
if di["hdr_save_type"] == 0x00:
|
||||
di["hdr_save_type"] = "No SRAM (0x{:02X})".format(di["hdr_save_type"])
|
||||
elif di["hdr_save_type"] in DMG_Header_RAM_Sizes_Map:
|
||||
|
|
@ -572,7 +594,7 @@ def GetDumpReport(di, device):
|
|||
else:
|
||||
di["hdr_save_type"] = "Unknown (0x{:02X})".format(di["hdr_save_type"])
|
||||
|
||||
di["hdr_mapper_type"] = di["header"]["mapper_raw"]
|
||||
di["hdr_mapper_type"] = header["mapper_raw"]
|
||||
if di["hdr_mapper_type"] in DMG_Header_Mapper:
|
||||
di["hdr_mapper_type"] = "{:s} (0x{:02X})".format(DMG_Header_Mapper[di["hdr_mapper_type"]], di["hdr_mapper_type"])
|
||||
else:
|
||||
|
|
@ -592,7 +614,7 @@ def GetDumpReport(di, device):
|
|||
"* SRAM Size: {hdr_save_type:s}\n" \
|
||||
"* Mapper Type: {hdr_mapper_type:s}\n" \
|
||||
"* Target Platform: {hdr_target_platform:s}\n" \
|
||||
.format(hdr_game_title=game_title, hdr_game_code=game_code, hdr_revision=str(di["header"]["version"]), hdr_sgb=di["hdr_sgb"], hdr_cgb=di["hdr_cgb"], hdr_logo=di["hdr_logo"], hdr_header_checksum=di["hdr_header_checksum"], hdr_rom_checksum=di["hdr_rom_checksum"], hdr_rom_size=di["hdr_rom_size"], hdr_save_type=di["hdr_save_type"], hdr_mapper_type=di["hdr_mapper_type"], hdr_target_platform=di["hdr_target_platform"])
|
||||
.format(hdr_game_title=game_title, hdr_game_code=game_code, hdr_revision=str(header["version"]), hdr_sgb=di["hdr_sgb"], hdr_cgb=di["hdr_cgb"], hdr_logo=di["hdr_logo"], hdr_header_checksum=di["hdr_header_checksum"], hdr_rom_checksum=di["hdr_rom_checksum"], hdr_rom_size=di["hdr_rom_size"], hdr_save_type=di["hdr_save_type"], hdr_mapper_type=di["hdr_mapper_type"], hdr_target_platform=di["hdr_target_platform"])
|
||||
if "gbmem" in di and di["gbmem"] is not None:
|
||||
raw_data = ""
|
||||
for i in range(0, 4):
|
||||
|
|
@ -622,10 +644,22 @@ def GetDumpReport(di, device):
|
|||
s += "" \
|
||||
"* GB-Memory Data: {:s}\n" \
|
||||
.format(raw_data)
|
||||
|
||||
|
||||
if header["db"] is not None and header["db"]["rc"] == di["hash_crc32"]:
|
||||
db = header["db"]
|
||||
s += "\n== Database Match ==\n"
|
||||
if "gn" in db and "ne" in db: s += "* Game Name: {:s} {:s}\n".format(db["gn"], db["ne"])
|
||||
elif "gn" in db: s += "* Game Name: {:s}\n".format(db["gn"])
|
||||
if "rg" in db: s += "* Region: {:s}\n".format(db["rg"])
|
||||
if "lg" in db: s += "* Language(s): {:s}\n".format(db["lg"])
|
||||
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 mode == "AGB":
|
||||
di["header"]["game_code_raw"] = di["header"]["game_code_raw"].replace("\0", "␀")
|
||||
di["hdr_header_checksum"] = "OK (0x{:02X})".format(di["header"]["header_checksum"]) if di["header"]["header_checksum_correct"] else "Invalid (0x{:02X}≠0x{:02X})".format(di["header"]["header_checksum_calc"], di["header"]["header_checksum"])
|
||||
header["game_code_raw"] = header["game_code_raw"].replace("\0", "␀")
|
||||
di["hdr_header_checksum"] = "OK (0x{:02X})".format(header["header_checksum"]) if header["header_checksum_correct"] else "Invalid (0x{:02X}≠0x{:02X})".format(header["header_checksum_calc"], header["header_checksum"])
|
||||
if "agb_savelib" not in di:
|
||||
di["agb_savelib"] = "None"
|
||||
elif "SRAM_F_" in di["agb_savelib"]:
|
||||
|
|
@ -652,11 +686,25 @@ def GetDumpReport(di, device):
|
|||
"* Nintendo Logo: {hdr_logo:s}\n" \
|
||||
"* Header Checksum: {hdr_header_checksum:s}\n" \
|
||||
"* Save Type: {agb_savetype}\n" \
|
||||
.format(hdr_game_title=di["header"]["game_title_raw"], hdr_game_code=di["header"]["game_code_raw"], hdr_revision=str(di["header"]["version"]), hdr_logo=di["hdr_logo"], hdr_header_checksum=di["hdr_header_checksum"], agb_savetype=di["agb_savelib"])
|
||||
.format(hdr_game_title=header["game_title_raw"], hdr_game_code=header["game_code_raw"], hdr_revision=str(header["version"]), hdr_logo=di["hdr_logo"], hdr_header_checksum=di["hdr_header_checksum"], agb_savetype=di["agb_savelib"])
|
||||
if "agb_save_flash_id" in di and di["agb_save_flash_id"] is not None:
|
||||
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 header["db"] is not None and header["db"]["rc"] == di["hash_crc32"]:
|
||||
db = header["db"]
|
||||
s += "\n== Database Match ==\n"
|
||||
if "gn" in db and "ne" in db: s += "* Game Name: {:s} {:s}\n".format(db["gn"], db["ne"])
|
||||
elif "gn" in db: s += "* Game Name: {:s}\n".format(db["gn"])
|
||||
if "rg" in db: s += "* Region: {:s}\n".format(db["rg"])
|
||||
if "lg" in db: s += "* Language(s): {:s}\n".format(db["lg"])
|
||||
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 "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))
|
||||
|
||||
return s
|
||||
|
||||
|
|
@ -717,8 +765,11 @@ def GenerateFileName(mode, header, settings):
|
|||
path = re.sub(r"[<>:\"/\\|\?\*]", "_", path)
|
||||
path += "." + path_extension
|
||||
|
||||
if fe_ni and header["db"] is not None and (mode != "DMG" or get_mbc_name(header["mapper_raw"]) != "G-MMC1"):
|
||||
path = "{:s} {:s}.{:s}".format(header["db"]["gn"], header["db"]["ne"], path_extension)
|
||||
if fe_ni and header["db"] is not None:
|
||||
if mode == "DMG" and get_mbc_name(header["mapper_raw"]) == "G-MMC1":
|
||||
path = "{:s}.{:s}".format(header["db"]["gn"], path_extension)
|
||||
else:
|
||||
path = "{:s} {:s}.{:s}".format(header["db"]["gn"], header["db"]["ne"], path_extension)
|
||||
|
||||
return path
|
||||
|
||||
|
|
|
|||
|
|
@ -32122,8 +32122,8 @@
|
|||
"gn": "Daigassou! Band-Brothers - Request Selection",
|
||||
"ne": "(Japan) (DS Expansion Cartridge)",
|
||||
"gc": "ZBBJ",
|
||||
"rc": 3284246738,
|
||||
"rs": 4194304,
|
||||
"rc": 927851296,
|
||||
"rs": 1048576,
|
||||
"st": 0,
|
||||
"ss": 0,
|
||||
"lg": "Ja",
|
||||
|
|
@ -32155,8 +32155,8 @@
|
|||
"gn": "Nintendo MP3 Player",
|
||||
"ne": "(Europe) (En,Fr,De,Es,It)",
|
||||
"gc": "ZMDE",
|
||||
"rc": 2256268406,
|
||||
"rs": 4194304,
|
||||
"rc": 384159986,
|
||||
"rs": 1048576,
|
||||
"st": 0,
|
||||
"ss": 0,
|
||||
"lg": "En,Fr,De,Es,It",
|
||||
|
|
|
|||
96
FlashGBX/config/fc_DMG_AM29LV160DT.txt
Normal file
96
FlashGBX/config/fc_DMG_AM29LV160DT.txt
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
"type":"DMG",
|
||||
"names":[
|
||||
"DRV with AM29LV160DT and ALTERA CPLD"
|
||||
],
|
||||
"flash_ids":[
|
||||
[ 0x02, 0x02, 0xC4, 0xC4 ]
|
||||
],
|
||||
"voltage":3.3,
|
||||
"flash_size":0x200000,
|
||||
"start_addr":0x4000,
|
||||
"first_bank":0,
|
||||
"write_pin":"WR",
|
||||
"sector_size":[
|
||||
[0x10000, 31],
|
||||
[0x8000, 1],
|
||||
[0x2000, 2],
|
||||
[0x4000, 1]
|
||||
],
|
||||
"chip_erase_timeout":120,
|
||||
"mbc":"manual",
|
||||
"command_set":"AMD",
|
||||
"commands":{
|
||||
"reset":[
|
||||
[ 0x4000, 0xF0 ]
|
||||
],
|
||||
"read_identifier":[
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ 0x4AAA, 0x90 ]
|
||||
],
|
||||
"read_cfi":[
|
||||
[ 0x4AAA, 0x98 ]
|
||||
],
|
||||
"chip_erase":[
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ 0x4AAA, 0x80 ],
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ 0x4AAA, 0x10 ]
|
||||
],
|
||||
"chip_erase_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ 0, 0xFF, 0xFF ]
|
||||
],
|
||||
"sector_erase":[
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ 0x4AAA, 0x80 ],
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ "SA", 0x30 ]
|
||||
],
|
||||
"sector_erase_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ "SA", 0xFF, 0xFF ]
|
||||
],
|
||||
"buffer_write":[
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ "SA", 0x26 ],
|
||||
[ "SA", "BS" ],
|
||||
[ "PA", "PD" ],
|
||||
[ "SA", 0x2A ]
|
||||
],
|
||||
"buffer_write_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ "SA", "PD", 0xFFFF ]
|
||||
],
|
||||
"single_write":[
|
||||
[ 0x4AAA, 0xA9 ],
|
||||
[ 0x4555, 0x56 ],
|
||||
[ 0x4AAA, 0xA0 ],
|
||||
[ "PA", "PD" ]
|
||||
],
|
||||
"single_write_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
# Author: Lesserkuma (github.com/lesserkuma)
|
||||
|
||||
import zipfile, serial, struct, time, random, hashlib, datetime
|
||||
from .pyside import QtCore, QtWidgets, QtGui, QDesktopWidget
|
||||
try:
|
||||
from . import Util
|
||||
except ImportError:
|
||||
|
|
@ -85,255 +84,259 @@ class FirmwareUpdater():
|
|||
time.sleep(0.2)
|
||||
return 1
|
||||
|
||||
class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||
APP = None
|
||||
DEVICE = None
|
||||
FWUPD = None
|
||||
DEV_NAME = "GBxCart RW"
|
||||
FW_VER = ""
|
||||
PCB_VER = ""
|
||||
try:
|
||||
from .pyside import QtCore, QtWidgets, QtGui, QDesktopWidget
|
||||
class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||
APP = None
|
||||
DEVICE = None
|
||||
FWUPD = None
|
||||
DEV_NAME = "GBxCart RW"
|
||||
FW_VER = ""
|
||||
PCB_VER = ""
|
||||
|
||||
def __init__(self, app, app_path, file=None, icon=None, device=None):
|
||||
QtWidgets.QDialog.__init__(self)
|
||||
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
|
||||
self.setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }")
|
||||
self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW v1.4")
|
||||
self.setWindowFlags((self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint) & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||
def __init__(self, app, app_path, file=None, icon=None, device=None):
|
||||
QtWidgets.QDialog.__init__(self)
|
||||
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
|
||||
self.setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }")
|
||||
self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW v1.4")
|
||||
self.setWindowFlags((self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint) & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||
|
||||
self.APP = app
|
||||
if device is not None:
|
||||
self.FWUPD = FirmwareUpdater(app_path, device.GetPort())
|
||||
self.DEV_NAME = device.GetName()
|
||||
self.FW_VER = device.GetFirmwareVersion(more=True)
|
||||
self.PCB_VER = device.GetPCBVersion()
|
||||
self.DEVICE = device
|
||||
else:
|
||||
self.APP.QT_APP.processEvents()
|
||||
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)
|
||||
self.APP = app
|
||||
if device is not None:
|
||||
self.FWUPD = FirmwareUpdater(app_path, device.GetPort())
|
||||
self.DEV_NAME = device.GetName()
|
||||
self.FW_VER = device.GetFirmwareVersion(more=True)
|
||||
self.PCB_VER = device.GetPCBVersion()
|
||||
self.DEVICE = device
|
||||
else:
|
||||
self.APP.QT_APP.processEvents()
|
||||
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()
|
||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||
self.FWUPD = FirmwareUpdater(app_path, None)
|
||||
|
||||
self.layout = QtWidgets.QGridLayout()
|
||||
self.layout.setContentsMargins(-1, 8, -1, 8)
|
||||
self.layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||
self.layout_device = QtWidgets.QVBoxLayout()
|
||||
|
||||
# ↓↓↓ Current Device Information
|
||||
self.grpDeviceInfo = QtWidgets.QGroupBox("Current Firmware")
|
||||
self.grpDeviceInfo.setMinimumWidth(420)
|
||||
self.grpDeviceInfoLayout = QtWidgets.QVBoxLayout()
|
||||
self.grpDeviceInfoLayout.setContentsMargins(-1, 3, -1, -1)
|
||||
rowDeviceInfo1 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceName = QtWidgets.QLabel("Device:")
|
||||
self.lblDeviceName.setMinimumWidth(120)
|
||||
self.lblDeviceNameResult = QtWidgets.QLabel("GBxCart RW")
|
||||
rowDeviceInfo1.addWidget(self.lblDeviceName)
|
||||
rowDeviceInfo1.addWidget(self.lblDeviceNameResult)
|
||||
rowDeviceInfo1.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo1)
|
||||
rowDeviceInfo3 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceFWVer = QtWidgets.QLabel("Firmware version:")
|
||||
self.lblDeviceFWVer.setMinimumWidth(120)
|
||||
self.lblDeviceFWVerResult = QtWidgets.QLabel("R30+L1")
|
||||
rowDeviceInfo3.addWidget(self.lblDeviceFWVer)
|
||||
rowDeviceInfo3.addWidget(self.lblDeviceFWVerResult)
|
||||
rowDeviceInfo3.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo3)
|
||||
rowDeviceInfo2 = QtWidgets.QHBoxLayout()
|
||||
self.lblDevicePCBVer = QtWidgets.QLabel("PCB version:")
|
||||
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.connect(self.optDevicePCBVer14a, QtCore.SIGNAL("clicked()"), self.SetPCBVersion)
|
||||
rowDeviceInfo2.addWidget(self.lblDevicePCBVer)
|
||||
rowDeviceInfo2.addWidget(self.optDevicePCBVer14)
|
||||
rowDeviceInfo2.addWidget(self.optDevicePCBVer14a)
|
||||
rowDeviceInfo2.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo2)
|
||||
self.grpDeviceInfo.setLayout(self.grpDeviceInfoLayout)
|
||||
self.layout_device.addWidget(self.grpDeviceInfo)
|
||||
# ↑↑↑ Current Device Information
|
||||
|
||||
# ↓↓↓ Available Firmware Updates
|
||||
self.grpAvailableFwUpdates = QtWidgets.QGroupBox("Available Firmware")
|
||||
self.grpAvailableFwUpdates.setMinimumWidth(400)
|
||||
self.grpAvailableFwUpdatesLayout = QtWidgets.QVBoxLayout()
|
||||
self.grpAvailableFwUpdatesLayout.setContentsMargins(-1, 3, -1, -1)
|
||||
|
||||
rowDeviceInfo4 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceFWVer2 = QtWidgets.QLabel("Firmware version:")
|
||||
self.lblDeviceFWVer2.setMinimumWidth(120)
|
||||
self.lblDeviceFWVer2Result = QtWidgets.QLabel("(Please choose the PCB version)")
|
||||
rowDeviceInfo4.addWidget(self.lblDeviceFWVer2)
|
||||
rowDeviceInfo4.addWidget(self.lblDeviceFWVer2Result)
|
||||
rowDeviceInfo4.addStretch(1)
|
||||
self.grpAvailableFwUpdatesLayout.addLayout(rowDeviceInfo4)
|
||||
|
||||
self.rowUpdate = QtWidgets.QHBoxLayout()
|
||||
self.btnUpdate = QtWidgets.QPushButton("Install Firmware Update")
|
||||
self.btnUpdate.setMinimumWidth(200)
|
||||
self.btnUpdate.setContentsMargins(20, 20, 20, 20)
|
||||
self.connect(self.btnUpdate, QtCore.SIGNAL("clicked()"), lambda: [ self.UpdateFirmware() ])
|
||||
self.rowUpdate.addStretch()
|
||||
self.rowUpdate.addWidget(self.btnUpdate)
|
||||
self.rowUpdate.addStretch()
|
||||
|
||||
self.grpAvailableFwUpdatesLayout.addSpacing(3)
|
||||
self.grpAvailableFwUpdatesLayout.addItem(self.rowUpdate)
|
||||
self.grpAvailableFwUpdates.setLayout(self.grpAvailableFwUpdatesLayout)
|
||||
self.layout_device.addWidget(self.grpAvailableFwUpdates)
|
||||
# ↑↑↑ Available Firmware Updates
|
||||
|
||||
self.grpStatus = QtWidgets.QGroupBox("")
|
||||
self.grpStatusLayout = QtWidgets.QGridLayout()
|
||||
self.prgStatus = QtWidgets.QProgressBar()
|
||||
self.prgStatus.setMinimum(0)
|
||||
self.prgStatus.setMaximum(1000)
|
||||
self.prgStatus.setValue(0)
|
||||
self.lblStatus = QtWidgets.QLabel("Ready.")
|
||||
|
||||
self.grpStatusLayout.addWidget(self.prgStatus, 1, 0)
|
||||
self.grpStatusLayout.addWidget(self.lblStatus, 2, 0)
|
||||
|
||||
self.grpStatus.setLayout(self.grpStatusLayout)
|
||||
self.layout_device.addWidget(self.grpStatus)
|
||||
|
||||
self.grpFooterLayout = QtWidgets.QHBoxLayout()
|
||||
self.btnClose = QtWidgets.QPushButton("&Close")
|
||||
self.connect(self.btnClose, QtCore.SIGNAL("clicked()"), lambda: [ self.reject() ])
|
||||
self.grpFooterLayout.addStretch()
|
||||
self.grpFooterLayout.addWidget(self.btnClose)
|
||||
self.layout_device.addItem(self.grpFooterLayout)
|
||||
|
||||
self.layout.addLayout(self.layout_device, 0, 0)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.lblDeviceNameResult.setText(self.DEV_NAME)
|
||||
self.lblDeviceFWVerResult.setText(self.FW_VER)
|
||||
if self.PCB_VER == "v1.4":
|
||||
self.optDevicePCBVer14.setChecked(True)
|
||||
elif self.PCB_VER == "v1.4a":
|
||||
self.optDevicePCBVer14a.setChecked(True)
|
||||
self.SetPCBVersion()
|
||||
|
||||
def SetPCBVersion(self):
|
||||
if self.optDevicePCBVer14.isChecked():
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip"
|
||||
elif self.optDevicePCBVer14a.isChecked():
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4a.zip"
|
||||
else:
|
||||
return
|
||||
|
||||
with zipfile.ZipFile(file_name) as zip:
|
||||
with zip.open("fw.ini") as f: ini_file = f.read()
|
||||
ini_file = ini_file.decode(encoding="utf-8")
|
||||
self.INI = Util.IniSettings(ini=ini_file, main_section="Firmware")
|
||||
self.OFW_VER = self.INI.GetValue("fw_ver")
|
||||
self.OFW_BUILDTS = self.INI.GetValue("fw_buildts")
|
||||
self.OFW_TEXT = self.INI.GetValue("fw_text")
|
||||
|
||||
self.lblDeviceFWVer2Result.setText("{:s} ({:s})".format(self.OFW_VER, datetime.datetime.fromtimestamp(int(self.OFW_BUILDTS)).astimezone().replace(microsecond=0).isoformat()))
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.layout.update()
|
||||
self.layout.activate()
|
||||
screenGeometry = QDesktopWidget().screenGeometry(self)
|
||||
x = (screenGeometry.width() - self.width()) / 2
|
||||
y = (screenGeometry.height() - self.height()) / 2
|
||||
self.move(x, y)
|
||||
self.show()
|
||||
except:
|
||||
return
|
||||
|
||||
def hideEvent(self, event):
|
||||
if self.DEVICE is None:
|
||||
self.APP.ConnectDevice()
|
||||
self.APP.activateWindow()
|
||||
|
||||
def reject(self):
|
||||
if self.CloseDialog():
|
||||
super().reject()
|
||||
|
||||
def CloseDialog(self):
|
||||
if self.btnClose.isEnabled() is False:
|
||||
text = "<b>WARNING:</b> If you close this window while a firmware update is still running, it may leave the device in an unbootable state. You can still recover it by running the Firmware Updater again later.<br><br>Are you sure you want to close this window?"
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.No: return False
|
||||
return True
|
||||
|
||||
def UpdateFirmware(self):
|
||||
if self.optDevicePCBVer14.isChecked():
|
||||
device_name = "v1.4"
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip"
|
||||
led = "Done"
|
||||
elif self.optDevicePCBVer14a.isChecked():
|
||||
device_name = "v1.4a"
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4a.zip"
|
||||
led = "Status"
|
||||
else:
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="Please select the PCB version of your GBxCart RW device.", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
|
||||
self.APP.DisconnectDevice()
|
||||
|
||||
text = "Please follow these steps to proceed with the firmware update:<ol><li>Disconnect the USB cable of your GBxCart RW {dev_name:s} device.</li><li>On the circuit board of your GBxCart RW {dev_name:s}, press and hold down the small button while connecting the USB cable again.</li><li>Keep the small button held for at least 2 seconds, then let go of it. If done right, the green LED labeled “{led:s}” should remain lit.</li><li>Click OK to continue.</li></ol>".format(dev_name=device_name, led=led)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
msgbox.setTextFormat(QtCore.Qt.TextFormat.RichText)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||
self.FWUPD = FirmwareUpdater(app_path, None)
|
||||
|
||||
self.layout = QtWidgets.QGridLayout()
|
||||
self.layout.setContentsMargins(-1, 8, -1, 8)
|
||||
self.layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||
self.layout_device = QtWidgets.QVBoxLayout()
|
||||
self.btnUpdate.setEnabled(False)
|
||||
self.btnClose.setEnabled(False)
|
||||
self.optDevicePCBVer14.setEnabled(False)
|
||||
self.optDevicePCBVer14a.setEnabled(False)
|
||||
|
||||
while True:
|
||||
ret = self.FWUPD.WriteFirmware(file_name, self.SetStatus)
|
||||
if ret == 1:
|
||||
text = "The firmware update is complete!"
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
self.DEVICE = None
|
||||
self.reject()
|
||||
return True
|
||||
elif ret == 2:
|
||||
text = "The firmware update has failed. Please try again."
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
elif ret == 3:
|
||||
text = "The firmware update file is corrupted. Please re-install the application."
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
|
||||
# ↓↓↓ Current Device Information
|
||||
self.grpDeviceInfo = QtWidgets.QGroupBox("Current Firmware")
|
||||
self.grpDeviceInfo.setMinimumWidth(420)
|
||||
self.grpDeviceInfoLayout = QtWidgets.QVBoxLayout()
|
||||
self.grpDeviceInfoLayout.setContentsMargins(-1, 3, -1, -1)
|
||||
rowDeviceInfo1 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceName = QtWidgets.QLabel("Device:")
|
||||
self.lblDeviceName.setMinimumWidth(120)
|
||||
self.lblDeviceNameResult = QtWidgets.QLabel("GBxCart RW")
|
||||
rowDeviceInfo1.addWidget(self.lblDeviceName)
|
||||
rowDeviceInfo1.addWidget(self.lblDeviceNameResult)
|
||||
rowDeviceInfo1.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo1)
|
||||
rowDeviceInfo3 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceFWVer = QtWidgets.QLabel("Firmware version:")
|
||||
self.lblDeviceFWVer.setMinimumWidth(120)
|
||||
self.lblDeviceFWVerResult = QtWidgets.QLabel("R30+L1")
|
||||
rowDeviceInfo3.addWidget(self.lblDeviceFWVer)
|
||||
rowDeviceInfo3.addWidget(self.lblDeviceFWVerResult)
|
||||
rowDeviceInfo3.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo3)
|
||||
rowDeviceInfo2 = QtWidgets.QHBoxLayout()
|
||||
self.lblDevicePCBVer = QtWidgets.QLabel("PCB version:")
|
||||
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.connect(self.optDevicePCBVer14a, QtCore.SIGNAL("clicked()"), self.SetPCBVersion)
|
||||
rowDeviceInfo2.addWidget(self.lblDevicePCBVer)
|
||||
rowDeviceInfo2.addWidget(self.optDevicePCBVer14)
|
||||
rowDeviceInfo2.addWidget(self.optDevicePCBVer14a)
|
||||
rowDeviceInfo2.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo2)
|
||||
self.grpDeviceInfo.setLayout(self.grpDeviceInfoLayout)
|
||||
self.layout_device.addWidget(self.grpDeviceInfo)
|
||||
# ↑↑↑ Current Device Information
|
||||
|
||||
# ↓↓↓ Available Firmware Updates
|
||||
self.grpAvailableFwUpdates = QtWidgets.QGroupBox("Available Firmware")
|
||||
self.grpAvailableFwUpdates.setMinimumWidth(400)
|
||||
self.grpAvailableFwUpdatesLayout = QtWidgets.QVBoxLayout()
|
||||
self.grpAvailableFwUpdatesLayout.setContentsMargins(-1, 3, -1, -1)
|
||||
|
||||
rowDeviceInfo4 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceFWVer2 = QtWidgets.QLabel("Firmware version:")
|
||||
self.lblDeviceFWVer2.setMinimumWidth(120)
|
||||
self.lblDeviceFWVer2Result = QtWidgets.QLabel("(Please choose the PCB version)")
|
||||
rowDeviceInfo4.addWidget(self.lblDeviceFWVer2)
|
||||
rowDeviceInfo4.addWidget(self.lblDeviceFWVer2Result)
|
||||
rowDeviceInfo4.addStretch(1)
|
||||
self.grpAvailableFwUpdatesLayout.addLayout(rowDeviceInfo4)
|
||||
|
||||
self.rowUpdate = QtWidgets.QHBoxLayout()
|
||||
self.btnUpdate = QtWidgets.QPushButton("Install Firmware Update")
|
||||
self.btnUpdate.setMinimumWidth(200)
|
||||
self.btnUpdate.setContentsMargins(20, 20, 20, 20)
|
||||
self.connect(self.btnUpdate, QtCore.SIGNAL("clicked()"), lambda: [ self.UpdateFirmware() ])
|
||||
self.rowUpdate.addStretch()
|
||||
self.rowUpdate.addWidget(self.btnUpdate)
|
||||
self.rowUpdate.addStretch()
|
||||
|
||||
self.grpAvailableFwUpdatesLayout.addSpacing(3)
|
||||
self.grpAvailableFwUpdatesLayout.addItem(self.rowUpdate)
|
||||
self.grpAvailableFwUpdates.setLayout(self.grpAvailableFwUpdatesLayout)
|
||||
self.layout_device.addWidget(self.grpAvailableFwUpdates)
|
||||
# ↑↑↑ Available Firmware Updates
|
||||
|
||||
self.grpStatus = QtWidgets.QGroupBox("")
|
||||
self.grpStatusLayout = QtWidgets.QGridLayout()
|
||||
self.prgStatus = QtWidgets.QProgressBar()
|
||||
self.prgStatus.setMinimum(0)
|
||||
self.prgStatus.setMaximum(1000)
|
||||
self.prgStatus.setValue(0)
|
||||
self.lblStatus = QtWidgets.QLabel("Ready.")
|
||||
|
||||
self.grpStatusLayout.addWidget(self.prgStatus, 1, 0)
|
||||
self.grpStatusLayout.addWidget(self.lblStatus, 2, 0)
|
||||
|
||||
self.grpStatus.setLayout(self.grpStatusLayout)
|
||||
self.layout_device.addWidget(self.grpStatus)
|
||||
|
||||
self.grpFooterLayout = QtWidgets.QHBoxLayout()
|
||||
self.btnClose = QtWidgets.QPushButton("&Close")
|
||||
self.connect(self.btnClose, QtCore.SIGNAL("clicked()"), lambda: [ self.reject() ])
|
||||
self.grpFooterLayout.addStretch()
|
||||
self.grpFooterLayout.addWidget(self.btnClose)
|
||||
self.layout_device.addItem(self.grpFooterLayout)
|
||||
|
||||
self.layout.addLayout(self.layout_device, 0, 0)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.lblDeviceNameResult.setText(self.DEV_NAME)
|
||||
self.lblDeviceFWVerResult.setText(self.FW_VER)
|
||||
if self.PCB_VER == "v1.4":
|
||||
self.optDevicePCBVer14.setChecked(True)
|
||||
elif self.PCB_VER == "v1.4a":
|
||||
self.optDevicePCBVer14a.setChecked(True)
|
||||
self.SetPCBVersion()
|
||||
|
||||
def SetPCBVersion(self):
|
||||
if self.optDevicePCBVer14.isChecked():
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip"
|
||||
elif self.optDevicePCBVer14a.isChecked():
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4a.zip"
|
||||
else:
|
||||
return
|
||||
|
||||
with zipfile.ZipFile(file_name) as zip:
|
||||
with zip.open("fw.ini") as f: ini_file = f.read()
|
||||
ini_file = ini_file.decode(encoding="utf-8")
|
||||
self.INI = Util.IniSettings(ini=ini_file, main_section="Firmware")
|
||||
self.OFW_VER = self.INI.GetValue("fw_ver")
|
||||
self.OFW_BUILDTS = self.INI.GetValue("fw_buildts")
|
||||
self.OFW_TEXT = self.INI.GetValue("fw_text")
|
||||
|
||||
self.lblDeviceFWVer2Result.setText("{:s} ({:s})".format(self.OFW_VER, datetime.datetime.fromtimestamp(int(self.OFW_BUILDTS)).astimezone().replace(microsecond=0).isoformat()))
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.layout.update()
|
||||
self.layout.activate()
|
||||
screenGeometry = QDesktopWidget().screenGeometry(self)
|
||||
x = (screenGeometry.width() - self.width()) / 2
|
||||
y = (screenGeometry.height() - self.height()) / 2
|
||||
self.move(x, y)
|
||||
self.show()
|
||||
except:
|
||||
return
|
||||
|
||||
def hideEvent(self, event):
|
||||
if self.DEVICE is None:
|
||||
self.APP.ConnectDevice()
|
||||
self.APP.activateWindow()
|
||||
|
||||
def reject(self):
|
||||
if self.CloseDialog():
|
||||
super().reject()
|
||||
|
||||
def CloseDialog(self):
|
||||
if self.btnClose.isEnabled() is False:
|
||||
text = "<b>WARNING:</b> If you close this window while a firmware update is still running, it may leave the device in an unbootable state. You can still recover it by running the Firmware Updater again later.<br><br>Are you sure you want to close this window?"
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.No: return False
|
||||
return True
|
||||
|
||||
def UpdateFirmware(self):
|
||||
if self.optDevicePCBVer14.isChecked():
|
||||
device_name = "v1.4"
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip"
|
||||
led = "Done"
|
||||
elif self.optDevicePCBVer14a.isChecked():
|
||||
device_name = "v1.4a"
|
||||
file_name = self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4a.zip"
|
||||
led = "Status"
|
||||
else:
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="Please select the PCB version of your GBxCart RW device.", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
|
||||
self.APP.DisconnectDevice()
|
||||
|
||||
text = "Please follow these steps to proceed with the firmware update:<ol><li>Disconnect the USB cable of your GBxCart RW {dev_name:s} device.</li><li>On the circuit board of your GBxCart RW {dev_name:s}, press and hold down the small button while connecting the USB cable again.</li><li>Keep the small button held for at least 2 seconds, then let go of it. If done right, the green LED labeled “{led:s}” should remain lit.</li><li>Click OK to continue.</li></ol>".format(dev_name=device_name, led=led)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
msgbox.setTextFormat(QtCore.Qt.TextFormat.RichText)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||
self.btnUpdate.setEnabled(False)
|
||||
self.btnClose.setEnabled(False)
|
||||
self.optDevicePCBVer14.setEnabled(False)
|
||||
self.optDevicePCBVer14a.setEnabled(False)
|
||||
|
||||
while True:
|
||||
ret = self.FWUPD.WriteFirmware(file_name, self.SetStatus)
|
||||
if ret == 1:
|
||||
text = "The firmware update is complete!"
|
||||
def SetStatus(self, text, enableUI=False, setProgress=None):
|
||||
self.lblStatus.setText("Status: {:s}".format(text))
|
||||
if setProgress is not None:
|
||||
self.prgStatus.setValue(setProgress * 10)
|
||||
if enableUI:
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
self.DEVICE = None
|
||||
self.reject()
|
||||
return True
|
||||
elif ret == 2:
|
||||
text = "The firmware update has failed. Please try again."
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
elif ret == 3:
|
||||
text = "The firmware update file is corrupted. Please re-install the application."
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
|
||||
def SetStatus(self, text, enableUI=False, setProgress=None):
|
||||
self.lblStatus.setText("Status: {:s}".format(text))
|
||||
if setProgress is not None:
|
||||
self.prgStatus.setValue(setProgress * 10)
|
||||
if enableUI:
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.optDevicePCBVer14.setEnabled(True)
|
||||
self.optDevicePCBVer14a.setEnabled(True)
|
||||
self.APP.QT_APP.processEvents()
|
||||
self.APP.QT_APP.processEvents()
|
||||
except ImportError:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ from . import Util
|
|||
class GbxDevice:
|
||||
DEVICE_NAME = "GBxCart RW"
|
||||
DEVICE_MIN_FW = 1
|
||||
DEVICE_MAX_FW = 8
|
||||
DEVICE_LATEST_FW_TS = { 4:1619427330, 5:1680535740, 6:1680535740 }
|
||||
DEVICE_MAX_FW = 9
|
||||
DEVICE_LATEST_FW_TS = { 4:1619427330, 5:1681395695, 6:1681395696 }
|
||||
|
||||
DEVICE_CMD = {
|
||||
"NULL":0x30,
|
||||
|
|
@ -69,6 +69,7 @@ class GbxDevice:
|
|||
"AGB_FLASH_WRITE_SHORT":0xD2,
|
||||
"FLASH_PROGRAM":0xD3,
|
||||
"CART_WRITE_FLASH_CMD":0xD4,
|
||||
"CALC_CRC32":0xD5,
|
||||
}
|
||||
# \#define VAR(\d+)_([^\t]+)\t+(.+)
|
||||
DEVICE_VAR = {
|
||||
|
|
@ -280,9 +281,9 @@ class GbxDevice:
|
|||
|
||||
def IsSupportedMbc(self, mbc):
|
||||
if self.CanPowerCycleCart():
|
||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x06, 0x0B, 0x0D, 0x10, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x201, 0x202, 0x203, 0x204, 0x205 )
|
||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x201, 0x202, 0x203, 0x204, 0x205 )
|
||||
else:
|
||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x06, 0x0B, 0x0D, 0x10, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x202, 0x205 )
|
||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x202, 0x205 )
|
||||
|
||||
def IsSupported3dMemory(self):
|
||||
return True
|
||||
|
|
@ -454,7 +455,11 @@ class GbxDevice:
|
|||
if self.DEVICE.in_waiting > 1000: dprint("in_waiting = {:d} bytes".format(self.DEVICE.in_waiting))
|
||||
buffer = self.DEVICE.read(count)
|
||||
if len(buffer) != count:
|
||||
dprint("Warning: Received {:d} byte(s) instead of the expected {:d} byte(s)".format(len(buffer), count))
|
||||
tb_stack = traceback.extract_stack()
|
||||
stack = tb_stack[len(tb_stack)-2] # caller only
|
||||
if stack.name == "_read": stack = tb_stack[len(tb_stack)-3]
|
||||
#traceback.print_stack()
|
||||
dprint("Warning: Received {:d} byte(s) instead of the expected {:d} byte(s) ({:s}(), line {:d})".format(len(buffer), count, stack.name, stack.lineno))
|
||||
self.READ_ERRORS += 1
|
||||
while self.DEVICE.in_waiting > 0:
|
||||
self.DEVICE.reset_input_buffer()
|
||||
|
|
@ -591,51 +596,27 @@ class GbxDevice:
|
|||
def CartPowerCycle(self, delay=0.1):
|
||||
if self.CanPowerCycleCart():
|
||||
dprint("Power cycling cartridge with a delay of {:.1f} seconds".format(delay))
|
||||
try:
|
||||
self.CartPowerOff(delay=delay)
|
||||
self.CartPowerOn(delay=delay)
|
||||
if self.MODE == "DMG":
|
||||
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
||||
elif self.MODE == "AGB":
|
||||
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
||||
except:
|
||||
if self.DEVICE is None:
|
||||
print("The USB connection was lost after power cycling")
|
||||
else:
|
||||
print("An unknown error occured after power cycling")
|
||||
return False
|
||||
self.CartPowerOff(delay=delay)
|
||||
self.CartPowerOn(delay=delay)
|
||||
if self.MODE == "DMG":
|
||||
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
||||
elif self.MODE == "AGB":
|
||||
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
||||
|
||||
def CartPowerOff(self, delay=0.1):
|
||||
if not self.IsConnected(): return
|
||||
if self.FW["pcb_ver"] in (5, 6):
|
||||
self._write(self.DEVICE_CMD["OFW_QUERY_CART_PWR"])
|
||||
if self._read(1) == 1:
|
||||
dprint("Turning off cartridge power ({:s})".format(self.MODE))
|
||||
self._write(self.DEVICE_CMD["OFW_CART_PWR_OFF"])
|
||||
time.sleep(delay)
|
||||
while True:
|
||||
self._write(self.DEVICE_CMD["OFW_QUERY_CART_PWR"])
|
||||
if self._read(1) == 0: return
|
||||
self._write(self.DEVICE_CMD["OFW_CART_PWR_OFF"])
|
||||
time.sleep(delay)
|
||||
else:
|
||||
self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"])
|
||||
|
||||
def CartPowerOn(self, delay=0.1):
|
||||
if not self.IsConnected(): return
|
||||
if self.FW["pcb_ver"] in (5, 6):
|
||||
self._write(self.DEVICE_CMD["OFW_QUERY_CART_PWR"])
|
||||
if self._read(1) == 0:
|
||||
dprint("Turning on cartridge power ({:s})".format(self.MODE))
|
||||
self._write(self.DEVICE_CMD["OFW_CART_PWR_ON"])
|
||||
time.sleep(delay)
|
||||
self._write(self.DEVICE_CMD["OFW_QUERY_CART_PWR"])
|
||||
while True:
|
||||
temp = self._read(1)
|
||||
if temp is False:
|
||||
self.DEVICE.reset_input_buffer()
|
||||
self.DEVICE.reset_output_buffer()
|
||||
return
|
||||
elif temp == 1:
|
||||
return
|
||||
self.DEVICE.reset_input_buffer() # bug workaround
|
||||
|
||||
def GetMode(self):
|
||||
return self.MODE
|
||||
|
|
@ -726,20 +707,21 @@ class GbxDevice:
|
|||
header[0xD0:0xE0] = self.ReadROM(0x40D0, 0x10)
|
||||
data = RomFileDMG(header).GetHeader()
|
||||
|
||||
_mbc = DMG_MBC().GetInstance(args={"mbc":data["mapper_raw"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
||||
if checkRtc:
|
||||
data["has_rtc"] = _mbc.HasRTC() is True
|
||||
if data["has_rtc"] is True:
|
||||
if _mbc.GetName() == "TAMA5": _mbc.EnableMapper()
|
||||
_mbc.LatchRTC()
|
||||
data["rtc_buffer"] = _mbc.ReadRTC()
|
||||
if _mbc.GetName() == "TAMA5": self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
||||
else:
|
||||
data["has_rtc"] = False
|
||||
try:
|
||||
data["rtc_string"] = _mbc.GetRTCString()
|
||||
except:
|
||||
data["rtc_string"] = "Invalid data"
|
||||
data["has_rtc"] = False
|
||||
data["rtc_string"] = "Not available"
|
||||
if data["logo_correct"] is True:
|
||||
_mbc = DMG_MBC().GetInstance(args={"mbc":data["mapper_raw"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
||||
if checkRtc:
|
||||
data["has_rtc"] = _mbc.HasRTC() is True
|
||||
if data["has_rtc"] is True:
|
||||
if _mbc.GetName() == "TAMA5": _mbc.EnableMapper()
|
||||
_mbc.LatchRTC()
|
||||
data["rtc_buffer"] = _mbc.ReadRTC()
|
||||
if _mbc.GetName() == "TAMA5": self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
||||
try:
|
||||
data["rtc_string"] = _mbc.GetRTCString()
|
||||
except:
|
||||
data["rtc_string"] = "Invalid data"
|
||||
|
||||
elif self.MODE == "AGB":
|
||||
# Unlock DACS carts on older firmware
|
||||
|
|
@ -2201,7 +2183,31 @@ class GbxDevice:
|
|||
if (self.MODE == "AGB" and "command_set" in cart_type and cart_type["command_set"] == "3DMEMORY"):
|
||||
temp = self.ReadROM_3DMemory(address=pos, length=buffer_len, max_length=max_length)
|
||||
else:
|
||||
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
|
||||
if self.FW["fw_ver"] >= 9 and "verify_write" in args and (self.MODE != "AGB" or args["verify_base_pos"] > 0xC9):
|
||||
# Verify mode (by checksum)
|
||||
dprint("Checksum verification (verify_base_pos=0x{:X}, pos=0x{:X}, pos_total=0x{:X}, buffer_len=0x{:X})".format(args["verify_base_pos"], pos, pos_total, buffer_len))
|
||||
if self.MODE == "DMG":
|
||||
self._set_fw_variable("ADDRESS", pos)
|
||||
elif self.MODE == "AGB":
|
||||
self._set_fw_variable("ADDRESS", pos >> 1)
|
||||
self._write(self.DEVICE_CMD["CALC_CRC32"])
|
||||
self._write(bytearray(struct.pack(">I", buffer_len)))
|
||||
checksum_expected = zlib.crc32(args["verify_write"][pos_total:pos_total+buffer_len])
|
||||
checksum_calculated = struct.unpack(">I", self._read(4))[0]
|
||||
dprint("Expected Checksum: 0x{:X}".format(checksum_expected))
|
||||
dprint("Calculated Checksum: 0x{:X}".format(checksum_calculated))
|
||||
if checksum_expected == checksum_calculated:
|
||||
pos += buffer_len
|
||||
pos_total += buffer_len
|
||||
dprint("Verification successful between 0x{:X} and 0x{:X}".format(pos_total-buffer_len, pos_total))
|
||||
self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
|
||||
continue
|
||||
else:
|
||||
dprint("Mismatch during verification between 0x{:X} and 0x{:X}".format(pos_total, pos_total+buffer_len))
|
||||
return pos_total
|
||||
else:
|
||||
# Normal read
|
||||
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
|
||||
skip_init = True
|
||||
|
||||
if len(temp) != buffer_len:
|
||||
|
|
@ -2748,6 +2754,9 @@ class GbxDevice:
|
|||
self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)+len(rtc_buffer)})
|
||||
|
||||
if args["path"] is not None:
|
||||
if self.MODE == "DMG" and _mbc.GetName() == "MBC2":
|
||||
for i in range(0, len(buffer)):
|
||||
buffer[i] = buffer[i] & 0x0F
|
||||
file = open(args["path"], "wb")
|
||||
file.write(buffer)
|
||||
if rtc_buffer is not None:
|
||||
|
|
@ -3426,6 +3435,7 @@ class GbxDevice:
|
|||
break
|
||||
elif buffer_pos == 0 or not self.DEVICE.is_open or self.DEVICE is None:
|
||||
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{:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), errmsg_mbc_selection)})
|
||||
break
|
||||
else:
|
||||
if chip_erase: retry_hp = 0
|
||||
if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
|
||||
|
|
@ -3452,7 +3462,7 @@ class GbxDevice:
|
|||
self.SetProgress({"action":"ERROR", "abortable":True, "pos":buffer_pos, "text":"Write error! Retrying from 0x{:X}...".format(rev_buffer_pos)})
|
||||
delay = 0.5 + (100-retry_hp)/50
|
||||
if self.CanPowerCycleCart():
|
||||
self.CartPowerOff(0)
|
||||
self.CartPowerOff()
|
||||
time.sleep(delay)
|
||||
self.CartPowerOn()
|
||||
if self.MODE == "DMG" and _mbc.HasFlashBanks(): _mbc.SelectBankFlash(bank)
|
||||
|
|
@ -3587,7 +3597,10 @@ class GbxDevice:
|
|||
ret = False
|
||||
self.SIGNAL = signal
|
||||
try:
|
||||
dprint("args:", args)
|
||||
temp = copy.copy(args)
|
||||
if "buffer" in temp: temp["buffer"] = "(data)"
|
||||
dprint("args:", temp)
|
||||
del(temp)
|
||||
self.NO_PROG_UPDATE = False
|
||||
self.READ_ERRORS = 0
|
||||
if args['mode'] == 1: ret = self._BackupROM(args)
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ class GbxDevice:
|
|||
return ["DMG", "AGB"]
|
||||
|
||||
def IsSupportedMbc(self, mbc):
|
||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x10, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0xFC, 0xFE, 0xFF, 0x101, 0x103 )
|
||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x10, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0xFC, 0xFE, 0xFF, 0x101, 0x103, 0x110 )
|
||||
|
||||
def IsSupported3dMemory(self):
|
||||
return False
|
||||
|
|
@ -703,7 +703,7 @@ class GbxDevice:
|
|||
|
||||
def EnableRAM(self, mbc=1, enable=True):
|
||||
if enable:
|
||||
if mbc in (0x01, 0x02, 0x03, 0x101, 0x103, 0x10, 0x13): # MBC1, MBC1M, MBC3
|
||||
if mbc in (0x01, 0x02, 0x03, 0x101, 0x103, 0x10, 0x13, 0x110): # MBC1, MBC1M, MBC3, MBC30
|
||||
self.cart_write(0x6000, 1)
|
||||
self.cart_write(0x0000, 0x0A, cs=(mbc == 0xFD))
|
||||
else:
|
||||
|
|
@ -1811,7 +1811,7 @@ class GbxDevice:
|
|||
self.cart_write(0xA080, 0xC0)
|
||||
|
||||
# RTC for MBC3+RTC+SRAM+BATTERY
|
||||
elif mbc == 0x10 and args["rtc"]:
|
||||
elif mbc in (0x10, 0x110) and args["rtc"]:
|
||||
buffer = bytearray()
|
||||
self.cart_write(0x6000, 0)
|
||||
self.cart_write(0x6000, 1)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#
|
||||
|
||||
from .Util import dprint
|
||||
# import importlib
|
||||
|
||||
try:
|
||||
import PySide2
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -24,7 +24,7 @@ for [Windows](https://github.com/lesserkuma/FlashGBX/releases), [Linux](https://
|
|||
|
||||
## Installing and running
|
||||
|
||||
### Windows Binaries
|
||||
### Windows Packages
|
||||
|
||||
Available in the GitHub [Releases](https://github.com/lesserkuma/FlashGBX/releases) section:
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
|||
- MBC7
|
||||
- MBC1M
|
||||
- MMM01
|
||||
- GBD (Game Boy Camera)
|
||||
- MAC-GBD (Game Boy Camera)
|
||||
- G-MMC1 (GB Memory)
|
||||
- M161
|
||||
- HuC-1
|
||||
|
|
@ -82,6 +82,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
|||
- Unlicensed Wisdom Tree Mapper
|
||||
- Unlicensed Xploder GB Mapper
|
||||
- Unlicensed Sachen Mapper
|
||||
- Unlicensed Datel Orbit V2 Mapper
|
||||
|
||||
- Game Boy Advance
|
||||
- All cartridges without memory mapping
|
||||
|
|
@ -158,6 +159,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
|||
- DMG-DHCN-20 with MX29LV320ET
|
||||
- DMG-GBRW-20 with 29LV320ETMI-70G
|
||||
- DRV with AM29LV160DB and ALTERA CPLD
|
||||
- DRV with AM29LV160DT and ALTERA CPLD
|
||||
- ES29LV160_DRV with 29DL32TF-70
|
||||
- GB-M968 with 29LV160DB
|
||||
- GB-M968 with M29W160EB
|
||||
|
|
@ -299,7 +301,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, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, metroid-maniac, Mr_V, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, Shinichi999, sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, Timville, twitnic, velipso, Veund, voltagex, Voultar, Wkr, x7l7j8cc, xactoes, Zeii, Zelante, zvxr
|
||||
2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, metroid-maniac, Mr_V, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, Shinichi999, sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, Timville, twitnic, velipso, Veund, voltagex, Voultar, Wkr, x7l7j8cc, xactoes, Zeii, Zelante, zvxr
|
||||
|
||||
## DISCLAIMER
|
||||
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
|
|||
|
||||
setuptools.setup(
|
||||
name="FlashGBX",
|
||||
version="3.24",
|
||||
version="3.25",
|
||||
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