FlashGBX 4.3__py3-none-any.whl → 4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
FlashGBX/FlashGBX_GUI.py CHANGED
@@ -1,11 +1,13 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # FlashGBX
3
- # Author: Lesserkuma (github.com/lesserkuma)
3
+ # Author: Lesserkuma (github.com/Lesserkuma)
4
4
 
5
- import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, pkg_resources, threading, calendar
5
+ import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, threading, calendar, queue
6
6
  from .pyside import QtCore, QtWidgets, QtGui, QApplication
7
7
  from PIL.ImageQt import ImageQt
8
+ from PIL import Image
8
9
  from serial import SerialException
10
+ from packaging import version
9
11
  from .RomFileDMG import RomFileDMG
10
12
  from .RomFileAGB import RomFileAGB
11
13
  from .PocketCameraWindow import PocketCameraWindow
@@ -28,6 +30,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
28
30
  FWUPWIN = None
29
31
  STATUS = {}
30
32
  TEXT_COLOR = (0, 0, 0, 255)
33
+ MSGBOX_QUEUE = queue.Queue()
34
+ MSGBOX_DISPLAYING = False
31
35
 
32
36
  def __init__(self, args):
33
37
  sys.excepthook = Util.exception_hook
@@ -201,6 +205,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
201
205
  self.mnuConfig.addAction("Use &No-Intro file names", lambda: self.SETTINGS.setValue("UseNoIntroFilenames", str(self.mnuConfig.actions()[7].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
202
206
  self.mnuConfig.addAction("Automatic cartridge &power off", lambda: [ self.SETTINGS.setValue("AutoPowerOff", str(self.mnuConfig.actions()[8].isChecked()).lower().replace("true", "350").replace("false", "0")), self.SetAutoPowerOff() ])
203
207
  self.mnuConfig.addAction("Skip writing matching ROM chunk&s", lambda: self.SETTINGS.setValue("CompareSectors", str(self.mnuConfig.actions()[9].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
208
+ self.mnuConfig.addAction("Alternate address set mode (can fix or cause write errors)", lambda: self.SETTINGS.setValue("ForceWrPullup", str(self.mnuConfig.actions()[10].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
204
209
  self.mnuConfig.addSeparator()
205
210
  self.mnuConfigReadModeAGB = QtWidgets.QMenu("&Read Method (Game Boy Advance)")
206
211
  self.mnuConfigReadModeAGB.addAction("S&tream", lambda: [ self.SETTINGS.setValue("AGBReadMethod", str(self.mnuConfigReadModeAGB.actions()[1].isChecked()).lower().replace("true", "2")), self.SetAGBReadMethod() ])
@@ -230,6 +235,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
230
235
  self.mnuConfig.actions()[7].setCheckable(True)
231
236
  self.mnuConfig.actions()[8].setCheckable(True)
232
237
  self.mnuConfig.actions()[9].setCheckable(True)
238
+ self.mnuConfig.actions()[10].setCheckable(True)
233
239
  self.mnuConfig.actions()[0].setChecked(self.SETTINGS.value("UpdateCheck") == "enabled")
234
240
  self.mnuConfig.actions()[1].setChecked(self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled") == "enabled")
235
241
  self.mnuConfig.actions()[2].setChecked(self.SETTINGS.value("PreferChipErase", default="disabled") == "enabled")
@@ -240,6 +246,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
240
246
  self.mnuConfig.actions()[7].setChecked(self.SETTINGS.value("UseNoIntroFilenames", default="enabled") == "enabled")
241
247
  self.mnuConfig.actions()[8].setChecked(self.SETTINGS.value("AutoPowerOff", default="350") != "0")
242
248
  self.mnuConfig.actions()[9].setChecked(self.SETTINGS.value("CompareSectors", default="enabled") == "enabled")
249
+ self.mnuConfig.actions()[10].setChecked(self.SETTINGS.value("ForceWrPullup", default="disabled") == "enabled")
243
250
 
244
251
  self.mnuThirdParty = QtWidgets.QMenu("Third Party &Notices")
245
252
  self.mnuThirdParty.addAction("About &Qt", lambda: [ QtWidgets.QMessageBox.aboutQt(None) ])
@@ -301,8 +308,19 @@ class FlashGBX_GUI(QtWidgets.QWidget):
301
308
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), config_ret[i][1], QtWidgets.QMessageBox.Ok)
302
309
 
303
310
  QtCore.QTimer.singleShot(1, lambda: [ self.UpdateCheck(), self.FindDevices(port=args["argparsed"].device_port, firstRun=True) ])
311
+ self.MSGBOX_TIMER = QtCore.QTimer()
312
+ self.MSGBOX_TIMER.timeout.connect(self.MsgBoxCheck)
313
+ self.MSGBOX_TIMER.start(200)
314
+
304
315
 
305
-
316
+ def MsgBoxCheck(self):
317
+ if not self.MSGBOX_DISPLAYING and not self.MSGBOX_QUEUE.empty():
318
+ self.MSGBOX_DISPLAYING = True
319
+ msgbox = self.MSGBOX_QUEUE.get()
320
+ Util.dprint(f"Processing Message Box: {msgbox}")
321
+ msgbox.exec()
322
+ self.MSGBOX_DISPLAYING = False
323
+
306
324
  def GuiCreateGroupBoxDMGCartInfo(self):
307
325
  self.grpDMGCartridgeInfo = QtWidgets.QGroupBox("Game Boy Cartridge Information")
308
326
  self.grpDMGCartridgeInfo.setMinimumWidth(400)
@@ -612,8 +630,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
612
630
  self.SETTINGS.setValue("UpdateCheck", "disabled")
613
631
  if update_check and update_check.lower() == "enabled":
614
632
  print("")
615
- url = "https://api.github.com/repos/lesserkuma/FlashGBX/releases/latest"
616
- site = "https://github.com/lesserkuma/FlashGBX/releases/latest"
633
+ url = "https://api.github.com/repos/Lesserkuma/FlashGBX/releases/latest"
634
+ site = "https://github.com/Lesserkuma/FlashGBX/releases/latest"
617
635
  try:
618
636
  ret = requests.get(url, allow_redirects=True, timeout=1.5)
619
637
  except requests.exceptions.ConnectTimeout as e:
@@ -631,9 +649,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
631
649
  ret = json.loads(ret)
632
650
  if 'tag_name' in ret:
633
651
  latest_version = str(ret['tag_name'])
634
- if pkg_resources.parse_version(latest_version) == pkg_resources.parse_version(VERSION_PEP440):
652
+ if version.parse(latest_version) == version.parse(VERSION_PEP440):
635
653
  print("You are using the latest version of {:s}.".format(APPNAME))
636
- elif pkg_resources.parse_version(latest_version) > pkg_resources.parse_version(VERSION_PEP440):
654
+ elif version.parse(latest_version) > version.parse(VERSION_PEP440):
637
655
  msg_text = "A new version of {:s} has been released!\nVersion {:s} is now available.".format(APPNAME, latest_version)
638
656
  print(msg_text)
639
657
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} Update Check".format(APPNAME), text=msg_text)
@@ -657,23 +675,19 @@ class FlashGBX_GUI(QtWidgets.QWidget):
657
675
  print("Error: Failed to check for updates (too many API requests). Try again later.")
658
676
  else:
659
677
  print("Error: Failed to check for updates (HTTP status {:d}).".format(ret.status_code))
660
- else:
661
- update_check = self.SETTINGS.value("UpdateCheck")
662
- if update_check is None or (time.time() > (Util.VERSION_TIMESTAMP + (6*30*24*60*60))):
663
- QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "Welcome to {:s} {:s} by Lesserkuma!<br><br>".format(APPNAME, VERSION) + "The version update check has been disabled in the options menu and this version is now older than {:d} days. Please regularily check the <a href=\"https://github.com/lesserkuma/FlashGBX/releases/latest\">FlashGBX GitHub page</a> for the latest release notes and updates.".format(int((time.time() - Util.VERSION_TIMESTAMP)/60/60/24)), QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Ok)
664
678
 
665
679
  def DisconnectDevice(self):
666
680
  try:
667
681
  devname = self.CONN.GetFullNameExtended()
668
682
  self.CONN.Close(cartPowerOff=True)
669
- self.CONN = None
670
- self.DEVICES = {}
671
- self.cmbDevice.clear()
672
683
  print("Disconnected from {:s}".format(devname))
673
684
  except:
674
685
  pass
675
686
 
687
+ self.DEVICES = {}
688
+ self.cmbDevice.clear()
676
689
  self.CONN = None
690
+
677
691
  self.optAGB.setEnabled(False)
678
692
  self.optDMG.setEnabled(False)
679
693
  self.grpDMGCartridgeInfo.setEnabled(False)
@@ -698,6 +712,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
698
712
  self.mnuConfig.actions()[5].setVisible(True)
699
713
  self.mnuConfig.actions()[8].setVisible(True)
700
714
  self.mnuConfig.actions()[9].setVisible(True)
715
+ self.mnuConfig.actions()[10].setVisible(False)
701
716
  self.mnuTools.actions()[2].setEnabled(True)
702
717
  self.mnuConfigReadModeAGB.setEnabled(True)
703
718
 
@@ -711,14 +726,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
711
726
  def AboutFlashGBX(self):
712
727
  msg = "This software is being developed by Lesserkuma as a hobby project. There is no affiliation with Nintendo or any other company. 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!<br><br>"
713
728
  msg += f"© 2020–{datetime.datetime.now().year} Lesserkuma<br>"
714
- msg += "• Website: <a href=\"https://github.com/lesserkuma/FlashGBX\">https://github.com/lesserkuma/FlashGBX</a><br><br>"
715
- msg += "Acknowledgments and Contributors:<br>2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, aronson, Ausar, bbsan, BennVenn, ccs21, chobby, ClassicOldSong, Cliffback, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gandalf1980, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, inYourBackline, iyatemu, Jayro, Jenetrix, JFox, joyrider3774, jrharbort, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Lu, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, RibShark, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, simonK, Sithdown, skite2001, Smelly-Ghost, Sonikks, Squiddy, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, Warez Waldo, wickawack, Winter1760, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr"
729
+ msg += "• Website: <a href=\"https://github.com/Lesserkuma/FlashGBX\">https://github.com/Lesserkuma/FlashGBX</a><br><br>"
730
+ msg += "Acknowledgments and Contributors:<br>2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, aronson, Ausar, bbsan, BennVenn, ccs21, chobby, ClassicOldSong, Cliffback, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, delibird_deals, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, Gahr, gandalf1980, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, infinest, inYourBackline, iyatemu, Jayro, Jenetrix, JFox, joyrider3774, jrharbort, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Lu, Luca DS, LucentW, luxkiller65, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, Mufsta, olDirdey, orangeglo, paarongiroux, Paradoxical, Pese, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, RibShark, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, simonK, Sithdown, skite2001, Smelly-Ghost, Sonikks, Squiddy, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, Warez Waldo, wickawack, Winter1760, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr"
716
731
  QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
717
732
 
718
733
  def AboutGameDB(self):
719
734
  msg = f"{APPNAME} uses a game database that is based on the ongoing efforts of the No-Intro project. Visit <a href=\"https://no-intro.org/\">https://no-intro.org/</a> for more information.<br><br>"
720
735
  msg += f"No-Intro databases referenced for this version of {APPNAME}:<br>"
721
- msg += "• Nintendo - Game Boy (20241003-140822)<br>• Nintendo - Game Boy Advance (20241102-092521)<br>• Nintendo - Game Boy Advance (Video) (20241021-195439)<br>• Nintendo - Game Boy Color (20241105-004534)" # No-Intro DBs
736
+ msg += "• Nintendo - Game Boy (20260113-102506)<br>• Nintendo - Game Boy Advance (20260124-113814)<br>• Nintendo - Game Boy Advance (Video) (20251114-101831)<br>• Nintendo - Game Boy Color (20260110-131928)" # No-Intro DBs
722
737
  QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
723
738
 
724
739
  def OpenPath(self, path=None):
@@ -815,6 +830,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
815
830
  self.mnuConfig.actions()[5].setVisible(self.CONN.DEVICE_NAME == "GBxCart RW") # Limit Baud Rate
816
831
  self.mnuConfig.actions()[8].setVisible(self.CONN.CanPowerCycleCart() and self.CONN.CanPowerCycleCart() and self.CONN.FW["fw_ver"] >= 12) # Auto Power Off
817
832
  self.mnuConfig.actions()[9].setVisible(self.CONN.FW["fw_ver"] >= 12) # Skip writing matching ROM chunks
833
+ self.mnuConfig.actions()[10].setVisible(self.CONN.DEVICE_NAME == "Joey Jr") # Force WR Pullup
818
834
  self.mnuConfigReadModeAGB.setEnabled(self.CONN.FW["fw_ver"] >= 12)
819
835
  self.mnuConfigReadModeDMG.setEnabled(self.CONN.FW["fw_ver"] >= 12)
820
836
 
@@ -872,7 +888,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
872
888
  text = "A firmware update for your {:s} device is required to use this software. Do you want to update now?".format(dev.GetFullName())
873
889
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
874
890
  elif dev.FW_UPDATE_REQ == 2:
875
- text = "Your {:s} device is no longer supported as of FlashGBX v4.0+. You can still use the Firmware Updater, however any other functions are no longer available.\n\nDo you want to run the Firmware Updater now?".format(dev.GetFullName())
891
+ text = "Your {:s} device is no longer supported in this version of FlashGBX due to technical limitations. The last supported version is <a href=\"https://github.com/Lesserkuma/FlashGBX/releases/tag/3.37\">FlashGBX v3.37</a>.\n\nYou can still use the Firmware Updater, however any other functions are no longer available.\n\nDo you want to run the Firmware Updater now?".format(dev.GetFullName()).replace("\n", "<br>")
876
892
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
877
893
  else:
878
894
  text = "A firmware update for your {:s} device is available. Do you want to update now?".format(dev.GetFullName())
@@ -916,7 +932,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
916
932
  qt_app.processEvents()
917
933
 
918
934
  messages = []
919
- #last_msg = ""
920
935
 
921
936
  # pylint: disable=global-variable-not-assigned
922
937
  global hw_devices
@@ -938,8 +953,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
938
953
  msg = ret[i][1]
939
954
  if msg in messages: # don’t show the same message twice
940
955
  continue
941
- #else:
942
- # last_msg = msg
943
956
  if status == 3:
944
957
  messages.append(msg)
945
958
  self.CONN = None
@@ -964,7 +977,23 @@ class FlashGBX_GUI(QtWidgets.QWidget):
964
977
  msg += message + "\n\n"
965
978
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), msg[:-2], QtWidgets.QMessageBox.Ok)
966
979
  elif not firstRun:
967
- QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="No compatible devices found. Please ensure the device is connected properly.\n\nTroubleshooting advice:\n- Reconnect the device, try different USB ports/cables, avoid passive USB hubs\n- Use a USB data cable (battery charging cables may not work)\n- Check if the operating system detects the device (if not, reboot your machine)\n- Ensure your user account has permissions to use the device\n- Refer to the device compatibility list on the <a href=\"https://github.com/lesserkuma/FlashGBX/#compatible-cartridge-readerwriter-hardware\">GitHub page</a>\n- Update the firmware manually through Options → Tools → Firmware Updater".replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok).exec()
980
+ msg = \
981
+ "No compatible devices found. Please ensure the device is connected properly.\n\n" \
982
+ "Compatible devices:\n" \
983
+ "- insideGadgets GBxCart RW\n" \
984
+ "- Geeksimon GBFlash\n" \
985
+ "- BennVenn Joey Jr (requires firmware update)\n\n" \
986
+ "Troubleshooting advice:\n" \
987
+ "- Reconnect the device, try different USB ports/cables, avoid passive USB hubs\n" \
988
+ "- Use a USB data cable (battery charging cables may not work)\n" \
989
+ "- Check if the operating system detects the device (if not, reboot your machine)\n" \
990
+ "- Update the firmware through Options → Tools → Firmware Updater"
991
+ if platform.system() == "Darwin":
992
+ msg += "\n   - <b>For Joey Jr on macOS:</b> Use the dedicated <a href=\"https://github.com/Lesserkuma/JoeyJr_FWUpdater\">Firmware Updater for Joey Jr</a>"
993
+ elif platform.system() == "Linux":
994
+ msg += "\n- <b>For Linux users:</b> Ensure your user account has permissions to use the device (try adding yourself to user groups “dialout” or “uucp”)"
995
+
996
+ QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg.replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok).exec()
968
997
 
969
998
  self.lblDevice.setText("No devices found.")
970
999
  self.lblDevice.setStyleSheet("")
@@ -1156,22 +1185,24 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1156
1185
 
1157
1186
  dontShowAgainCameraSavePopup = str(self.SETTINGS.value("SkipCameraSavePopup", default="disabled")).lower() == "enabled"
1158
1187
  if not dontShowAgainCameraSavePopup:
1159
- if self.CONN.GetMode() == "DMG" and self.CONN.INFO["mapper_raw"] == 252 and self.CONN.INFO["transferred"] == 131072: # Pocket Camera / 128 KiB
1160
- cbCameraSavePopup = QtWidgets.QCheckBox("Don’t show this message again", checked=dontShowAgain)
1161
- msgboxCameraPopup = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Would you like to load your save data with the GB Camera Viewer now?")
1162
- msgboxCameraPopup.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
1163
- msgboxCameraPopup.setDefaultButton(QtWidgets.QMessageBox.Yes)
1164
- msgboxCameraPopup.setCheckBox(cbCameraSavePopup)
1165
- answer = msgboxCameraPopup.exec()
1166
- dontShowAgainCameraSavePopup = cbCameraSavePopup.isChecked()
1167
- if dontShowAgainCameraSavePopup: self.SETTINGS.setValue("SkipCameraSavePopup", "enabled")
1168
- if answer == QtWidgets.QMessageBox.Yes:
1169
- self.CAMWIN = None
1170
- self.CAMWIN = PocketCameraWindow(self, icon=self.windowIcon(), file=self.CONN.INFO["last_path"], config_path=Util.CONFIG_PATH, app_path=Util.APP_PATH)
1171
- self.CAMWIN.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
1172
- self.CAMWIN.setModal(True)
1173
- self.CAMWIN.run()
1174
- return
1188
+ if self.CONN.GetMode() == "DMG" and self.CONN.INFO["mapper_raw"] == 252:
1189
+ # Pocket Camera
1190
+ if self.CONN.INFO["transferred"] == 0x20000 or (self.CONN.INFO["transferred"] == 0x100000 and "Unlicensed Photo!" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]):
1191
+ cbCameraSavePopup = QtWidgets.QCheckBox("Don’t show this message again", checked=dontShowAgain)
1192
+ msgboxCameraPopup = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Would you like to load your save data with the GB Camera Viewer now?")
1193
+ msgboxCameraPopup.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
1194
+ msgboxCameraPopup.setDefaultButton(QtWidgets.QMessageBox.Yes)
1195
+ msgboxCameraPopup.setCheckBox(cbCameraSavePopup)
1196
+ answer = msgboxCameraPopup.exec()
1197
+ dontShowAgainCameraSavePopup = cbCameraSavePopup.isChecked()
1198
+ if dontShowAgainCameraSavePopup: self.SETTINGS.setValue("SkipCameraSavePopup", "enabled")
1199
+ if answer == QtWidgets.QMessageBox.Yes:
1200
+ self.CAMWIN = None
1201
+ self.CAMWIN = PocketCameraWindow(self, icon=self.windowIcon(), file=self.CONN.INFO["last_path"], config_path=Util.CONFIG_PATH, app_path=Util.APP_PATH)
1202
+ self.CAMWIN.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
1203
+ self.CAMWIN.setModal(True)
1204
+ self.CAMWIN.run()
1205
+ return
1175
1206
 
1176
1207
  if "last_path" in self.CONN.INFO:
1177
1208
  button_open_dir = msgbox.addButton(" Open Fol&der ", QtWidgets.QMessageBox.ActionRole)
@@ -1278,6 +1309,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1278
1309
  def CartridgeTypeChanged(self, index):
1279
1310
  self.STATUS["cart_type"] = {}
1280
1311
  if index in (-1, 0): return
1312
+ if "detect_cartridge_args" in self.STATUS: return
1281
1313
  if self.CONN.GetMode() == "DMG":
1282
1314
  cart_types = self.CONN.GetSupportedCartridgesDMG()
1283
1315
  if cart_types[1][index] == "RETAIL": # special keyword
@@ -1397,7 +1429,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1397
1429
 
1398
1430
  if cart_type is False: # clicked Cancel button
1399
1431
  return
1400
- elif cart_type is None or cart_type == 0:
1432
+ elif cart_type is None or cart_type == 0 or not isinstance(cart_type, int):
1401
1433
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "A compatible flash cartridge profile could not be auto-detected.", QtWidgets.QMessageBox.Ok)
1402
1434
  return
1403
1435
 
@@ -1451,8 +1483,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1451
1483
  msg += " You can still give it a try, but it’s possible that it’s too large which may cause the ROM writing to fail."
1452
1484
  answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
1453
1485
  if answer == QtWidgets.QMessageBox.Cancel: return
1454
- #if "mbc" in carts[cart_type]:
1455
- # mbc = carts[cart_type]["mbc"]
1456
1486
 
1457
1487
  override_voltage = False
1458
1488
  if 'voltage_variants' in carts[cart_type] and carts[cart_type]['voltage'] == 3.3:
@@ -1568,7 +1598,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1568
1598
  pass
1569
1599
 
1570
1600
  flash_offset = 0
1571
-
1601
+ force_wr_pullup = self.SETTINGS.value("ForceWrPullup", default="disabled").lower() == "enabled"
1602
+
1572
1603
  self.grpDMGCartridgeInfo.setEnabled(False)
1573
1604
  self.grpAGBCartridgeInfo.setEnabled(False)
1574
1605
  self.grpActions.setEnabled(False)
@@ -1582,7 +1613,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1582
1613
  verify_write = False
1583
1614
  args = { "path":"", "buffer":buffer, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header, "fix_bootlogo":fix_bootlogo, "mbc":mbc }
1584
1615
  else:
1585
- args = { "path":path, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header, "fix_bootlogo":fix_bootlogo, "mbc":mbc, "flash_offset":flash_offset }
1616
+ args = { "path":path, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header, "fix_bootlogo":fix_bootlogo, "mbc":mbc, "flash_offset":flash_offset, "force_wr_pullup":force_wr_pullup }
1586
1617
  args["compare_sectors"] = self.SETTINGS.value("CompareSectors", default="disabled").lower() == "enabled"
1587
1618
  self.CONN.FlashROM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
1588
1619
  #self.CONN._FlashROM(args=args)
@@ -1592,18 +1623,48 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1592
1623
  self.STATUS["last_path"] = path
1593
1624
  self.STATUS["args"] = args
1594
1625
 
1595
- def BackupRAM(self):
1626
+ def BackupRAM(self, dpath=""):
1596
1627
  if not self.CheckDeviceAlive(): return
1597
1628
 
1598
1629
  rtc = False
1599
- add_date_time = self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled")
1600
- path_datetime = ""
1601
- if add_date_time and add_date_time.lower() == "enabled":
1602
- path_datetime = "_{:s}".format(datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
1630
+ path = ""
1603
1631
 
1604
- path = Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=self.SETTINGS)
1605
- path = os.path.splitext(path)[0]
1606
- path += "{:s}.sav".format(path_datetime)
1632
+ # Detect Cartridge needed?
1633
+ if \
1634
+ (self.CONN.GetMode() == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1635
+ (self.CONN.GetMode() == "DMG" and self.cmbDMGHeaderSaveTypeResult.currentIndex() < len(Util.DMG_Header_RAM_Sizes) and "Batteryless SRAM" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) or \
1636
+ (self.CONN.GetMode() == "DMG" and "Unlicensed Photo!" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) \
1637
+ :
1638
+ if self.CONN.GetFWBuildDate() == "": # Legacy Mode
1639
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="This feature is not supported in Legacy Mode.", standardButtons=QtWidgets.QMessageBox.Ok)
1640
+ msgbox.exec()
1641
+ return
1642
+
1643
+ if self.CONN.GetMode() == "AGB":
1644
+ cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1645
+ elif self.CONN.GetMode() == "DMG":
1646
+ cart_type = self.cmbDMGCartridgeTypeResult.currentIndex()
1647
+ if cart_type == 0 or ("dump_info" not in self.CONN.INFO or "batteryless_sram" not in self.CONN.INFO["dump_info"]):
1648
+ if "detected_cart_type" not in self.STATUS: self.STATUS["detected_cart_type"] = ""
1649
+ if self.STATUS["detected_cart_type"] == "":
1650
+ self.STATUS["detected_cart_type"] = "WAITING_SAVE_READ"
1651
+ self.STATUS["detect_cartridge_args"] = { "dpath":path }
1652
+ self.STATUS["can_skip_message"] = True
1653
+ self.DetectCartridge(checkSaveType=True)
1654
+ return
1655
+ cart_type = self.STATUS["detected_cart_type"]
1656
+ if "detected_cart_type" in self.STATUS: del(self.STATUS["detected_cart_type"])
1657
+
1658
+ if cart_type is False: # clicked Cancel button
1659
+ return
1660
+ elif cart_type is None or cart_type == 0 or not isinstance(cart_type, int):
1661
+ QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "A compatible flash cartridge profile could not be auto-detected.", QtWidgets.QMessageBox.Ok)
1662
+ return
1663
+ if self.CONN.GetMode() == "AGB":
1664
+ self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
1665
+ elif self.CONN.GetMode() == "DMG":
1666
+ self.cmbDMGCartridgeTypeResult.setCurrentIndex(cart_type)
1667
+
1607
1668
  cart_type = 0
1608
1669
  if self.CONN.GetMode() == "DMG":
1609
1670
  setting_name = "LastDirSaveDataDMG"
@@ -1615,7 +1676,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1615
1676
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
1616
1677
  return
1617
1678
  cart_type = self.cmbDMGCartridgeTypeResult.currentIndex()
1618
- #save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(save_type)]
1619
1679
 
1620
1680
  elif self.CONN.GetMode() == "AGB":
1621
1681
  setting_name = "LastDirSaveDataAGB"
@@ -1627,13 +1687,23 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1627
1687
  QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
1628
1688
  return
1629
1689
  cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1630
- #save_size = Util.AGB_Header_Save_Sizes[save_type]
1631
1690
  else:
1632
1691
  return
1633
1692
 
1634
1693
  if not self.CheckHeader(): return
1635
- path = QtWidgets.QFileDialog.getSaveFileName(self, "Backup Save Data", last_dir + "/" + path, "Save Data File (*.sav *.srm *.fla *.eep);;All Files (*.*)")[0]
1636
- if (path == ""): return
1694
+ if dpath == "":
1695
+ path = Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=self.SETTINGS)
1696
+ path = os.path.splitext(path)[0]
1697
+
1698
+ add_date_time = self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled")
1699
+ if len(path) > 0 and add_date_time and add_date_time.lower() == "enabled":
1700
+ path += "_{:s}".format(datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
1701
+
1702
+ path += ".sav"
1703
+ path = QtWidgets.QFileDialog.getSaveFileName(self, "Backup Save Data", last_dir + "/" + path, "Save Data File (*.sav *.srm *.fla *.eep);;All Files (*.*)")[0]
1704
+ if (path == ""): return
1705
+ else:
1706
+ path = dpath
1637
1707
 
1638
1708
  verify_read = self.SETTINGS.value("VerifyData", default="enabled")
1639
1709
  if verify_read and verify_read.lower() == "enabled":
@@ -1654,37 +1724,22 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1654
1724
  rtc = (answer == QtWidgets.QMessageBox.Yes)
1655
1725
 
1656
1726
  bl_args = {}
1657
- if self.CONN.GetMode() == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]:
1658
- if self.CONN.GetFWBuildDate() == "": # Legacy Mode
1659
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="This feature is not supported in Legacy Mode.", standardButtons=QtWidgets.QMessageBox.Ok)
1660
- msgbox.exec()
1661
- return
1662
- cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1663
- if cart_type == 0 or ("dump_info" not in self.CONN.INFO or "batteryless_sram" not in self.CONN.INFO["dump_info"]):
1664
- if "detected_cart_type" not in self.STATUS: self.STATUS["detected_cart_type"] = ""
1665
- if self.STATUS["detected_cart_type"] == "":
1666
- self.STATUS["detected_cart_type"] = "WAITING_SAVE_READ"
1667
- self.STATUS["detect_cartridge_args"] = { "dpath":path }
1668
- self.STATUS["can_skip_message"] = True
1669
- self.DetectCartridge(checkSaveType=True)
1670
- return
1671
- cart_type = self.STATUS["detected_cart_type"]
1672
- if "detected_cart_type" in self.STATUS: del(self.STATUS["detected_cart_type"])
1673
-
1674
- if cart_type is False: # clicked Cancel button
1675
- return
1676
- elif cart_type is None or cart_type == 0:
1677
- QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "A compatible flash cartridge profile could not be auto-detected.", QtWidgets.QMessageBox.Ok)
1678
- return
1679
- self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
1680
-
1727
+ if \
1728
+ (self.CONN.GetMode() == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1729
+ (self.CONN.GetMode() == "DMG" and self.cmbDMGHeaderSaveTypeResult.currentIndex() < len(Util.DMG_Header_RAM_Sizes) and "Batteryless SRAM" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) \
1730
+ :
1681
1731
  if "detected_cart_type" in self.STATUS: del(self.STATUS["detected_cart_type"])
1682
1732
 
1683
1733
  if "dump_info" in self.CONN.INFO and "batteryless_sram" in self.CONN.INFO["dump_info"]:
1684
1734
  detected = self.CONN.INFO["dump_info"]["batteryless_sram"]
1685
1735
  else:
1686
1736
  detected = False
1687
- bl_args = self.GetBLArgs(rom_size=Util.AGB_Header_ROM_Sizes_Map[self.cmbAGBHeaderROMSizeResult.currentIndex()], detected=detected)
1737
+
1738
+ if self.CONN.GetMode() == "AGB":
1739
+ rom_size = Util.AGB_Header_ROM_Sizes_Map[self.cmbAGBHeaderROMSizeResult.currentIndex()]
1740
+ elif self.CONN.GetMode() == "DMG":
1741
+ rom_size = Util.DMG_Header_ROM_Sizes_Map[self.cmbDMGHeaderROMSizeResult.currentIndex()]
1742
+ bl_args = self.GetBLArgs(rom_size=rom_size, detected=detected)
1688
1743
  if bl_args is False: return
1689
1744
 
1690
1745
  self.SETTINGS.setValue(setting_name, os.path.dirname(path))
@@ -1714,13 +1769,46 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1714
1769
  if not self.CheckDeviceAlive(): return
1715
1770
  mode = self.CONN.GetMode()
1716
1771
 
1717
- if erase is True: dpath = ""
1772
+ path = ""
1773
+ if erase is True:
1774
+ dpath = ""
1775
+
1776
+ # Detect Cartridge needed?
1777
+ if not test and ( \
1778
+ (mode == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1779
+ (mode == "DMG" and self.cmbDMGHeaderSaveTypeResult.currentIndex() < len(Util.DMG_Header_RAM_Sizes) and "Batteryless SRAM" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) or \
1780
+ (mode == "DMG" and "Unlicensed Photo!" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) \
1781
+ ):
1782
+ if self.CONN.GetFWBuildDate() == "": # Legacy Mode
1783
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="This feature is not supported in Legacy Mode.", standardButtons=QtWidgets.QMessageBox.Ok)
1784
+ msgbox.exec()
1785
+ return
1786
+
1787
+ if mode == "AGB":
1788
+ cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1789
+ elif mode == "DMG":
1790
+ cart_type = self.cmbDMGCartridgeTypeResult.currentIndex()
1791
+ if cart_type == 0 or ("dump_info" not in self.CONN.INFO or "batteryless_sram" not in self.CONN.INFO["dump_info"]):
1792
+ if "detected_cart_type" not in self.STATUS: self.STATUS["detected_cart_type"] = ""
1793
+ if self.STATUS["detected_cart_type"] == "":
1794
+ self.STATUS["detected_cart_type"] = "WAITING_SAVE_WRITE"
1795
+ self.STATUS["detect_cartridge_args"] = { "dpath":dpath, "erase":erase }
1796
+ self.STATUS["can_skip_message"] = True
1797
+ self.DetectCartridge(checkSaveType=True)
1798
+ return
1799
+ cart_type = self.STATUS["detected_cart_type"]
1800
+ if "detected_cart_type" in self.STATUS: del(self.STATUS["detected_cart_type"])
1801
+
1802
+ if cart_type is False: # clicked Cancel button
1803
+ return
1804
+ elif cart_type is None or cart_type == 0 or not isinstance(cart_type, int):
1805
+ QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "A compatible flash cartridge profile could not be auto-detected.", QtWidgets.QMessageBox.Ok)
1806
+ return
1807
+ if mode == "AGB":
1808
+ self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
1809
+ elif mode == "DMG":
1810
+ self.cmbDMGCartridgeTypeResult.setCurrentIndex(cart_type)
1718
1811
 
1719
- if dpath == "":
1720
- path = Util.GenerateFileName(mode=mode, header=self.CONN.INFO, settings=self.SETTINGS)
1721
- path = os.path.splitext(path)[0]
1722
- path += ".sav"
1723
-
1724
1812
  if mode == "DMG":
1725
1813
  setting_name = "LastDirSaveDataDMG"
1726
1814
  last_dir = self.SETTINGS.value(setting_name)
@@ -1731,7 +1819,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1731
1819
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
1732
1820
  return
1733
1821
  cart_type = self.cmbDMGCartridgeTypeResult.currentIndex()
1734
- #save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(save_type)]
1735
1822
 
1736
1823
  elif mode == "AGB":
1737
1824
  setting_name = "LastDirSaveDataAGB"
@@ -1742,7 +1829,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1742
1829
  if save_type == 0:
1743
1830
  QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
1744
1831
  return
1745
- #save_size = Util.AGB_Header_Save_Sizes[save_type]
1746
1832
  cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1747
1833
  else:
1748
1834
  return
@@ -1772,6 +1858,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1772
1858
  msgbox.exec()
1773
1859
 
1774
1860
  if (mode == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1861
+ (mode == "DMG" and self.cmbDMGHeaderSaveTypeResult.currentIndex() < len(Util.DMG_Header_RAM_Sizes) and "Batteryless SRAM" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) or \
1862
+ (mode == "DMG" and self.cmbDMGHeaderSaveTypeResult.currentIndex() < len(Util.DMG_Header_RAM_Sizes) and "Unlicensed Photo!" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) or \
1775
1863
  ("8M DACS" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1776
1864
  (mode == "AGB" and "ereader" in self.CONN.INFO and self.CONN.INFO["ereader"] is True) or \
1777
1865
  (mode == "DMG" and "256M Multi Cart" in self.cmbDMGHeaderMapperResult.currentText() and not self.CONN.CanPowerCycleCart()):
@@ -1780,11 +1868,15 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1780
1868
  answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), "The cartridge’s save chip will be tested for potential problems as follows:\n- Read the same data multiple times\n- Writing and reading different test patterns\n\nPlease ensure the cartridge pins are freshly cleaned and the save data is backed up before proceeding.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok)
1781
1869
  if answer == QtWidgets.QMessageBox.Cancel: return
1782
1870
  else:
1871
+ if path == "":
1872
+ path = Util.GenerateFileName(mode=mode, header=self.CONN.INFO, settings=self.SETTINGS)
1873
+ path = os.path.splitext(path)[0]
1874
+ path += ".sav"
1783
1875
  path = QtWidgets.QFileDialog.getOpenFileName(self, "Restore Save Data", last_dir + "/" + path, "Save Data File (*.sav *.srm *.fla *.eep);;All Files (*.*)")[0]
1784
1876
  if not path == "": self.SETTINGS.setValue(setting_name, os.path.dirname(path))
1785
1877
  if (path == ""): return
1786
1878
 
1787
- if not erase and not test:
1879
+ if not erase and not test and len(path) > 0:
1788
1880
  filesize = os.path.getsize(path)
1789
1881
  if filesize == 0 or filesize > 0x200000: # reject too large files to avoid exploding RAM
1790
1882
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The size of this file is not supported.", QtWidgets.QMessageBox.Ok)
@@ -1807,7 +1899,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1807
1899
  buffer = bytearray([0xFF] * 0x20000)
1808
1900
  msg_text = "This {:s} cartridge currently has calibration data in place. It is strongly recommended to keep the existing calibration data.\n\nHow do you want to proceed?".format(cart_name)
1809
1901
  button_overwrite = msgbox.addButton(" &Erase everything ", QtWidgets.QMessageBox.ActionRole)
1810
- erase = False # Don’t just erase everything
1811
1902
  else:
1812
1903
  with open(path, "rb") as f: buffer = bytearray(f.read())
1813
1904
  msg_text = "This {:s} cartridge currently has calibration data in place that is different from this save file’s data. It is strongly recommended to keep the existing calibration data unless you actually need to restore it from a previous backup.\n\nWould you like to keep the existing calibration data, or overwrite it with data from the file you selected?".format(cart_name)
@@ -1830,6 +1921,56 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1830
1921
  answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg_text, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
1831
1922
  if answer == QtWidgets.QMessageBox.No: return
1832
1923
 
1924
+ elif mode == "DMG" and self.CONN.INFO["dump_info"]["header"]["mapper_raw"] == 0xFC:
1925
+ if self.CONN.GetFWBuildDate() == "": # Legacy Mode
1926
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="This cartridge is not supported in Legacy Mode.", standardButtons=QtWidgets.QMessageBox.Ok)
1927
+ msgbox.exec()
1928
+ return
1929
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="")
1930
+ button_keep = msgbox.addButton(" &Keep existing calibration data ", QtWidgets.QMessageBox.ActionRole)
1931
+ if "Unlicensed Photo!" not in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]:
1932
+ button_reset = msgbox.addButton(" &Force recalibration ", QtWidgets.QMessageBox.ActionRole)
1933
+ else:
1934
+ button_reset = None
1935
+ self.CONN.ReadInfo()
1936
+ cart_name = "Game Boy Camera"
1937
+ if self.CONN.INFO["db"] is not None:
1938
+ cart_name = self.CONN.INFO["db"]["gn"]
1939
+ if not test:
1940
+ if "gbcamera_calibration1" in self.CONN.INFO:
1941
+ if erase:
1942
+ buffer = bytearray([0x00] * 0x20000)
1943
+ if "Unlicensed Photo!" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]:
1944
+ buffer += bytearray([0xFF] * 0xE0000)
1945
+ msg_text = "This {:s} cartridge currently has calibration data in place.\n\nHow would you like to proceed?".format(cart_name)
1946
+ button_overwrite = msgbox.addButton(" &Erase everything ", QtWidgets.QMessageBox.ActionRole)
1947
+ else:
1948
+ with open(path, "rb") as f: buffer = bytearray(f.read())
1949
+ msg_text = "This {:s} cartridge currently has calibration data in place that is different from this save file’s data.\n\nHow would you like to proceed?".format(cart_name)
1950
+ button_overwrite = msgbox.addButton(" &Restore from save data ", QtWidgets.QMessageBox.ActionRole)
1951
+ button_cancel = msgbox.addButton("&Cancel", QtWidgets.QMessageBox.RejectRole)
1952
+ msgbox.setText(msg_text)
1953
+ msgbox.setDefaultButton(button_keep)
1954
+ msgbox.setEscapeButton(button_cancel)
1955
+
1956
+ if buffer[0x4FF2:0x5000] != self.CONN.INFO["gbcamera_calibration1"] or buffer[0x11FF2:0x12000] != self.CONN.INFO["gbcamera_calibration2"]:
1957
+ answer = msgbox.exec()
1958
+ if msgbox.clickedButton() == button_cancel:
1959
+ return
1960
+ elif msgbox.clickedButton() == button_keep:
1961
+ buffer[0x4FF2:0x5000] = self.CONN.INFO["gbcamera_calibration1"]
1962
+ buffer[0x11FF2:0x12000] = self.CONN.INFO["gbcamera_calibration2"]
1963
+ elif msgbox.clickedButton() == button_reset:
1964
+ buffer[0x4FF2:0x5000] = bytearray([0xAA] * 0xE)
1965
+ buffer[0x11FF2:0x12000] = bytearray([0xAA] * 0xE)
1966
+ elif msgbox.clickedButton() == button_overwrite:
1967
+ pass
1968
+ else:
1969
+ msg_text = "Warning: This {:s} cartridge may currently have calibration data in place. It is recommended to create a backup of the original save data first and store it in a safe place. That way the calibration data can be restored later.\n\nDo you still want to continue?".format(cart_name)
1970
+ answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg_text, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
1971
+ if answer == QtWidgets.QMessageBox.No: return
1972
+
1973
+
1833
1974
  verify_write = self.SETTINGS.value("VerifyData", default="enabled")
1834
1975
  if verify_write and verify_write.lower() == "enabled":
1835
1976
  verify_write = True
@@ -2049,30 +2190,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2049
2190
 
2050
2191
  else:
2051
2192
  bl_args = {}
2052
- if self.CONN.GetMode() == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]:
2053
- if self.CONN.GetFWBuildDate() == "": # Legacy Mode
2054
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="This feature is not supported in Legacy Mode.", standardButtons=QtWidgets.QMessageBox.Ok)
2055
- msgbox.exec()
2056
- return
2057
- cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
2058
- if cart_type == 0 or ("dump_info" not in self.CONN.INFO or "batteryless_sram" not in self.CONN.INFO["dump_info"]):
2059
- if "detected_cart_type" not in self.STATUS: self.STATUS["detected_cart_type"] = ""
2060
- if self.STATUS["detected_cart_type"] == "":
2061
- self.STATUS["detected_cart_type"] = "WAITING_SAVE_WRITE"
2062
- self.STATUS["detect_cartridge_args"] = { "dpath":path, "erase":erase }
2063
- self.STATUS["can_skip_message"] = True
2064
- self.DetectCartridge(checkSaveType=True)
2065
- return
2066
- cart_type = self.STATUS["detected_cart_type"]
2067
- if "detected_cart_type" in self.STATUS: del(self.STATUS["detected_cart_type"])
2068
-
2069
- if cart_type is False: # clicked Cancel button
2070
- return
2071
- elif cart_type is None or cart_type == 0:
2072
- QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "A compatible flash cartridge profile could not be auto-detected.", QtWidgets.QMessageBox.Ok)
2073
- return
2074
- self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
2075
-
2193
+ if \
2194
+ (mode == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and "Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
2195
+ (mode == "DMG" and self.cmbDMGHeaderSaveTypeResult.currentIndex() < len(Util.DMG_Header_RAM_Sizes) and "Batteryless SRAM" in Util.DMG_Header_RAM_Sizes[self.cmbDMGHeaderSaveTypeResult.currentIndex()]) \
2196
+ :
2076
2197
  if "detected_cart_type" in self.STATUS: del(self.STATUS["detected_cart_type"])
2077
2198
 
2078
2199
  if "dump_info" in self.CONN.INFO and "batteryless_sram" in self.CONN.INFO["dump_info"]:
@@ -2083,6 +2204,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2083
2204
  if bl_args is False: return
2084
2205
 
2085
2206
  args = { "path":path, "cart_type":cart_type, "override_voltage":False, "prefer_chip_erase":False, "fast_read_mode":True, "verify_write":verify_write, "fix_header":False, "fix_bootlogo":False, "mbc":mbc }
2207
+ args.update(bl_args)
2086
2208
  args.update({"bl_save":True, "flash_offset":bl_args["bl_offset"], "flash_size":bl_args["bl_size"]})
2087
2209
  if erase:
2088
2210
  args["path"] = ""
@@ -2090,12 +2212,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2090
2212
  self.STATUS["args"] = args
2091
2213
  self.CONN.FlashROM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
2092
2214
  #self.CONN._FlashROM(args=args)
2215
+
2093
2216
  else:
2094
- #cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
2095
2217
  args = { "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "rtc_advance":rtc_advance, "erase":erase, "verify_write":verify_write, "cart_type":cart_type }
2096
2218
  if buffer is not None:
2097
2219
  args["buffer"] = buffer
2098
2220
  args["path"] = None
2221
+ args["erase"] = False
2099
2222
  self.STATUS["args"] = args
2100
2223
  self.CONN.RestoreRAM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
2101
2224
  #args = { "mode":3, "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "rtc_advance":rtc_advance, "erase":erase, "verify_write":verify_write }
@@ -2118,11 +2241,18 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2118
2241
  qt_app.processEvents()
2119
2242
 
2120
2243
  def GetBLArgs(self, rom_size, detected=False):
2121
- locs = [ 0x3C0000, 0x7C0000, 0xFC0000, 0x1FC0000 ]
2122
- lens = [ 0x2000, 0x8000, 0x10000, 0x20000 ]
2123
- temp = self.SETTINGS.value("BatterylessSramLocations{:s}".format(self.CONN.GetMode()), "[]")
2244
+ mode = self.CONN.GetMode()
2245
+ if mode == "AGB":
2246
+ locs = [ 0x3C0000, 0x7C0000, 0xFC0000, 0x1FC0000 ]
2247
+ lens = [ 0x2000, 0x8000, 0x10000, 0x20000 ]
2248
+ elif mode == "DMG":
2249
+ locs = [ 0xD0000, 0x100000, 0x110000, 0x1D0000, 0x1E0000, 0x210000, 0x3D0000 ]
2250
+ lens = [ 0x2000, 0x8000, 0x10000, 0x20000 ]
2251
+
2252
+ temp = self.SETTINGS.value("BatterylessSramLocations{:s}".format(mode), "[]")
2124
2253
  loc_index = None
2125
2254
  len_index = None
2255
+ lay_index = None
2126
2256
 
2127
2257
  try:
2128
2258
  temp = json.loads(temp)
@@ -2139,15 +2269,41 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2139
2269
  try:
2140
2270
  loc_index = locs.index(detected["bl_offset"])
2141
2271
  len_index = lens.index(detected["bl_size"])
2142
- intro_msg = "In order to access Batteryless SRAM save data, its ROM location and size must\nbe specified. The previously detected parameters have been pre-selected.\nPlease adjust if necessary, then click “OK” to continue."
2272
+ intro_msg = "In order to access Batteryless SRAM save data, its ROM location and size must be specified.\n\nThe previously detected parameters have been pre-selected. Please adjust if necessary, then click “OK” to continue."
2143
2273
  except:
2144
2274
  detected = False
2145
2275
  if detected is False:
2146
- intro_msg = "In order to access Batteryless SRAM save data, its ROM location and size must\nbe specified.\n\n⚠️ The required parameters could not be auto-detected.\nPlease enter the ROM location and size manually below."
2276
+ intro_msg = "In order to access Batteryless SRAM save data, its ROM location and size must be specified.\n\n"
2277
+ # if mode == "AGB":
2278
+ # max_size = self.cmbAGBHeaderROMSizeResult.currentText().replace(" ", " ")
2279
+ # elif mode == "DMG":
2280
+ # max_size = self.cmbDMGHeaderROMSizeResult.currentText().replace(" ", " ")
2281
+ intro_msg2 = "⚠️ The required parameters could not be auto-detected. Please enter the ROM location and size manually below. Note that wrong values can corrupt your game upon writing, so having a full ROM backup is recommended."
2282
+
2283
+ if mode == "DMG":
2284
+ # Load database of observed configurations from various bootlegs
2285
+ preselect = {}
2286
+ if os.path.exists(Util.CONFIG_PATH + "/db_DMG_bl.json"):
2287
+ with open(Util.CONFIG_PATH + "/db_DMG_bl.json", "r") as f:
2288
+ try:
2289
+ preselect = json.loads(f.read())
2290
+ except Exception as e:
2291
+ print("ERROR: Couldn’t load the database of batteryless SRAM configurations.", e, sep="\n")
2292
+
2293
+ try:
2294
+ if self.CONN.INFO["dump_info"]["header"]["game_title"] in list(preselect.keys()):
2295
+ loc_index = locs.index(preselect[self.CONN.INFO["dump_info"]["header"]["game_title"]][0])
2296
+ len_index = lens.index(preselect[self.CONN.INFO["dump_info"]["header"]["game_title"]][1])
2297
+ lay_index = preselect[self.CONN.INFO["dump_info"]["header"]["game_title"]][2]
2298
+ intro_msg2 = "The required parameters were pre-selected based on the ROM title “" + self.CONN.INFO["dump_info"]["header"]["game_title"] + "”. These may still be inaccurate, so you can adjust them below if necessary. Note that wrong values can corrupt your game when writing, so having a full " + max_size + " ROM backup is recommended."
2299
+ except:
2300
+ pass
2301
+
2302
+ intro_msg += intro_msg2
2147
2303
 
2148
2304
  try:
2149
2305
  if loc_index is None:
2150
- loc_index = locs.index(int(self.SETTINGS.value("BatterylessSramLastLocation{:s}".format(self.CONN.GetMode()))))
2306
+ loc_index = locs.index(int(self.SETTINGS.value("BatterylessSramLastLocation{:s}".format(mode))))
2151
2307
  except:
2152
2308
  pass
2153
2309
 
@@ -2158,7 +2314,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2158
2314
  if l + 0x40000 >= rom_size: break
2159
2315
  loc_index += 1
2160
2316
  if loc_index >= len(locs): loc_index = len(locs) - 1
2161
- if len_index is None: len_index = 2
2317
+ if len_index is None:
2318
+ if mode == "AGB":
2319
+ len_index = 2
2320
+ elif mode == "DMG":
2321
+ len_index = 1
2322
+ if lay_index is None:
2323
+ lay_index = 2
2162
2324
 
2163
2325
  dlg_args = {
2164
2326
  "title":"Batteryless SRAM Parameters",
@@ -2169,6 +2331,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2169
2331
  [ "len", "cmb", "Size:", [ Util.formatFileSize(size=s, asInt=True) for s in lens ], len_index ],
2170
2332
  ]
2171
2333
  }
2334
+ if mode == "DMG":
2335
+ dlg_args["params"].append(
2336
+ [ "layout", "cmb", "Layout:", [ "Continuous", "First half of ROM bank", "Second half of ROM bank" ], lay_index ]
2337
+ )
2338
+
2172
2339
  dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
2173
2340
  if dlg.exec_() == 1:
2174
2341
  result = dlg.GetResult()
@@ -2183,10 +2350,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2183
2350
  else:
2184
2351
  bl_args["bl_offset"] = locs[result["loc"].currentIndex()]
2185
2352
  bl_args["bl_size"] = lens[result["len"].currentIndex()]
2353
+ if mode == "DMG":
2354
+ bl_args["bl_layout"] = result["layout"].currentIndex()
2186
2355
 
2187
2356
  locs.append(bl_args["bl_offset"])
2188
- self.SETTINGS.setValue("BatterylessSramLocations{:s}".format(self.CONN.GetMode()), json.dumps(locs))
2189
- self.SETTINGS.setValue("BatterylessSramLastLocation{:s}".format(self.CONN.GetMode()), json.dumps(bl_args["bl_offset"]))
2357
+ self.SETTINGS.setValue("BatterylessSramLocations{:s}".format(mode), json.dumps(locs))
2358
+ self.SETTINGS.setValue("BatterylessSramLastLocation{:s}".format(mode), json.dumps(bl_args["bl_offset"]))
2190
2359
  ret = bl_args
2191
2360
  else:
2192
2361
  ret = False
@@ -2432,8 +2601,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2432
2601
  self.CONN.SetMode("DMG")
2433
2602
  elif self.optAGB.isChecked() and (mode == "DMG" or mode == None):
2434
2603
  self.CONN.SetMode("AGB")
2435
- except BrokenPipeError:
2436
- msg = "Failed to turn on the cartridge power.\n\nAs a workaround, try to disable the \"Automatic cartridge power off\" setting and then hotplug the cartridge after clicking \"Refresh\"."
2604
+ except (BrokenPipeError, SerialException):
2605
+ msg = "Failed to turn on the cartridge power.\n\nThe Automatic cartridge power off setting has therefore been disabled. Please re-connect the device and try again."
2606
+ self.mnuConfig.actions()[5].setChecked(False)
2607
+ self.SETTINGS.setValue("AutoPowerOff", "0")
2437
2608
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
2438
2609
  self.DisconnectDevice()
2439
2610
  return False
@@ -2465,14 +2636,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2465
2636
 
2466
2637
  try:
2467
2638
  data = self.CONN.ReadInfo(setPinsAsInputs=True)
2468
- except SerialException:
2639
+ except (BrokenPipeError, SerialException):
2640
+ self.LimitBaudRateGBxCartRW()
2469
2641
  self.DisconnectDevice()
2470
2642
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The connection to the device was lost while trying to read the ROM header. This may happen if the inserted cartridge issues a short circuit or its peak power draw is too high.\n\nAs a potential workaround for the latter, you can try hotswapping the cartridge:\n1. Remove the cartridge from the device.\n2. Reconnect the device and select mode.\n3. Then insert the cartridge and click “{:s}”.".format(self.btnHeaderRefresh.text().replace("&", "")), QtWidgets.QMessageBox.Ok)
2471
2643
  return False
2472
2644
 
2473
2645
  if data == False or len(data) == 0:
2646
+ self.LimitBaudRateGBxCartRW()
2474
2647
  self.DisconnectDevice()
2475
- QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "Invalid response from the device.", QtWidgets.QMessageBox.Ok)
2648
+ QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "Invalid response from the device. Please re-connect the USB cable.", QtWidgets.QMessageBox.Ok)
2476
2649
  return False
2477
2650
 
2478
2651
  if self.CONN.CheckROMStable() is False and resetStatus:
@@ -2572,10 +2745,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2572
2745
  self.lblDMGHeaderBootlogoResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
2573
2746
  self.lblDMGHeaderROMChecksumResult.setText("")
2574
2747
  self.lblDMGHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
2575
- # cart_types = self.CONN.GetSupportedCartridgesDMG()
2576
- # for i in range(0, len(cart_types[0])):
2577
- # if "command_set" in cart_types[1][i] and cart_types[1][i]["command_set"] == "BLAZE_XPLODER":
2578
- # self.cmbDMGCartridgeTypeResult.setCurrentIndex(i)
2579
2748
  elif data["mapper_raw"] == 0x205: # Datel
2580
2749
  self.lblDMGHeaderRtcResult.setText("")
2581
2750
  self.lblDMGHeaderBootlogoResult.setText("")
@@ -2594,7 +2763,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2594
2763
  if "logo_sachen" in data:
2595
2764
  data["logo_sachen"].putpalette([ 255, 255, 255, 128, 128, 128 ])
2596
2765
  try:
2597
- self.lblDMGHeaderBootlogoResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo_sachen"].convert("RGBA"))))
2766
+ self.lblDMGHeaderBootlogoResult.setPixmap(Util.bitmap2pixmap(data["logo_sachen"]))
2598
2767
  except:
2599
2768
  pass
2600
2769
  else:
@@ -2606,7 +2775,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2606
2775
  else:
2607
2776
  data["logo"].putpalette([ 255, 255, 255, 251, 0, 24 ])
2608
2777
  try:
2609
- self.lblDMGHeaderBootlogoResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo"].convert("RGBA"))))
2778
+ self.lblDMGHeaderBootlogoResult.setPixmap(Util.bitmap2pixmap(data["logo"]))
2610
2779
  except:
2611
2780
  pass
2612
2781
 
@@ -2719,7 +2888,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2719
2888
  else:
2720
2889
  data["logo"].putpalette([ 255, 255, 255, 251, 0, 24 ])
2721
2890
  try:
2722
- self.lblAGBHeaderBootlogoResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo"].convert("RGBA"))))
2891
+ self.lblAGBHeaderBootlogoResult.setPixmap(Util.bitmap2pixmap(data["logo"]))
2723
2892
  except:
2724
2893
  pass
2725
2894
 
@@ -2743,6 +2912,22 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2743
2912
  if data['game_title'][:11] == "YJencrypted" and resetStatus:
2744
2913
  QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge may be protected against reading or writing a ROM. If you don’t want to risk this cartridge to render itself unusable, please do not try to write a new ROM to it.", QtWidgets.QMessageBox.Ok)
2745
2914
 
2915
+ def LimitBaudRateGBxCartRW(self):
2916
+ if self.CONN.GetName() == "GBxCart RW" and str(self.SETTINGS.value("AutoLimitBaudRate", default="enabled")).lower() == "enabled" and str(self.SETTINGS.value("LimitBaudRate", default="disabled")).lower() == "disabled":
2917
+ Util.dprint("Setting “" + self.mnuConfig.actions()[5].text().replace("&", "") + "” to “enabled”")
2918
+ self.mnuConfig.actions()[5].setChecked(True)
2919
+ self.SETTINGS.setValue("LimitBaudRate", "enabled")
2920
+ Util.dprint("Setting “" + self.mnuConfig.actions()[8].text().replace("&", "") + "” to “0”")
2921
+ self.mnuConfig.actions()[5].setChecked(False)
2922
+ self.SETTINGS.setValue("AutoPowerOff", "0")
2923
+ try:
2924
+ self.CONN.ChangeBaudRate(baudrate=1000000)
2925
+ except:
2926
+ try:
2927
+ self.DisconnectDevice()
2928
+ except:
2929
+ pass
2930
+
2746
2931
  def DetectCartridge(self, checkSaveType=True):
2747
2932
  if not self.CheckDeviceAlive(): return
2748
2933
  if not self.CONN.CheckROMStable():
@@ -2775,11 +2960,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2775
2960
  limitVoltage = str(self.SETTINGS.value("AutoDetectLimitVoltage", default="disabled")).lower() == "enabled"
2776
2961
  if ret is False:
2777
2962
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "An error occured while trying to analyze the cartridge and you may need to physically reconnect the device.\n\nThis cartridge may not be auto-detectable, please select the cartridge type manually.", QtWidgets.QMessageBox.Ok)
2963
+ self.LimitBaudRateGBxCartRW()
2778
2964
  self.DisconnectDevice()
2779
2965
  cart_type = None
2780
2966
  else:
2781
2967
  (header, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, _, flash_id, detected_size) = ret
2782
-
2968
+
2783
2969
  # Save Type
2784
2970
  if not self.STATUS["can_skip_message"]:
2785
2971
  try:
@@ -2805,6 +2991,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2805
2991
  except Exception as e:
2806
2992
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="An unknown error occured. Please try again.\n\n" + str(e), standardButtons=QtWidgets.QMessageBox.Ok)
2807
2993
  msgbox.exec()
2994
+ self.LimitBaudRateGBxCartRW()
2808
2995
  return
2809
2996
 
2810
2997
  try:
@@ -2837,7 +3024,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2837
3024
  temp = ""
2838
3025
  if not self.STATUS["can_skip_message"] and save_type is not False and save_type is not None:
2839
3026
  if save_chip is not None:
2840
- temp = "{:s} ({:s})".format(Util.AGB_Header_Save_Types[save_type], save_chip)
3027
+ if save_type == 5 and "Unlicensed" in save_chip and "data" in self.CONN.INFO and self.CONN.INFO["data"] == bytearray([0xFF] * len(self.CONN.INFO["data"])):
3028
+ temp = "{:s} or {:s} ({:s})".format(Util.AGB_Header_Save_Types[4], Util.AGB_Header_Save_Types[5], save_chip)
3029
+ else:
3030
+ temp = "{:s} ({:s})".format(Util.AGB_Header_Save_Types[save_type], save_chip)
2841
3031
  else:
2842
3032
  if self.CONN.GetMode() == "DMG":
2843
3033
  try:
@@ -2858,7 +3048,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2858
3048
  pass
2859
3049
 
2860
3050
  if save_type == 0:
2861
- msg_save_type_s = "<b>Save Type:</b> None or unknown (no save data detected)<br>"
3051
+ if "Unknown" in save_chip:
3052
+ msg_save_type_s = "<b>Save Type:</b> {:s}<br>".format(save_chip)
3053
+ else:
3054
+ msg_save_type_s = "<b>Save Type:</b> None or unknown (no save data detected)<br>"
2862
3055
  else:
2863
3056
  if sram_unstable and "SRAM" in temp:
2864
3057
  msg_save_type_s = "<b>Save Type:</b> {:s} <span style=\"color: red;\">(not stable or not battery-backed)</span><br>".format(temp)
@@ -2933,10 +3126,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2933
3126
  else:
2934
3127
  msg_flash_id_s_limit = ""
2935
3128
  msg_flash_id_s = "<br><b>Flash ID Check{:s}:</b><pre style=\"font-size: 8pt; margin: 0;\">{:s}</pre>".format(msg_flash_id_s_limit, flash_id[:-1])
3129
+ if not is_generic:
2936
3130
  if cfi_s != "":
2937
3131
  msg_cfi_s = "<br><b>Common Flash Interface Data:</b><br>{:s}<br><br>".format(cfi_s.replace("\n", "<br>"))
2938
3132
  else:
2939
- msg_cfi_s = "<br><b>Common Flash Interface Data:</b> No data provided<br><br>"
3133
+ msg_cfi_s = "<br><b>Common Flash Interface Data:</b> Not available<br><br>"
2940
3134
 
2941
3135
  if msg_cart_type_s_detail == "": msg_cart_type_s_detail = msg_cart_type_s
2942
3136
  self.SetProgressBars(min=0, max=100, value=100)
@@ -2986,7 +3180,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2986
3180
  if not dontShowAgain or not self.STATUS["can_skip_message"]:
2987
3181
  temp = "{:s}{:s}{:s}{:s}{:s}{:s}".format(msg, msg_flash_size_s, msg_save_type_s, msg_flash_mapper_s, msg_cart_type_s, msg_gbmem)
2988
3182
  temp = temp[:-4]
2989
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=temp)
3183
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s} | {:s}".format(APPNAME, VERSION, self.CONN.GetFullNameLabel()), text=temp)
2990
3184
  msgbox.setTextFormat(QtCore.Qt.RichText)
2991
3185
  button_ok = msgbox.addButton("&OK", QtWidgets.QMessageBox.ActionRole)
2992
3186
  button_details = msgbox.addButton("&Details", QtWidgets.QMessageBox.ActionRole)
@@ -3022,7 +3216,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
3022
3216
  return
3023
3217
 
3024
3218
  if not found_supported or show_details is True:
3025
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION))
3219
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s} | {:s}".format(APPNAME, VERSION, self.CONN.GetFullNameLabel()))
3026
3220
  button_ok = msgbox.addButton("&OK", QtWidgets.QMessageBox.ActionRole)
3027
3221
  msgbox.setDefaultButton(button_ok)
3028
3222
  msgbox.setEscapeButton(button_ok)
@@ -3091,7 +3285,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
3091
3285
  else:
3092
3286
  self.FlashROM()
3093
3287
  elif waiting == "WAITING_SAVE_READ":
3094
- self.BackupRAM()
3288
+ if "detect_cartridge_args" in self.STATUS:
3289
+ self.BackupRAM(dpath=self.STATUS["detect_cartridge_args"]["dpath"])
3290
+ del(self.STATUS["detect_cartridge_args"])
3291
+ else:
3292
+ self.BackupRAM()
3095
3293
  elif waiting == "WAITING_SAVE_WRITE":
3096
3294
  if "detect_cartridge_args" in self.STATUS:
3097
3295
  self.WriteRAM(dpath=self.STATUS["detect_cartridge_args"]["dpath"], erase=self.STATUS["detect_cartridge_args"]["erase"], skip_warning=True)
@@ -3142,6 +3340,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
3142
3340
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=str(args["error"]), standardButtons=QtWidgets.QMessageBox.Ok)
3143
3341
  if not '\n' in str(args["error"]): msgbox.setTextFormat(QtCore.Qt.RichText)
3144
3342
  msgbox.exec()
3343
+ self.LimitBaudRateGBxCartRW()
3145
3344
  return
3146
3345
 
3147
3346
  self.grpDMGCartridgeInfo.setEnabled(False)
@@ -3250,16 +3449,20 @@ class FlashGBX_GUI(QtWidgets.QWidget):
3250
3449
 
3251
3450
  if "info_type" in args.keys() and "info_msg" in args.keys():
3252
3451
  if args["info_type"] == "msgbox_critical":
3253
- Util.dprint("Displaying Message Box:\n----\n{:s} {:s}\n----\n{:s}\n----".format(APPNAME, VERSION, args["info_msg"]))
3254
- self.WriteDebugLog()
3255
3452
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=args["info_msg"], standardButtons=QtWidgets.QMessageBox.Ok)
3453
+ Util.dprint("Queueing Message Box {:s}:\n----\n{:s} {:s}\n----\n{:s}\n----".format(str(msgbox), APPNAME, VERSION, args["info_msg"]))
3256
3454
  if not '\n' in args["info_msg"]: msgbox.setTextFormat(QtCore.Qt.RichText)
3257
- msgbox.exec()
3258
- if "fatal" in args: self.DisconnectDevice()
3455
+ self.MSGBOX_QUEUE.put(msgbox)
3456
+ self.WriteDebugLog()
3457
+ if "fatal" in args:
3458
+ self.LimitBaudRateGBxCartRW()
3459
+ self.DisconnectDevice()
3259
3460
  elif args["info_type"] == "msgbox_information":
3260
3461
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=args["info_msg"], standardButtons=QtWidgets.QMessageBox.Ok)
3462
+ Util.dprint("Queueing Message Box {:s}:\n----\n{:s} {:s}\n----\n{:s}\n----".format(str(msgbox), APPNAME, VERSION, args["info_msg"]))
3261
3463
  if not '\n' in args["info_msg"]: msgbox.setTextFormat(QtCore.Qt.RichText)
3262
- msgbox.exec()
3464
+ #msgbox.exec()
3465
+ self.MSGBOX_QUEUE.put(msgbox)
3263
3466
  elif args["info_type"] == "label":
3264
3467
  self.lblStatus4a.setText(args["info_msg"])
3265
3468
 
@@ -3353,9 +3556,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
3353
3556
  if self.CONN.GetMode() == "DMG":
3354
3557
  header = self.CONN.ReadInfo(setPinsAsInputs=True)
3355
3558
  if header["mapper_raw"] == 252: # GBD
3356
- args = { "path":None, "mbc":252, "save_type":4, "rtc":False }
3559
+ args = { "path":None, "mbc":252, "save_type":header["ram_size_raw"], "rtc":False }
3560
+ self.lblStatus4a.setText("Loading data, please wait...")
3561
+ qt_app.processEvents()
3357
3562
  self.CONN.BackupRAM(fncSetProgress=False, args=args)
3358
3563
  data = self.CONN.INFO["data"]
3564
+ self.lblStatus4a.setText("Ready.")
3359
3565
 
3360
3566
  self.CAMWIN = None
3361
3567
  self.CAMWIN = PocketCameraWindow(self, icon=self.windowIcon(), file=data, config_path=Util.CONFIG_PATH, app_path=Util.APP_PATH)
@@ -3416,6 +3622,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
3416
3622
 
3417
3623
  def closeEvent(self, event):
3418
3624
  self.DisconnectDevice()
3625
+
3626
+ self.MSGBOX_TIMER.stop()
3627
+ self.MSGBOX_DISPLAYING = True
3628
+ with self.MSGBOX_QUEUE.mutex:
3629
+ self.MSGBOX_QUEUE.queue.clear()
3419
3630
  event.accept()
3420
3631
 
3421
3632
  def run(self):