diff --git a/.github/FlashGBX_Ubuntu.png b/.github/FlashGBX_Ubuntu.png index eaacb21..da1c6b1 100644 Binary files a/.github/FlashGBX_Ubuntu.png and b/.github/FlashGBX_Ubuntu.png differ diff --git a/.github/FlashGBX_Windows.png b/.github/FlashGBX_Windows.png index 625cdf6..192077c 100644 Binary files a/.github/FlashGBX_Windows.png and b/.github/FlashGBX_Windows.png differ diff --git a/.github/FlashGBX_macOS.png b/.github/FlashGBX_macOS.png index 8cc05dd..b204336 100644 Binary files a/.github/FlashGBX_macOS.png and b/.github/FlashGBX_macOS.png differ diff --git a/FlashGBX/FlashGBX.py b/FlashGBX/FlashGBX.py index a89eb8f..358d4c0 100644 --- a/FlashGBX/FlashGBX.py +++ b/FlashGBX/FlashGBX.py @@ -11,7 +11,7 @@ from . import hw_GBxCartRW hw_devices = [hw_GBxCartRW] APPNAME = "FlashGBX" -VERSION_PEP440 = "0.10" +VERSION_PEP440 = "1.0" VERSION = "v{:s}".format(VERSION_PEP440) class FlashGBX(QtWidgets.QWidget): @@ -229,11 +229,10 @@ class FlashGBX(QtWidgets.QWidget): # Read config, find devices and connect self.InitConfig(args) - self.FindDevices() # Show app window first, then do update check qt_app.processEvents() - QtCore.QTimer.singleShot(1, lambda: [ self.UpdateCheck() ]) + QtCore.QTimer.singleShot(1, lambda: [ self.UpdateCheck(), self.FindDevices() ]) def InitConfig(self, args): app_path = args['app_path'] @@ -524,7 +523,7 @@ class FlashGBX(QtWidgets.QWidget): def UpdateCheck(self): update_check = self.SETTINGS.value("UpdateCheck") if update_check is None: - answer = QtWidgets.QMessageBox.question(self, APPNAME, "Would you like to automatically check for new versions at application startup?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + answer = QtWidgets.QMessageBox.question(self, APPNAME, "Would you like to automatically check for new versions at application startup?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes) if answer == QtWidgets.QMessageBox.Yes: self.SETTINGS.setValue("UpdateCheck", "enabled") self.mnuConfig.actions()[0].setChecked(True) @@ -636,6 +635,7 @@ class FlashGBX(QtWidgets.QWidget): if ret is False: self.CONN = None + if self.cmbDevice.count() == 0: self.lblDevice.setText("No connection.") return False elif isinstance(ret, list): @@ -699,6 +699,7 @@ class FlashGBX(QtWidgets.QWidget): #self.btnScan.setEnabled(False) self.btnConnect.setEnabled(False) qt_app.processEvents() + time.sleep(0.05) global hw_devices for hw_device in hw_devices: @@ -816,7 +817,7 @@ class FlashGBX(QtWidgets.QWidget): if self.CONN.INFO["transferred"] == 131072: # 128 KB with open(self.CONN.INFO["last_path"], "rb") as file: temp = file.read() if temp[0x1FFB1:0x1FFB6] == b'Magic': - answer = QtWidgets.QMessageBox.question(self, APPNAME, "Game Boy Camera save data was detected.\nWould you like to load it with the GB Camera Viewer now?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + answer = QtWidgets.QMessageBox.question(self, APPNAME, "Game Boy Camera save data was detected.\nWould you like to load it with the GB Camera Viewer now?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes) if answer == QtWidgets.QMessageBox.Yes: self.CAMWIN = None self.CAMWIN = PocketCameraWindow(self, icon=self.windowIcon(), file=self.CONN.INFO["last_path"]) @@ -862,7 +863,7 @@ class FlashGBX(QtWidgets.QWidget): QtWidgets.QMessageBox.critical(self, APPNAME, "No flash cartridge type configuration files found. Try to restart the application with the “--reset” switch to reset the configuration.", QtWidgets.QMessageBox.Ok) return 0 - msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="Would you like " + APPNAME + " to try and auto-detect the flash cartridge type?", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="Would you like " + APPNAME + " to try and auto-detect the flash cartridge type?", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes) cb = QtWidgets.QCheckBox("Limit voltage to 3.3V", checked=True) if self.CONN.GetMode() == "DMG": msgbox.setCheckBox(cb) @@ -873,7 +874,7 @@ class FlashGBX(QtWidgets.QWidget): else: detected = self.CONN.AutoDetectFlash(limitVoltage) if len(detected) == 0: - msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="No pre-configured flash cartridge type was detected. You can still try and manually select one from the list -- look for similar PCB text and/or flash chip markings. However, chances are this cartridge is currently not supported for flashing with " + APPNAME + ".\n\nWould you like " + APPNAME + " to run a flash chip query? This may help adding support for your flash cartridge in the future.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="No pre-configured flash cartridge type was detected. You can still try and manually select one from the list -- look for similar PCB text and/or flash chip markings. However, chances are this cartridge is currently not supported for flashing with " + APPNAME + ".\n\nWould you like " + APPNAME + " to run a flash chip query? This may help adding support for your flash cartridge in the future.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes) if self.CONN.GetMode() == "DMG": msgbox.setCheckBox(cb) answer = msgbox.exec() @@ -1013,7 +1014,7 @@ class FlashGBX(QtWidgets.QWidget): path = "" if dpath != "": text = "The following ROM file will now be written to the flash cartridge:\n" + dpath - answer = QtWidgets.QMessageBox.question(self, APPNAME, text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + answer = QtWidgets.QMessageBox.question(self, APPNAME, text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok) if answer == QtWidgets.QMessageBox.Cancel: return path = dpath @@ -1058,7 +1059,7 @@ class FlashGBX(QtWidgets.QWidget): with open(path, "rb") as file: buffer = file.read() rom_size = len(buffer) if rom_size > carts[cart_type]['flash_size']: - answer = QtWidgets.QMessageBox.warning(self, APPNAME, "The selected flash cartridge type seems to support ROMs that are up to " + str(int(carts[cart_type]['flash_size'] / 1024 / 1024)) + " MB in size, but the file you selected is " + str(os.path.getsize(path)/1024/1024) + " MB. You can still give it a try, but it’s possible that it’s too large.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + answer = QtWidgets.QMessageBox.warning(self, APPNAME, "The selected flash cartridge type seems to support ROMs that are up to " + str(int(carts[cart_type]['flash_size'] / 1024 / 1024)) + " MB in size, but the file you selected is " + str(os.path.getsize(path)/1024/1024) + " MB. You can still give it a try, but it’s possible that it’s too large.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) if answer == QtWidgets.QMessageBox.Cancel: return override_voltage = False @@ -1098,10 +1099,10 @@ class FlashGBX(QtWidgets.QWidget): elif self.CONN.GetMode() == "AGB": hdr = RomFileAGB(path).GetHeader() if not hdr["logo_correct"]: - answer = QtWidgets.QMessageBox.warning(self, APPNAME, "Warning: The ROM file you selected may not boot on actual hardware due to invalid logo data.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + answer = QtWidgets.QMessageBox.warning(self, APPNAME, "Warning: The ROM file you selected will not boot on actual hardware due to invalid logo data.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) if answer == QtWidgets.QMessageBox.Cancel: return if not hdr["header_checksum_correct"]: - answer = QtWidgets.QMessageBox.warning(self, APPNAME, "Warning: The ROM file you selected may not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).".format(hdr["header_checksum_calc"], hdr["header_checksum"]), QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + answer = QtWidgets.QMessageBox.warning(self, APPNAME, "The ROM file you selected will not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).".format(hdr["header_checksum_calc"], hdr["header_checksum"]), QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) if answer == QtWidgets.QMessageBox.Cancel: return self.CONN.FlashROM(fncSetProgress=self.SetProgress, path=path, cart_type=cart_type, override_voltage=override_voltage, prefer_sector_erase=prefer_sector_erase, reverse_sectors=reverse_sectors) @@ -1175,12 +1176,12 @@ class FlashGBX(QtWidgets.QWidget): if dpath != "": text = "The following save data file will now be written to the cartridge:\n" + dpath - answer = QtWidgets.QMessageBox.question(self, APPNAME, text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + answer = QtWidgets.QMessageBox.question(self, APPNAME, text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok) if answer == QtWidgets.QMessageBox.Cancel: return path = dpath self.SETTINGS.setValue(setting_name, os.path.dirname(path)) elif erase: - answer = QtWidgets.QMessageBox.warning(self, APPNAME, "The save data on your cartridge will now be erased.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + answer = QtWidgets.QMessageBox.warning(self, APPNAME, "The save data on your cartridge will now be erased.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) if answer == QtWidgets.QMessageBox.Cancel: return else: path = path + ".sav" @@ -1203,7 +1204,7 @@ class FlashGBX(QtWidgets.QWidget): dontShowAgain = str(self.SETTINGS.value("AutoReconnect")).lower() == "enabled" if not dontShowAgain: cb = QtWidgets.QCheckBox("Always try to reconnect without asking", checked=False) - msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="The connection to the device was lost. Do you want to try and reconnect to the first device found? The cartridge information will also be reset and read again.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="The connection to the device was lost. Do you want to try and reconnect to the first device found? The cartridge information will also be reset and read again.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes) msgbox.setCheckBox(cb) answer = msgbox.exec() dontShowAgain = cb.isChecked() @@ -1248,7 +1249,7 @@ class FlashGBX(QtWidgets.QWidget): if not dontShowAgain and mode is not None: cb = QtWidgets.QCheckBox("Don’t show this message again.", checked=False) - msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle=APPNAME, text="The mode will now be changed to " + {"DMG":"Game Boy", "AGB":"Game Boy Advance"}[setTo] + " mode. To be safe, cartridges should only be exchanged while the device is not powered on." + voltageWarning, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle=APPNAME, text="The mode will now be changed to " + {"DMG":"Game Boy", "AGB":"Game Boy Advance"}[setTo] + " mode. To be safe, cartridges should only be exchanged while the device is not powered on." + voltageWarning, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, defaultButton=QtWidgets.QMessageBox.Ok) if self.CONN.CanSetVoltageAutomatically(): msgbox.setCheckBox(cb) answer = msgbox.exec() dontShowAgain = cb.isChecked() @@ -1769,7 +1770,7 @@ def main(portableMode=False): args = parser.parse_args() config_path = cp[args.cfgdir] - print("\n{:s} {:s} by Lesserkuma".format(APPNAME, VERSION)) + print("{:s} {:s} by Lesserkuma".format(APPNAME, VERSION)) print("\nDISCLAIMER: This software is provided as-is and the developer is not responsible for any damage that is caused by the use of it. Use at your own risk!") print("\nFor troubleshooting please visit https://github.com/lesserkuma/FlashGBX") diff --git a/FlashGBX/PocketCamera.py b/FlashGBX/PocketCamera.py index 30e9b90..f48fbd8 100644 --- a/FlashGBX/PocketCamera.py +++ b/FlashGBX/PocketCamera.py @@ -47,18 +47,25 @@ class PocketCameraWindow(QtWidgets.QDialog): grpColorsLayout.setContentsMargins(-1, 3, -1, -1) self.rowColors1 = QtWidgets.QHBoxLayout() self.optColorBW = QtWidgets.QRadioButton("&Grayscale") + self.optColorBW.setToolTip("Generic grayscale palette") self.connect(self.optColorBW, QtCore.SIGNAL("clicked()"), self.SetColors) self.optColorDMG = QtWidgets.QRadioButton("&DMG") + self.optColorDMG.setToolTip("Original Game Boy screen palette") self.connect(self.optColorDMG, QtCore.SIGNAL("clicked()"), self.SetColors) #self.optColorMGB = QtWidgets.QRadioButton("&MGB") + #self.optColorMGB.setToolTip("Game Boy Pocket palette") #self.connect(self.optColorMGB, QtCore.SIGNAL("clicked()"), self.SetColors) self.optColorSGB = QtWidgets.QRadioButton("&SGB") + self.optColorSGB.setToolTip("Super Game Boy palette") self.connect(self.optColorSGB, QtCore.SIGNAL("clicked()"), self.SetColors) self.optColorCGB1 = QtWidgets.QRadioButton("CGB &1") + self.optColorCGB1.setToolTip("Japanese Pocket Camera palette") self.connect(self.optColorCGB1, QtCore.SIGNAL("clicked()"), self.SetColors) self.optColorCGB2 = QtWidgets.QRadioButton("CGB &2") + self.optColorCGB2.setToolTip("Golden Limited Edition Game Boy Camera palette") self.connect(self.optColorCGB2, QtCore.SIGNAL("clicked()"), self.SetColors) self.optColorCGB3 = QtWidgets.QRadioButton("CGB &3") + self.optColorCGB3.setToolTip("Non-Japanese Game Boy Camera palette") self.connect(self.optColorCGB3, QtCore.SIGNAL("clicked()"), self.SetColors) self.rowColors1.addWidget(self.optColorBW) self.rowColors1.addWidget(self.optColorDMG) diff --git a/FlashGBX/config/fc_DMG_Doctor_GB_Card_64M.txt b/FlashGBX/config/fc_DMG_Doctor_GB_Card_64M.txt index 349f67d..94c3770 100644 --- a/FlashGBX/config/fc_DMG_Doctor_GB_Card_64M.txt +++ b/FlashGBX/config/fc_DMG_Doctor_GB_Card_64M.txt @@ -1,9 +1,11 @@ { "type":"DMG", "names":[ - "BUNG Doctor GB Card 64M" + "BUNG Doctor GB Card 64M", + "Mr Flash 64M" ], "flash_ids":[ + [ 0x89, 0x89, 0x15, 0x15 ], [ 0x89, 0x89, 0x15, 0x15 ] ], "voltage":5, diff --git a/FlashGBX/config/fc_DMG_iG_512KB.txt b/FlashGBX/config/fc_DMG_iG_512KB.txt index 7a14711..64cfb48 100644 --- a/FlashGBX/config/fc_DMG_iG_512KB.txt +++ b/FlashGBX/config/fc_DMG_iG_512KB.txt @@ -10,6 +10,7 @@ "flash_size":0x80000, "start_addr":0, "first_bank":1, + "flash_commands_on_bank_1":true, "write_pin":"AUDIO", "chip_erase_timeout":30, "commands":{ diff --git a/FlashGBX/hw_GBxCartRW.py b/FlashGBX/hw_GBxCartRW.py index 072f3ca..68e2702 100644 --- a/FlashGBX/hw_GBxCartRW.py +++ b/FlashGBX/hw_GBxCartRW.py @@ -1203,6 +1203,11 @@ class GbxDevice: # Reverse sectors if requested if reverse_sectors: flashcart_meta['sector_size'].reverse() + # Firmware check R20+ + if (int(self.FW[0]) < 20) and self.MODE == "AGB" and "buffer_write" in flashcart_meta["commands"] and flashcart_meta["commands"]["buffer_write"] == [[0xAAA, 0xAA], [0x555, 0x55], ['SA', 0x25], ['SA', 'BS'], ['PA', 'PD'], ['SA', 0x29]]: + self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"A firmware update is required to write to this cartridge. Please update the firmware of your GBxCart RW device to version R20 or higher.", "abortable":False}) + return False + # Firmware check R20+ # Firmware check R23+ if (int(self.FW[0]) < 23) and self.MODE == "AGB" and "buffer_write" in flashcart_meta["commands"] and flashcart_meta["commands"]["buffer_write"] == [[0xAAA, 0xA9], [0x555, 0x56], ['SA', 0x26], ['SA', 'BS'], ['PA', 'PD'], ['SA', 0x2A]]: print("NOTE: Update your GBxCart RW firmware to version R23 or higher for a better transfer rate with this cartridge.") diff --git a/FlashGBX/res/config.zip b/FlashGBX/res/config.zip index 7e9f1d9..c878441 100644 Binary files a/FlashGBX/res/config.zip and b/FlashGBX/res/config.zip differ diff --git a/README.md b/README.md index 7d718a5..2b9237f 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ by Lesserkuma - insideGadgets 2 MB, 128 KB SRAM/32 KB FRAM *(thanks AlexiG)* - insideGadgets 4 MB, 128 KB SRAM/FRAM *(thanks AlexiG)* - insideGadgets 4 MB, 32 KB FRAM, MBC3+RTC *(thanks AlexiG)* + - Mr Flash 64M - Game Boy Advance @@ -242,3 +243,8 @@ The author would like to thank the following very kind people for their help and - Added support for BX2006_TSOPBGA_0106 with K8D6316UTM-PI07 *(thanks LucentW)* - Added support for the currently available insideGadgets Game Boy Advance flash cartridges *(thanks AlexiG)* - Added a Game Boy Camera (Pocket Camera) album viewer and picture extractor + +### v1.0 (released 2021-01-01) +- Added a firmware check when writing to insideGadgets Game Boy Advance flash cartridges (requires GBxCart RW firmware R20 or higher) +- Confirmed support for Mr Flash 64M (rebranded BUNG Doctor GB Card 64M) +- Fixed a problem with writing to the insideGadgets 512 KB Game Boy flash cartridge diff --git a/setup.py b/setup.py index 508961f..53bc710 100644 --- a/setup.py +++ b/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="0.10", + version="1.0", author="Lesserkuma", description="A GUI application that can read and write Game Boy and Game Boy Advance cartridge data. Currently supports the GBxCart RW hardware device by insideGadgets.", url="https://github.com/lesserkuma/FlashGBX",