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/DataTransfer.py +1 -1
- FlashGBX/FlashGBX.py +4 -3
- FlashGBX/FlashGBX_CLI.py +36 -20
- FlashGBX/FlashGBX_GUI.py +359 -148
- FlashGBX/Flashcart.py +3 -2
- FlashGBX/GBMemory.py +4 -2
- FlashGBX/LK_Device.py +327 -233
- FlashGBX/Mapper.py +6 -27
- FlashGBX/PocketCamera.py +1 -1
- FlashGBX/PocketCameraWindow.py +32 -2
- FlashGBX/RomFileAGB.py +5 -2
- FlashGBX/RomFileDMG.py +8 -3
- FlashGBX/UserInputDialog.py +1 -1
- FlashGBX/Util.py +33 -11
- FlashGBX/__main__.py +1 -1
- FlashGBX/fw_GBFlash.py +20 -3
- FlashGBX/fw_GBxCartRW_v1_3.py +1 -1
- FlashGBX/fw_GBxCartRW_v1_4.py +1 -1
- FlashGBX/fw_JoeyJr.py +10 -3
- FlashGBX/hw_GBFlash.py +3 -3
- FlashGBX/hw_GBxCartRW.py +16 -16
- FlashGBX/hw_JoeyJr.py +3 -3
- FlashGBX/pyside.py +1 -1
- FlashGBX/res/config.zip +0 -0
- FlashGBX/res/fw_GBFlash.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4a.zip +0 -0
- FlashGBX/res/fw_JoeyJr.zip +0 -0
- {FlashGBX-4.3.dist-info → flashgbx-4.6.dist-info}/METADATA +103 -57
- flashgbx-4.6.dist-info/RECORD +43 -0
- {FlashGBX-4.3.dist-info → flashgbx-4.6.dist-info}/WHEEL +1 -1
- FlashGBX-4.3.dist-info/RECORD +0 -43
- {FlashGBX-4.3.dist-info → flashgbx-4.6.dist-info}/entry_points.txt +0 -0
- {FlashGBX-4.3.dist-info → flashgbx-4.6.dist-info/licenses}/LICENSE +0 -0
- {FlashGBX-4.3.dist-info → flashgbx-4.6.dist-info}/top_level.txt +0 -0
FlashGBX/FlashGBX_GUI.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# FlashGBX
|
|
3
|
-
# Author: Lesserkuma (github.com/
|
|
3
|
+
# Author: Lesserkuma (github.com/Lesserkuma)
|
|
4
4
|
|
|
5
|
-
import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser,
|
|
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/
|
|
616
|
-
site = "https://github.com/
|
|
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
|
|
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
|
|
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/
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
self.
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
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
|
-
|
|
1636
|
-
|
|
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
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
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
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
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
|
|
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
|
|
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(
|
|
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:
|
|
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(
|
|
2189
|
-
self.SETTINGS.setValue("BatterylessSramLastLocation{:s}".format(
|
|
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\
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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>
|
|
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.
|
|
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
|
-
|
|
3258
|
-
|
|
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":
|
|
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):
|