FlashGBX 3.37__py3-none-any.whl → 4.0.1__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
@@ -2,7 +2,7 @@
2
2
  # FlashGBX
3
3
  # Author: Lesserkuma (github.com/lesserkuma)
4
4
 
5
- import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, pkg_resources, threading
5
+ import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, pkg_resources, threading, calendar
6
6
  from .pyside import QtCore, QtWidgets, QtGui, QApplication
7
7
  from PIL.ImageQt import ImageQt
8
8
  from serial import SerialException
@@ -12,8 +12,8 @@ from .PocketCameraWindow import PocketCameraWindow
12
12
  from .UserInputDialog import UserInputDialog
13
13
  from .Util import APPNAME, VERSION, VERSION_PEP440
14
14
  from . import Util
15
- from . import hw_GBxCartRW, hw_GBxCartRW_ofw
16
- hw_devices = [hw_GBxCartRW, hw_GBxCartRW_ofw]
15
+ from . import hw_GBxCartRW, hw_GBFlash, hw_JoeyJr
16
+ hw_devices = [hw_GBxCartRW, hw_GBFlash, hw_JoeyJr]
17
17
 
18
18
  class FlashGBX_GUI(QtWidgets.QWidget):
19
19
  CONN = None
@@ -35,7 +35,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
35
35
  Util.APP_PATH = args['app_path']
36
36
  self.SETTINGS = Util.IniSettings(path=args["config_path"] + "/settings.ini")
37
37
  self.FLASHCARTS = args["flashcarts"]
38
- self.PROGRESS = Util.Progress(self.UpdateProgress)
38
+ self.PROGRESS = Util.Progress(self.UpdateProgress, self.WaitProgress)
39
39
 
40
40
  self.setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }")
41
41
  self.setWindowIcon(QtGui.QIcon(Util.APP_PATH + "/res/icon.ico"))
@@ -58,7 +58,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
58
58
  self.layout_left.addWidget(self.grpAGBCartridgeInfo)
59
59
 
60
60
  # Actions
61
- self.grpActions = QtWidgets.QGroupBox("Options")
61
+ self.grpActions = QtWidgets.QGroupBox("Functions")
62
62
  self.grpActionsLayout = QtWidgets.QVBoxLayout()
63
63
  self.grpActionsLayout.setContentsMargins(-1, 3, -1, -1)
64
64
 
@@ -174,43 +174,61 @@ class FlashGBX_GUI(QtWidgets.QWidget):
174
174
  # List devices
175
175
  self.layout_devices = QtWidgets.QHBoxLayout()
176
176
  self.lblDevice = QtWidgets.QLabel()
177
- self.lblDevice.mousePressEvent = self.WriteDebugLog
177
+ self.lblDevice.mousePressEvent = lambda event: [ self.WriteDebugLog(event, open_log=True) ]
178
+ self.lblDevice.setToolTip("Click here to generate a log file for debugging")
179
+ self.lblDevice.setCursor(QtCore.Qt.PointingHandCursor)
178
180
  self.cmbDevice = QtWidgets.QComboBox()
179
181
  self.cmbDevice.setStyleSheet("QComboBox { border: 0; margin: 0; padding: 0; max-width: 0px; }")
180
182
  self.layout_devices.addWidget(self.lblDevice)
181
183
  self.layout_devices.addWidget(self.cmbDevice)
182
184
  self.layout_devices.addStretch()
183
185
 
184
- btnText = "Too&ls"
185
- self.btnTools = QtWidgets.QPushButton(btnText)
186
- btnWidth = self.btnTools.fontMetrics().boundingRect(btnText).width() + 24
187
- if platform.system() == "Darwin": btnWidth += 12
188
- self.btnTools.setMaximumWidth(btnWidth)
189
- self.mnuTools = QtWidgets.QMenu()
186
+ #btnText = "Too&ls"
187
+ #self.btnTools = QtWidgets.QPushButton(btnText)
188
+ #btnWidth = self.btnTools.fontMetrics().boundingRect(btnText).width() + 24
189
+ #if platform.system() == "Darwin": btnWidth += 12
190
+ #self.btnTools.setMaximumWidth(btnWidth)
191
+ self.mnuTools = QtWidgets.QMenu("&Tools")
190
192
  self.mnuTools.addAction("Game Boy &Camera Album Viewer", self.ShowPocketCameraWindow)
191
193
  self.mnuTools.addSeparator()
192
194
  self.mnuTools.addAction("Firmware &Updater", self.ShowFirmwareUpdateWindow)
193
- self.btnTools.setMenu(self.mnuTools)
194
-
195
- btnText = "C&onfig"
196
- self.btnConfig = QtWidgets.QPushButton(btnText)
197
- btnWidth = self.btnConfig.fontMetrics().boundingRect(btnText).width() + 24
198
- if platform.system() == "Darwin": btnWidth += 12
199
- self.btnConfig.setMaximumWidth(btnWidth)
200
-
201
- self.mnuConfig = QtWidgets.QMenu()
195
+ #self.btnTools.setMenu(self.mnuTools)
196
+
197
+ #btnText = "C&onfig"
198
+ #self.btnConfig = QtWidgets.QPushButton(btnText)
199
+ #btnWidth = self.btnConfig.fontMetrics().boundingRect(btnText).width() + 24
200
+ #if platform.system() == "Darwin": btnWidth += 12
201
+ #self.btnConfig.setMaximumWidth(btnWidth)
202
+ self.mnuConfig = QtWidgets.QMenu("&Settings")
202
203
  self.mnuConfig.addAction("Check for &updates on application startup", lambda: [ self.EnableUpdateCheck() ])
203
204
  self.mnuConfig.addAction("&Append date && time to filename of save data backups", lambda: self.SETTINGS.setValue("SaveFileNameAddDateTime", str(self.mnuConfig.actions()[1].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
204
205
  self.mnuConfig.addAction("Prefer full &chip erase", lambda: self.SETTINGS.setValue("PreferChipErase", str(self.mnuConfig.actions()[2].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
205
206
  self.mnuConfig.addAction("&Verify transferred data", lambda: self.SETTINGS.setValue("VerifyData", str(self.mnuConfig.actions()[3].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
206
- self.mnuConfig.addAction("&Limit voltage to 3.3V when detecting Game Boy flash cartridges", lambda: self.SETTINGS.setValue("AutoDetectLimitVoltage", str(self.mnuConfig.actions()[4].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
207
- self.mnuConfig.addAction("Limit &baud rate to 1Mbps for GBxCart RW v1.4 devices", lambda: [ self.SETTINGS.setValue("LimitBaudRate", str(self.mnuConfig.actions()[5].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")), self.SetLimitBaudRate() ])
207
+ self.mnuConfig.addAction("&Limit voltage when analyzing Game Boy carts", lambda: self.SETTINGS.setValue("AutoDetectLimitVoltage", str(self.mnuConfig.actions()[4].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
208
+ self.mnuConfig.addAction("Limit &baud rate to 1Mbps", lambda: [ self.SETTINGS.setValue("LimitBaudRate", str(self.mnuConfig.actions()[5].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")), self.SetLimitBaudRate() ])
208
209
  self.mnuConfig.addAction("Always &generate ROM dump reports", lambda: self.SETTINGS.setValue("GenerateDumpReports", str(self.mnuConfig.actions()[6].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
209
210
  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")))
211
+ 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() ])
212
+ 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")))
210
213
  self.mnuConfig.addSeparator()
211
- self.mnuConfig.addAction("Re-&enable suppressed messages", self.ReEnableMessages)
214
+ self.mnuConfigReadModeAGB = QtWidgets.QMenu("&Read Method (Game Boy Advance)")
215
+ self.mnuConfigReadModeAGB.addAction("S&tream", lambda: [ self.SETTINGS.setValue("AGBReadMethod", str(self.mnuConfigReadModeAGB.actions()[1].isChecked()).lower().replace("true", "2")), self.SetAGBReadMethod() ])
216
+ self.mnuConfigReadModeAGB.addAction("&Single", lambda: [ self.SETTINGS.setValue("AGBReadMethod", str(self.mnuConfigReadModeAGB.actions()[0].isChecked()).lower().replace("true", "0")), self.SetAGBReadMethod() ])
217
+ self.mnuConfigReadModeAGB.actions()[0].setCheckable(True)
218
+ self.mnuConfigReadModeAGB.actions()[1].setCheckable(True)
219
+ self.mnuConfigReadModeAGB.actions()[0].setChecked(self.SETTINGS.value("AGBReadMethod", default="2") == "2")
220
+ self.mnuConfigReadModeAGB.actions()[1].setChecked(self.SETTINGS.value("AGBReadMethod", default="2") == "0")
221
+ self.mnuConfigReadModeDMG = QtWidgets.QMenu("&Read Method (Game Boy)")
222
+ self.mnuConfigReadModeDMG.addAction("&Normal", lambda: [ self.SETTINGS.setValue("DMGReadMethod", str(self.mnuConfigReadModeAGB.actions()[1].isChecked()).lower().replace("true", "1")), self.SetDMGReadMethod() ])
223
+ self.mnuConfigReadModeDMG.addAction("&Delayed", lambda: [ self.SETTINGS.setValue("DMGReadMethod", str(self.mnuConfigReadModeAGB.actions()[0].isChecked()).lower().replace("true", "2")), self.SetDMGReadMethod() ])
224
+ self.mnuConfigReadModeDMG.actions()[0].setCheckable(True)
225
+ self.mnuConfigReadModeDMG.actions()[1].setCheckable(True)
226
+ self.mnuConfigReadModeDMG.actions()[0].setChecked(self.SETTINGS.value("DMGReadMethod", default="1") == "1")
227
+ self.mnuConfigReadModeDMG.actions()[1].setChecked(self.SETTINGS.value("DMGReadMethod", default="1") == "2")
228
+ self.mnuConfig.addMenu(self.mnuConfigReadModeDMG)
229
+ self.mnuConfig.addMenu(self.mnuConfigReadModeAGB)
212
230
  self.mnuConfig.addSeparator()
213
- self.mnuConfig.addAction("Open &config folder", self.OpenPath)
231
+ self.mnuConfig.addAction("Re-&enable suppressed messages", self.ReEnableMessages)
214
232
  self.mnuConfig.actions()[0].setCheckable(True)
215
233
  self.mnuConfig.actions()[1].setCheckable(True)
216
234
  self.mnuConfig.actions()[2].setCheckable(True)
@@ -219,6 +237,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
219
237
  self.mnuConfig.actions()[5].setCheckable(True)
220
238
  self.mnuConfig.actions()[6].setCheckable(True)
221
239
  self.mnuConfig.actions()[7].setCheckable(True)
240
+ self.mnuConfig.actions()[8].setCheckable(True)
241
+ self.mnuConfig.actions()[9].setCheckable(True)
222
242
  self.mnuConfig.actions()[0].setChecked(self.SETTINGS.value("UpdateCheck") == "enabled")
223
243
  self.mnuConfig.actions()[1].setChecked(self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled") == "enabled")
224
244
  self.mnuConfig.actions()[2].setChecked(self.SETTINGS.value("PreferChipErase", default="disabled") == "enabled")
@@ -227,15 +247,37 @@ class FlashGBX_GUI(QtWidgets.QWidget):
227
247
  self.mnuConfig.actions()[5].setChecked(self.SETTINGS.value("LimitBaudRate", default="disabled") == "enabled")
228
248
  self.mnuConfig.actions()[6].setChecked(self.SETTINGS.value("GenerateDumpReports", default="disabled") == "enabled")
229
249
  self.mnuConfig.actions()[7].setChecked(self.SETTINGS.value("UseNoIntroFilenames", default="enabled") == "enabled")
230
-
231
- self.btnConfig.setMenu(self.mnuConfig)
232
-
250
+ self.mnuConfig.actions()[8].setChecked(self.SETTINGS.value("AutoPowerOff", default="350") != "0")
251
+ self.mnuConfig.actions()[9].setChecked(self.SETTINGS.value("CompareSectors", default="enabled") == "enabled")
252
+ #self.btnConfig.setMenu(self.mnuConfig)
253
+
254
+ self.mnuThirdParty = QtWidgets.QMenu("Third Party &Notices")
255
+ self.mnuThirdParty.addAction("About &Qt", lambda: [ QtWidgets.QMessageBox.aboutQt(None) ])
256
+ self.mnuThirdParty.addAction("Licenses", lambda: [ self.OpenPath(Util.APP_PATH + "/res/Third Party Notices.md") ])
257
+
258
+ btnText = "&Options"
259
+ self.btnMainMenu = QtWidgets.QPushButton(btnText)
260
+ btnWidth = self.btnMainMenu.fontMetrics().boundingRect(btnText).width() + 24
261
+ if platform.system() == "Darwin": btnWidth += 12
262
+ self.btnMainMenu.setMaximumWidth(btnWidth)
263
+ self.mnuMainMenu = QtWidgets.QMenu()
264
+ self.mnuMainMenu.addMenu(self.mnuConfig)
265
+ self.mnuMainMenu.addMenu(self.mnuTools)
266
+ self.mnuMainMenu.addSeparator()
267
+ self.mnuMainMenu.addSeparator()
268
+ self.mnuMainMenu.addAction("Open &config folder", self.OpenPath)
269
+ self.mnuMainMenu.addSeparator()
270
+ self.mnuMainMenu.addMenu(self.mnuThirdParty)
271
+ self.mnuMainMenu.addAction("About &FlashGBX", self.AboutFlashGBX)
272
+ self.btnMainMenu.setMenu(self.mnuMainMenu)
273
+
233
274
  self.btnConnect = QtWidgets.QPushButton("&Connect")
234
275
  self.connect(self.btnConnect, QtCore.SIGNAL("clicked()"), self.ConnectDevice)
235
- self.layout_devices.addWidget(self.btnTools)
236
- self.layout_devices.addWidget(self.btnConfig)
276
+ self.layout_devices.addWidget(self.btnMainMenu)
277
+ #self.layout_devices.addWidget(self.btnTools)
278
+ #self.layout_devices.addWidget(self.btnConfig)
237
279
  self.layout_devices.addWidget(self.btnConnect)
238
-
280
+
239
281
  self.layout.addLayout(self.layout_devices, 1, 0, 1, 0)
240
282
 
241
283
  # Disable widgets
@@ -269,12 +311,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
269
311
  elif config_ret[i][0] == 3:
270
312
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), config_ret[i][1], QtWidgets.QMessageBox.Ok)
271
313
 
272
- QtCore.QTimer.singleShot(1, lambda: [ self.UpdateCheck(), self.FindDevices(port=args["argparsed"].device_port) ])
314
+ QtCore.QTimer.singleShot(1, lambda: [ self.UpdateCheck(), self.FindDevices(port=args["argparsed"].device_port, firstRun=True) ])
273
315
 
274
316
 
275
317
  def GuiCreateGroupBoxDMGCartInfo(self):
276
318
  self.grpDMGCartridgeInfo = QtWidgets.QGroupBox("Game Boy Cartridge Information")
277
- self.grpDMGCartridgeInfo.setMinimumWidth(375)
319
+ self.grpDMGCartridgeInfo.setMinimumWidth(400)
278
320
  group_layout = QtWidgets.QVBoxLayout()
279
321
  group_layout.setContentsMargins(-1, 5, -1, -1)
280
322
 
@@ -284,6 +326,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
284
326
  rowDMGGameName.addWidget(self.lblDMGGameName)
285
327
  self.lblDMGGameNameResult = QtWidgets.QLabel("")
286
328
  rowDMGGameName.addWidget(self.lblDMGGameNameResult)
329
+ rowDMGGameName.setStretch(0, 9)
330
+ rowDMGGameName.setStretch(1, 11)
287
331
  group_layout.addLayout(rowDMGGameName)
288
332
 
289
333
  rowDMGRomTitle = QtWidgets.QHBoxLayout()
@@ -292,6 +336,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
292
336
  rowDMGRomTitle.addWidget(self.lblDMGRomTitle)
293
337
  self.lblDMGRomTitleResult = QtWidgets.QLabel("")
294
338
  rowDMGRomTitle.addWidget(self.lblDMGRomTitleResult)
339
+ rowDMGRomTitle.setStretch(0, 9)
340
+ rowDMGRomTitle.setStretch(1, 11)
295
341
  group_layout.addLayout(rowDMGRomTitle)
296
342
 
297
343
  rowDMGGameCodeRevision = QtWidgets.QHBoxLayout()
@@ -300,6 +346,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
300
346
  rowDMGGameCodeRevision.addWidget(self.lblDMGGameCodeRevision)
301
347
  self.lblDMGGameCodeRevisionResult = QtWidgets.QLabel("")
302
348
  rowDMGGameCodeRevision.addWidget(self.lblDMGGameCodeRevisionResult)
349
+ rowDMGGameCodeRevision.setStretch(0, 9)
350
+ rowDMGGameCodeRevision.setStretch(1, 11)
303
351
  group_layout.addLayout(rowDMGGameCodeRevision)
304
352
 
305
353
  rowDMGHeaderRtc = QtWidgets.QHBoxLayout()
@@ -307,9 +355,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
307
355
  lblDMGHeaderRtc.setContentsMargins(0, 1, 0, 1)
308
356
  rowDMGHeaderRtc.addWidget(lblDMGHeaderRtc)
309
357
  self.lblDMGHeaderRtcResult = QtWidgets.QLabel("")
310
- self.lblDMGHeaderRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
311
- self.lblDMGHeaderRtcResult.setToolTip("This shows the internal register values; in-game clock may use an offset")
358
+ #self.lblDMGHeaderRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
359
+ #self.lblDMGHeaderRtcResult.setToolTip("This shows the internal register values; in-game clock may use an offset")
360
+ #self.lblDMGHeaderRtcResult.setStyleSheet("QLabel {{ color: {:s}; text-decoration: underline; cursor: hand; }}".format(QApplication.palette().color(QtGui.QPalette.Link).name()))
361
+ #self.lblDMGHeaderRtcResult.setCursor(QtCore.Qt.PointingHandCursor)
362
+ #self.connect(self.lblDMGHeaderRtcResult, QtCore.SIGNAL("clicked()"), self.EditRTC)
363
+ #self.lblDMGHeaderRtcResult.clicked.connect(self.EditRTC)
364
+ self.lblDMGHeaderRtcResult.mousePressEvent = lambda event: [ self.EditRTC(event) ]
312
365
  rowDMGHeaderRtc.addWidget(self.lblDMGHeaderRtcResult)
366
+ rowDMGHeaderRtc.setStretch(0, 9)
367
+ rowDMGHeaderRtc.setStretch(1, 11)
313
368
  group_layout.addLayout(rowDMGHeaderRtc)
314
369
 
315
370
  rowDMGHeaderBootlogo = QtWidgets.QHBoxLayout()
@@ -318,6 +373,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
318
373
  rowDMGHeaderBootlogo.addWidget(lblDMGHeaderBootlogo)
319
374
  self.lblDMGHeaderBootlogoResult = QtWidgets.QLabel("")
320
375
  rowDMGHeaderBootlogo.addWidget(self.lblDMGHeaderBootlogoResult)
376
+ rowDMGHeaderBootlogo.setStretch(0, 9)
377
+ rowDMGHeaderBootlogo.setStretch(1, 11)
321
378
  group_layout.addLayout(rowDMGHeaderBootlogo)
322
379
 
323
380
  rowDMGHeaderROMChecksum = QtWidgets.QHBoxLayout()
@@ -326,6 +383,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
326
383
  rowDMGHeaderROMChecksum.addWidget(lblDMGHeaderROMChecksum)
327
384
  self.lblDMGHeaderROMChecksumResult = QtWidgets.QLabel("")
328
385
  rowDMGHeaderROMChecksum.addWidget(self.lblDMGHeaderROMChecksumResult)
386
+ rowDMGHeaderROMChecksum.setStretch(0, 9)
387
+ rowDMGHeaderROMChecksum.setStretch(1, 11)
329
388
  group_layout.addLayout(rowDMGHeaderROMChecksum)
330
389
 
331
390
  rowDMGHeaderROMSize = QtWidgets.QHBoxLayout()
@@ -335,6 +394,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
335
394
  self.cmbDMGHeaderROMSizeResult.setStyleSheet("combobox-popup: 0;")
336
395
  self.cmbDMGHeaderROMSizeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
337
396
  rowDMGHeaderROMSize.addWidget(self.cmbDMGHeaderROMSizeResult)
397
+ rowDMGHeaderROMSize.setStretch(0, 9)
398
+ rowDMGHeaderROMSize.setStretch(1, 11)
338
399
  group_layout.addLayout(rowDMGHeaderROMSize)
339
400
 
340
401
  rowDMGHeaderSaveType = QtWidgets.QHBoxLayout()
@@ -344,6 +405,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
344
405
  self.cmbDMGHeaderSaveTypeResult.setStyleSheet("combobox-popup: 0;")
345
406
  self.cmbDMGHeaderSaveTypeResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
346
407
  rowDMGHeaderSaveType.addWidget(self.cmbDMGHeaderSaveTypeResult)
408
+ rowDMGHeaderSaveType.setStretch(0, 9)
409
+ rowDMGHeaderSaveType.setStretch(1, 11)
347
410
  group_layout.addLayout(rowDMGHeaderSaveType)
348
411
 
349
412
  rowDMGHeaderMapper = QtWidgets.QHBoxLayout()
@@ -353,10 +416,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
353
416
  self.cmbDMGHeaderMapperResult.setStyleSheet("combobox-popup: 0;")
354
417
  self.cmbDMGHeaderMapperResult.view().setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
355
418
  rowDMGHeaderMapper.addWidget(self.cmbDMGHeaderMapperResult)
419
+ rowDMGHeaderMapper.setStretch(0, 9)
420
+ rowDMGHeaderMapper.setStretch(1, 11)
356
421
  group_layout.addLayout(rowDMGHeaderMapper)
357
422
 
358
423
  rowDMGCartridgeType = QtWidgets.QHBoxLayout()
359
- lblDMGCartridgeType = QtWidgets.QLabel("Cart:")
424
+ lblDMGCartridgeType = QtWidgets.QLabel("Profile:")
360
425
  rowDMGCartridgeType.addWidget(lblDMGCartridgeType)
361
426
  self.cmbDMGCartridgeTypeResult = QtWidgets.QComboBox()
362
427
  self.cmbDMGCartridgeTypeResult.setStyleSheet("max-width: 260px;")
@@ -371,7 +436,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
371
436
 
372
437
  def GuiCreateGroupBoxAGBCartInfo(self):
373
438
  self.grpAGBCartridgeInfo = QtWidgets.QGroupBox("Game Boy Advance Cartridge Information")
374
- self.grpAGBCartridgeInfo.setMinimumWidth(375)
439
+ self.grpAGBCartridgeInfo.setMinimumWidth(400)
375
440
  group_layout = QtWidgets.QVBoxLayout()
376
441
  group_layout.setContentsMargins(-1, 5, -1, -1)
377
442
 
@@ -381,6 +446,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
381
446
  rowAGBGameName.addWidget(lblAGBGameName)
382
447
  self.lblAGBGameNameResult = QtWidgets.QLabel("")
383
448
  rowAGBGameName.addWidget(self.lblAGBGameNameResult)
449
+ rowAGBGameName.setStretch(0, 9)
450
+ rowAGBGameName.setStretch(1, 11)
384
451
  group_layout.addLayout(rowAGBGameName)
385
452
 
386
453
  rowAGBRomTitle = QtWidgets.QHBoxLayout()
@@ -389,6 +456,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
389
456
  rowAGBRomTitle.addWidget(lblAGBRomTitle)
390
457
  self.lblAGBRomTitleResult = QtWidgets.QLabel("")
391
458
  rowAGBRomTitle.addWidget(self.lblAGBRomTitleResult)
459
+ rowAGBRomTitle.setStretch(0, 9)
460
+ rowAGBRomTitle.setStretch(1, 11)
392
461
  group_layout.addLayout(rowAGBRomTitle)
393
462
 
394
463
  rowAGBHeaderGameCodeRevision = QtWidgets.QHBoxLayout()
@@ -397,6 +466,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
397
466
  rowAGBHeaderGameCodeRevision.addWidget(lblAGBHeaderGameCodeRevision)
398
467
  self.lblAGBHeaderGameCodeRevisionResult = QtWidgets.QLabel("")
399
468
  rowAGBHeaderGameCodeRevision.addWidget(self.lblAGBHeaderGameCodeRevisionResult)
469
+ rowAGBHeaderGameCodeRevision.setStretch(0, 9)
470
+ rowAGBHeaderGameCodeRevision.setStretch(1, 11)
400
471
  group_layout.addLayout(rowAGBHeaderGameCodeRevision)
401
472
 
402
473
  rowAGBGpioRtc = QtWidgets.QHBoxLayout()
@@ -404,17 +475,22 @@ class FlashGBX_GUI(QtWidgets.QWidget):
404
475
  lblAGBGpioRtc.setContentsMargins(0, 1, 0, 1)
405
476
  rowAGBGpioRtc.addWidget(lblAGBGpioRtc)
406
477
  self.lblAGBGpioRtcResult = QtWidgets.QLabel("")
407
- self.lblAGBGpioRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
408
- self.lblAGBGpioRtcResult.setToolTip(self.lblDMGHeaderRtcResult.toolTip())
478
+ #self.lblAGBGpioRtcResult.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
479
+ #self.lblAGBGpioRtcResult.setToolTip(self.lblDMGHeaderRtcResult.toolTip())
480
+ self.lblAGBGpioRtcResult.mousePressEvent = lambda event: [ self.EditRTC(event) ]
409
481
  rowAGBGpioRtc.addWidget(self.lblAGBGpioRtcResult)
482
+ rowAGBGpioRtc.setStretch(0, 9)
483
+ rowAGBGpioRtc.setStretch(1, 11)
410
484
  group_layout.addLayout(rowAGBGpioRtc)
411
485
 
412
486
  rowAGBHeaderBootlogo = QtWidgets.QHBoxLayout()
413
487
  lblAGBHeaderBootlogo = QtWidgets.QLabel("Boot Logo:")
414
488
  lblAGBHeaderBootlogo.setContentsMargins(0, 1, 0, 1)
415
489
  rowAGBHeaderBootlogo.addWidget(lblAGBHeaderBootlogo)
416
- self.lblAGBHeaderLogoValidResult = QtWidgets.QLabel("")
417
- rowAGBHeaderBootlogo.addWidget(self.lblAGBHeaderLogoValidResult)
490
+ self.lblAGBHeaderBootlogoResult = QtWidgets.QLabel("")
491
+ rowAGBHeaderBootlogo.addWidget(self.lblAGBHeaderBootlogoResult)
492
+ rowAGBHeaderBootlogo.setStretch(0, 9)
493
+ rowAGBHeaderBootlogo.setStretch(1, 11)
418
494
  group_layout.addLayout(rowAGBHeaderBootlogo)
419
495
 
420
496
  rowAGBHeaderChecksum = QtWidgets.QHBoxLayout()
@@ -423,6 +499,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
423
499
  rowAGBHeaderChecksum.addWidget(lblAGBHeaderChecksum)
424
500
  self.lblAGBHeaderChecksumResult = QtWidgets.QLabel("")
425
501
  rowAGBHeaderChecksum.addWidget(self.lblAGBHeaderChecksumResult)
502
+ rowAGBHeaderChecksum.setStretch(0, 9)
503
+ rowAGBHeaderChecksum.setStretch(1, 11)
426
504
  group_layout.addLayout(rowAGBHeaderChecksum)
427
505
 
428
506
  rowAGBHeaderROMChecksum = QtWidgets.QHBoxLayout()
@@ -431,6 +509,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
431
509
  rowAGBHeaderROMChecksum.addWidget(lblAGBHeaderROMChecksum)
432
510
  self.lblAGBHeaderROMChecksumResult = QtWidgets.QLabel("")
433
511
  rowAGBHeaderROMChecksum.addWidget(self.lblAGBHeaderROMChecksumResult)
512
+ rowAGBHeaderROMChecksum.setStretch(0, 9)
513
+ rowAGBHeaderROMChecksum.setStretch(1, 11)
434
514
  group_layout.addLayout(rowAGBHeaderROMChecksum)
435
515
 
436
516
  rowAGBHeaderROMSize = QtWidgets.QHBoxLayout()
@@ -442,6 +522,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
442
522
  self.cmbAGBHeaderROMSizeResult.addItems(Util.AGB_Header_ROM_Sizes)
443
523
  self.cmbAGBHeaderROMSizeResult.setCurrentIndex(self.cmbAGBHeaderROMSizeResult.count() - 1)
444
524
  rowAGBHeaderROMSize.addWidget(self.cmbAGBHeaderROMSizeResult)
525
+ rowAGBHeaderROMSize.setStretch(0, 9)
526
+ rowAGBHeaderROMSize.setStretch(1, 11)
445
527
  group_layout.addLayout(rowAGBHeaderROMSize)
446
528
 
447
529
  rowAGBHeaderSaveType = QtWidgets.QHBoxLayout()
@@ -453,10 +535,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
453
535
  self.cmbAGBSaveTypeResult.addItems(Util.AGB_Header_Save_Types)
454
536
  self.cmbAGBSaveTypeResult.setCurrentIndex(self.cmbAGBSaveTypeResult.count() - 1)
455
537
  rowAGBHeaderSaveType.addWidget(self.cmbAGBSaveTypeResult)
538
+ rowAGBHeaderSaveType.setStretch(0, 9)
539
+ rowAGBHeaderSaveType.setStretch(1, 11)
456
540
  group_layout.addLayout(rowAGBHeaderSaveType)
457
541
 
458
542
  rowAGBCartridgeType = QtWidgets.QHBoxLayout()
459
- lblAGBCartridgeType = QtWidgets.QLabel("Cart:")
543
+ lblAGBCartridgeType = QtWidgets.QLabel("Profile:")
460
544
  rowAGBCartridgeType.addWidget(lblAGBCartridgeType)
461
545
  self.cmbAGBCartridgeTypeResult = QtWidgets.QComboBox()
462
546
  self.cmbAGBCartridgeTypeResult.setStyleSheet("max-width: 260px;")
@@ -468,6 +552,42 @@ class FlashGBX_GUI(QtWidgets.QWidget):
468
552
 
469
553
  self.grpAGBCartridgeInfo.setLayout(group_layout)
470
554
  return self.grpAGBCartridgeInfo
555
+
556
+ def SetAutoPowerOff(self):
557
+ if not self.CheckDeviceAlive(): return
558
+ try:
559
+ value = int(self.SETTINGS.value("AutoPowerOff", default="0"))
560
+ except ValueError:
561
+ value = 0
562
+ self.CONN.SetAutoPowerOff(value=value)
563
+
564
+ def SetDMGReadMethod(self):
565
+ if not self.CheckDeviceAlive(): return
566
+ try:
567
+ method = int(self.SETTINGS.value("DMGReadMethod", "1"))
568
+ except ValueError:
569
+ method = 1
570
+ self.CONN.SetDMGReadMethod(method)
571
+ self.mnuConfigReadModeDMG.actions()[0].setChecked(False)
572
+ self.mnuConfigReadModeDMG.actions()[1].setChecked(False)
573
+ if method == 1:
574
+ self.mnuConfigReadModeDMG.actions()[0].setChecked(True)
575
+ elif method == 2:
576
+ self.mnuConfigReadModeDMG.actions()[1].setChecked(True)
577
+
578
+ def SetAGBReadMethod(self):
579
+ if not self.CheckDeviceAlive(): return
580
+ try:
581
+ method = int(self.SETTINGS.value("AGBReadMethod", "2"))
582
+ except ValueError:
583
+ method = 2
584
+ self.CONN.SetAGBReadMethod(method)
585
+ self.mnuConfigReadModeAGB.actions()[0].setChecked(False)
586
+ self.mnuConfigReadModeAGB.actions()[1].setChecked(False)
587
+ if method == 2:
588
+ self.mnuConfigReadModeAGB.actions()[0].setChecked(True)
589
+ elif method == 0:
590
+ self.mnuConfigReadModeAGB.actions()[1].setChecked(True)
471
591
 
472
592
  def SetLimitBaudRate(self):
473
593
  if not self.CheckDeviceAlive(): return
@@ -476,7 +596,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
476
596
  if limit_baudrate == "enabled":
477
597
  self.CONN.ChangeBaudRate(baudrate=1000000)
478
598
  else:
479
- self.CONN.ChangeBaudRate(baudrate=1700000)
599
+ self.CONN.ChangeBaudRate(baudrate=2000000)
480
600
  self.DisconnectDevice()
481
601
  self.FindDevices(connectToFirst=True, mode=mode)
482
602
 
@@ -558,8 +678,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
558
678
  print("Error: Failed to check for updates (HTTP status {:d}).".format(ret.status_code))
559
679
  else:
560
680
  update_check = self.SETTINGS.value("UpdateCheck")
561
- if update_check is None or (time.time() > (Util.VERSION_TIMESTAMP + (3*30*24*60*60))):
562
- 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 config 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)
681
+ if update_check is None or (time.time() > (Util.VERSION_TIMESTAMP + (6*30*24*60*60))):
682
+ 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)
563
683
 
564
684
  def DisconnectDevice(self):
565
685
  try:
@@ -567,6 +687,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
567
687
  self.CONN.Close(cartPowerOff=True)
568
688
  self.CONN = None
569
689
  self.DEVICES = {}
690
+ self.cmbDevice.clear()
570
691
  print("Disconnected from {:s}".format(devname))
571
692
  except:
572
693
  pass
@@ -593,6 +714,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
593
714
  self.lblStatus4aResult.setText("")
594
715
  self.lblStatus4a.setText("Disconnected.")
595
716
  self.grpStatus.setTitle("Transfer Status")
717
+ self.mnuConfig.actions()[5].setVisible(True)
718
+ self.mnuConfig.actions()[8].setVisible(True)
719
+ self.mnuConfig.actions()[9].setVisible(True)
720
+ self.mnuTools.actions()[2].setEnabled(True)
721
+ self.mnuConfigReadModeAGB.setEnabled(True)
596
722
 
597
723
  def ReEnableMessages(self):
598
724
  self.SETTINGS.setValue("AutoReconnect", "disabled")
@@ -601,6 +727,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
601
727
  self.SETTINGS.setValue("SkipFinishMessage", "disabled")
602
728
  self.SETTINGS.setValue("SkipCameraSavePopup", "disabled")
603
729
 
730
+ def AboutFlashGBX(self):
731
+ 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>"
732
+ msg += f"© 2020–{datetime.datetime.now().year} Lesserkuma<br>"
733
+ msg += "• Website: <a href=\"https://github.com/lesserkuma/FlashGBX\">https://github.com/lesserkuma/FlashGBX</a><br><br>"
734
+ 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, Jayro, Jenetrix, JFox, joyrider3774, 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, 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"
735
+ QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
736
+
604
737
  def OpenPath(self, path=None):
605
738
  if path is None:
606
739
  path = Util.CONFIG_PATH
@@ -616,24 +749,29 @@ class FlashGBX_GUI(QtWidgets.QWidget):
616
749
  else:
617
750
  subprocess.Popen(["xdg-open", path])
618
751
  except:
619
- QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "The path is:\n{:s}".format(path), QtWidgets.QMessageBox.Ok)
752
+ QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The file was not found.\n\n{:s}".format(path), QtWidgets.QMessageBox.Ok)
753
+
754
+ def WriteDebugLog(self, event=None, open_log=False):
755
+ if isinstance(event, QtGui.QMouseEvent):
756
+ if event.button() in (QtCore.Qt.MouseButton.MiddleButton, QtCore.Qt.MouseButton.RightButton): return
620
757
 
621
- def WriteDebugLog(self, event=None):
622
758
  try:
623
759
  Util.dprint("{:s} version: {:s} ({:d})".format(Util.APPNAME, Util.VERSION_PEP440, Util.VERSION_TIMESTAMP))
624
760
  Util.dprint("Platform: {:s}".format(platform.platform()))
625
761
  if self.CONN is not None:
626
762
  Util.dprint("Connected device: {:s}".format(self.CONN.GetFullNameExtended(more=True)))
627
763
  else:
628
- Util.dprint("No device connected.")
629
- Util.dprint("Now writing debug log file.")
764
+ Util.dprint("No device connected")
765
+ Util.dprint("Now writing debug log file")
630
766
  except:
631
767
  pass
632
768
  try:
633
769
  fn = Util.CONFIG_PATH + "/debug.log"
634
770
  with open(fn, "wb") as f:
635
771
  f.write("\n".join(Util.DEBUG_LOG).encode("UTF-8-SIG"))
636
- print("debug.log written.")
772
+ print("debug.log written")
773
+ if open_log is True:
774
+ self.OpenPath(fn)
637
775
  return True
638
776
  except:
639
777
  return False
@@ -643,13 +781,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
643
781
  self.DisconnectDevice()
644
782
  return True
645
783
  else:
784
+ self.CONN = None
646
785
  if self.cmbDevice.count() > 0:
647
786
  index = self.cmbDevice.currentText()
648
787
  else:
649
788
  index = self.lblDevice.text()
650
789
 
651
790
  if index not in self.DEVICES:
652
- self.FindDevices(True)
791
+ self.FindDevices()
653
792
  return
654
793
 
655
794
  dev = self.DEVICES[index]
@@ -657,7 +796,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
657
796
  if str(self.SETTINGS.value("LimitBaudRate", default="disabled")).lower() == "enabled":
658
797
  max_baud = 1000000
659
798
  else:
660
- max_baud = 1700000
799
+ max_baud = 2000000
661
800
  ret = dev.Initialize(self.FLASHCARTS, port=port, max_baud=max_baud)
662
801
  msg = ""
663
802
 
@@ -669,6 +808,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
669
808
  for i in range(0, len(ret)):
670
809
  status = ret[i][0]
671
810
  text = ret[i][1]
811
+ if text in msg: continue
672
812
  if status == 0:
673
813
  msg += text + "\n"
674
814
  elif status == 1:
@@ -687,9 +827,17 @@ class FlashGBX_GUI(QtWidgets.QWidget):
687
827
  return False
688
828
 
689
829
  if dev.IsConnected():
690
- dev.SetWriteDelay(enable=str(self.SETTINGS.value("WriteDelay", default="disabled")).lower() == "enabled")
691
- qt_app.processEvents()
692
830
  self.CONN = dev
831
+ dev.SetWriteDelay(enable=str(self.SETTINGS.value("WriteDelay", default="disabled")).lower() == "enabled")
832
+ self.SetAutoPowerOff()
833
+ self.SetDMGReadMethod()
834
+ self.SetAGBReadMethod()
835
+ self.mnuConfig.actions()[5].setVisible(self.CONN.DEVICE_NAME == "GBxCart RW") # Limit Baud Rate
836
+ self.mnuConfig.actions()[8].setVisible(self.CONN.CanPowerCycleCart() and self.CONN.CanPowerCycleCart() and self.CONN.FW["fw_ver"] >= 12) # Auto Power Off
837
+ self.mnuConfig.actions()[9].setVisible(self.CONN.FW["fw_ver"] >= 12) # Skip writing matching ROM chunks
838
+ self.mnuConfigReadModeAGB.setEnabled(self.CONN.FW["fw_ver"] >= 12)
839
+ self.mnuConfigReadModeDMG.setEnabled(self.CONN.FW["fw_ver"] >= 12)
840
+
693
841
  self.CONN.SetTimeout(float(self.SETTINGS.value("SerialTimeout", default="1")))
694
842
  self.optDMG.setAutoExclusive(False)
695
843
  self.optAGB.setAutoExclusive(False)
@@ -705,13 +853,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
705
853
  self.btnConnect.setText("&Disconnect")
706
854
  self.cmbDevice.setStyleSheet("QComboBox { border: 0; margin: 0; padding: 0; max-width: 0px; }")
707
855
  if dev.GetFWBuildDate() == "":
708
- self.lblDevice.setText(dev.GetFullNameExtended() + " [Legacy Mode]")
856
+ self.lblDevice.setText(dev.GetFullNameLabel() + " [Legacy Mode]")
709
857
  else:
710
- self.lblDevice.setText(dev.GetFullNameExtended())
858
+ self.lblDevice.setText(dev.GetFullNameLabel())
711
859
  print("\nConnected to {:s}".format(dev.GetFullNameExtended(more=True)))
712
860
  self.grpActions.setEnabled(True)
713
- self.btnTools.setEnabled(True)
714
- self.btnConfig.setEnabled(True)
861
+ self.mnuTools.setEnabled(True)
862
+ self.mnuConfig.setEnabled(True)
715
863
  self.btnCancel.setEnabled(False)
716
864
 
717
865
  # Firmware Update Menu
@@ -739,9 +887,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
739
887
  if dev.FirmwareUpdateAvailable():
740
888
  dontShowAgain = str(self.SETTINGS.value("SkipFirmwareUpdate", default="disabled")).lower() == "enabled"
741
889
  if not dontShowAgain or dev.FW_UPDATE_REQ:
742
- if dev.FW_UPDATE_REQ:
890
+ if dev.FW_UPDATE_REQ is True:
743
891
  text = "A firmware update for your {:s} device is required to use this software. Do you want to update now?".format(dev.GetFullName())
744
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)
893
+ elif dev.FW_UPDATE_REQ == 2:
894
+ 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())
895
+ 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)
745
896
  else:
746
897
  text = "A firmware update for your {:s} device is available. Do you want to update now?".format(dev.GetFullName())
747
898
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
@@ -757,8 +908,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
757
908
  if dontShowAgain: self.SETTINGS.setValue("SkipFirmwareUpdate", "enabled")
758
909
  if answer == QtWidgets.QMessageBox.Yes:
759
910
  self.ShowFirmwareUpdateWindow()
911
+
760
912
  elif dev.FW_UPDATE_REQ:
761
- text = "A firmware update for your {:s} device is required to use this software. Please visit the official website (<a href=\"{:s}\">{:s}</a>) for updates.<br><br>Current firmware version: {:s}".format(dev.GetFullName(), dev.GetOfficialWebsite(), dev.GetOfficialWebsite(), dev.GetFirmwareVersion())
913
+ text = "A firmware update for your {:s} device is required to use this software.<br><br>Current firmware version: {:s}".format(dev.GetFullName(), dev.GetFirmwareVersion())
762
914
  if not Util.DEBUG:
763
915
  self.DisconnectDevice()
764
916
  QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), text, QtWidgets.QMessageBox.Ok)
@@ -766,7 +918,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
766
918
  return True
767
919
  return False
768
920
 
769
- def FindDevices(self, connectToFirst=False, port=None, mode=None):
921
+ def FindDevices(self, connectToFirst=False, port=None, mode=None, firstRun=False):
770
922
  if self.CONN is not None:
771
923
  self.DisconnectDevice()
772
924
  self.lblDevice.setText("Searching...")
@@ -779,29 +931,39 @@ class FlashGBX_GUI(QtWidgets.QWidget):
779
931
  # pylint: disable=global-variable-not-assigned
780
932
  global hw_devices
781
933
  for hw_device in hw_devices:
782
- dev = hw_device.GbxDevice()
783
- if str(self.SETTINGS.value("LimitBaudRate", default="disabled")).lower() == "enabled":
784
- max_baud = 1000000
785
- else:
786
- max_baud = 1700000
787
- ret = dev.Initialize(self.FLASHCARTS, port=port, max_baud=max_baud)
788
- if ret is False:
789
- self.CONN = None
790
- elif isinstance(ret, list):
791
- for i in range(0, len(ret)):
792
- status = ret[i][0]
793
- msg = ret[i][1]
794
- if msg == last_msg: # don’t show the same message twice
795
- continue
796
- else:
797
- last_msg = msg
798
- if status == 3:
799
- messages.append(msg)
800
- self.CONN = None
801
-
802
- if dev.IsConnected():
803
- self.DEVICES[dev.GetFullNameExtended()] = dev
804
- dev.Close()
934
+ ports = []
935
+ while True: # for finding other devices of the same type
936
+ dev = hw_device.GbxDevice()
937
+ if str(self.SETTINGS.value("LimitBaudRate", default="disabled")).lower() == "enabled":
938
+ max_baud = 1000000
939
+ else:
940
+ max_baud = 2000000
941
+ ret = dev.Initialize(self.FLASHCARTS, port=port, max_baud=max_baud)
942
+ if ret is False or dev.CheckActive() is False:
943
+ self.CONN = None
944
+ break
945
+ elif isinstance(ret, list):
946
+ for i in range(0, len(ret)):
947
+ status = ret[i][0]
948
+ msg = ret[i][1]
949
+ if msg in messages: # don’t show the same message twice
950
+ continue
951
+ else:
952
+ last_msg = msg
953
+ if status == 3:
954
+ messages.append(msg)
955
+ self.CONN = None
956
+
957
+ if dev.GetPort() in ports:
958
+ break
959
+ ports.append(dev.GetPort())
960
+
961
+ if dev.IsConnected():
962
+ self.DEVICES[dev.GetFullNameExtended()] = dev
963
+ if dev.GetPort() in ports: break
964
+
965
+ for dev in self.DEVICES.values():
966
+ dev.Close()
805
967
 
806
968
  self.cmbDevice.setStyleSheet("QComboBox { border: 0; margin: 0; padding: 0; max-width: 0px; }")
807
969
 
@@ -811,7 +973,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
811
973
  for message in messages:
812
974
  msg += message + "\n\n"
813
975
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), msg[:-2], QtWidgets.QMessageBox.Ok)
814
- else:
976
+ elif not firstRun:
815
977
  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>".replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok).exec()
816
978
 
817
979
  self.lblDevice.setText("No devices found.")
@@ -847,9 +1009,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
847
1009
  return True
848
1010
 
849
1011
  def AbortOperation(self):
850
- self.CONN.CANCEL_ARGS["from_user"] = True
851
- self.CONN.CANCEL = True
852
- self.CONN.ERROR = False
1012
+ if "stresstest_running" in self.STATUS:
1013
+ del(self.STATUS["stresstest_running"])
1014
+ self.CONN.AbortOperation()
853
1015
  self.lblStatus4a.setText("Stopping... Please wait.")
854
1016
  self.lblStatus4aResult.setText("")
855
1017
 
@@ -859,8 +1021,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
859
1021
  self.grpDMGCartridgeInfo.setEnabled(True)
860
1022
  self.grpAGBCartridgeInfo.setEnabled(True)
861
1023
  self.grpActions.setEnabled(True)
862
- self.btnTools.setEnabled(True)
863
- self.btnConfig.setEnabled(True)
1024
+ self.mnuTools.setEnabled(True)
1025
+ self.mnuConfig.setEnabled(True)
864
1026
  self.btnCancel.setEnabled(False)
865
1027
 
866
1028
  dontShowAgain = str(self.SETTINGS.value("SkipFinishMessage", default="disabled")).lower() == "enabled"
@@ -931,7 +1093,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
931
1093
  if self.CONN.INFO["loop_detected"] is not False:
932
1094
  msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(size=self.CONN.INFO["loop_detected"], asInt=True))
933
1095
  else:
934
- msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
1096
+ msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps. You can also try to change the read mode in the options."
935
1097
  if self.CONN.GetMode() == "DMG" and self.cmbDMGHeaderMapperResult.currentText() == "MBC1":
936
1098
  msg += "\n\nIf this is a NP GB-Memory Cartridge, please use the “Retry as G-MMC1” button."
937
1099
  button_gmmc1 = msgbox.addButton(" Retry as G-MMC1 ", QtWidgets.QMessageBox.ActionRole)
@@ -1044,7 +1206,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1044
1206
  elif self.CONN.INFO["last_action"] == 4: # Flash ROM
1045
1207
  if "broken_sectors" in self.CONN.INFO:
1046
1208
  s = ""
1047
- for sector in self.CONN.INFO["broken_sectors"]: s += "0x{:X}~0x{:X}, ".format(sector[0], sector[0]+sector[1]-1)
1209
+ sc = 0
1210
+ for sector in self.CONN.INFO["broken_sectors"]:
1211
+ sc += 1
1212
+ if sc > 10:
1213
+ s += "and others "
1214
+ break
1215
+ s += "0x{:X}~0x{:X}, ".format(sector[0], sector[0]+sector[1]-1)
1048
1216
  msg_v = "The ROM was written completely, but verification of written data failed in the following sector(s): {:s}.".format(s[:-2])
1049
1217
  if "verify_error_params" in self.CONN.INFO:
1050
1218
  if self.CONN.GetMode() == "DMG":
@@ -1097,6 +1265,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1097
1265
  self.CONN.INFO["last_action"] = 0
1098
1266
 
1099
1267
  if dontShowAgain: self.SETTINGS.setValue("SkipFinishMessage", "enabled")
1268
+ # if self.CONN is not None and self.CONN.CanPowerCycleCart(): self.CONN.CartPowerOff()
1100
1269
  self.SetProgressBars(min=0, max=1, value=1)
1101
1270
 
1102
1271
  def DMGMapperTypeChanged(self, index):
@@ -1169,7 +1338,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1169
1338
  rom_size = Util.AGB_Header_ROM_Sizes_Map[self.cmbAGBHeaderROMSizeResult.currentIndex()]
1170
1339
  path = QtWidgets.QFileDialog.getSaveFileName(self, "Backup ROM", last_dir + "/" + path, "Game Boy Advance ROM File (*.gba *.srl);;All Files (*.*)")[0]
1171
1340
  cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1172
-
1341
+
1173
1342
  if (path == ""): return
1174
1343
 
1175
1344
  self.SETTINGS.setValue(setting_name, os.path.dirname(path))
@@ -1179,8 +1348,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1179
1348
  self.grpDMGCartridgeInfo.setEnabled(False)
1180
1349
  self.grpAGBCartridgeInfo.setEnabled(False)
1181
1350
  self.grpActions.setEnabled(False)
1182
- self.btnTools.setEnabled(False)
1183
- self.btnConfig.setEnabled(False)
1351
+ self.mnuTools.setEnabled(False)
1352
+ self.mnuConfig.setEnabled(False)
1184
1353
  self.lblStatus4a.setText("Preparing...")
1185
1354
  qt_app.processEvents()
1186
1355
  args = { "path":path, "mbc":mbc, "rom_size":rom_size, "agb_rom_size":rom_size, "fast_read_mode":True, "cart_type":cart_type, "settings":self.SETTINGS }
@@ -1365,7 +1534,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1365
1534
  elif msgbox.clickedButton() == button_2:
1366
1535
  pass
1367
1536
  else:
1368
- Util.dprint("Couldn’t find boot logo file in configuration folder.")
1537
+ Util.dprint("Couldn’t find boot logo file in configuration folder")
1369
1538
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg_text)
1370
1539
  msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
1371
1540
  msgbox.setDefaultButton(QtWidgets.QMessageBox.Cancel)
@@ -1397,8 +1566,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1397
1566
  self.grpDMGCartridgeInfo.setEnabled(False)
1398
1567
  self.grpAGBCartridgeInfo.setEnabled(False)
1399
1568
  self.grpActions.setEnabled(False)
1400
- self.btnTools.setEnabled(False)
1401
- self.btnConfig.setEnabled(False)
1569
+ self.mnuTools.setEnabled(False)
1570
+ self.mnuConfig.setEnabled(False)
1402
1571
  self.lblStatus4a.setText("Preparing...")
1403
1572
  qt_app.processEvents()
1404
1573
  if len(buffer) > 0x1000 or just_erase:
@@ -1408,6 +1577,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1408
1577
  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 }
1409
1578
  else:
1410
1579
  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 }
1580
+ args["compare_sectors"] = self.SETTINGS.value("CompareSectors", default="disabled").lower() == "enabled"
1411
1581
  self.CONN.FlashROM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
1412
1582
  #self.CONN._FlashROM(args=args)
1413
1583
  self.grpStatus.setTitle("Transfer Status")
@@ -1466,7 +1636,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1466
1636
  verify_read = False
1467
1637
 
1468
1638
  rtc = False
1469
- if self.CONN.INFO["has_rtc"]:
1639
+ if self.CONN.INFO["has_rtc"] is True:
1470
1640
  if self.CONN.GetMode() == "DMG" and mbc in (0x10, 0x110) and not self.CONN.IsClkConnected():
1471
1641
  rtc = False
1472
1642
  else:
@@ -1505,8 +1675,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1505
1675
  self.grpDMGCartridgeInfo.setEnabled(False)
1506
1676
  self.grpAGBCartridgeInfo.setEnabled(False)
1507
1677
  self.grpActions.setEnabled(False)
1508
- self.btnTools.setEnabled(False)
1509
- self.btnConfig.setEnabled(False)
1678
+ self.mnuTools.setEnabled(False)
1679
+ self.mnuConfig.setEnabled(False)
1510
1680
  self.lblStatus4a.setText("Preparing...")
1511
1681
  qt_app.processEvents()
1512
1682
 
@@ -1525,13 +1695,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1525
1695
 
1526
1696
  def WriteRAM(self, dpath="", erase=False, test=False):
1527
1697
  if not self.CheckDeviceAlive(): return
1528
-
1698
+ mode = self.CONN.GetMode()
1699
+
1529
1700
  if dpath == "":
1530
- path = Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=self.SETTINGS)
1701
+ path = Util.GenerateFileName(mode=mode, header=self.CONN.INFO, settings=self.SETTINGS)
1531
1702
  path = os.path.splitext(path)[0]
1532
1703
  path += ".sav"
1533
1704
 
1534
- if self.CONN.GetMode() == "DMG":
1705
+ if mode == "DMG":
1535
1706
  setting_name = "LastDirSaveDataDMG"
1536
1707
  last_dir = self.SETTINGS.value(setting_name)
1537
1708
  if last_dir is None: last_dir = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.DocumentsLocation)
@@ -1540,9 +1711,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1540
1711
  if save_type == 0:
1541
1712
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
1542
1713
  return
1714
+ cart_type = self.cmbDMGCartridgeTypeResult.currentIndex()
1543
1715
  #save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(save_type)]
1544
1716
 
1545
- elif self.CONN.GetMode() == "AGB":
1717
+ elif mode == "AGB":
1546
1718
  setting_name = "LastDirSaveDataAGB"
1547
1719
  last_dir = self.SETTINGS.value(setting_name)
1548
1720
  if last_dir is None: last_dir = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.DocumentsLocation)
@@ -1552,6 +1724,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1552
1724
  QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "No save type was selected.", QtWidgets.QMessageBox.Ok)
1553
1725
  return
1554
1726
  #save_size = Util.AGB_Header_Save_Sizes[save_type]
1727
+ cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
1555
1728
  else:
1556
1729
  return
1557
1730
  if not self.CheckHeader(): return
@@ -1573,18 +1746,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1573
1746
  msgbox.exec()
1574
1747
  return
1575
1748
  elif not self.CONN.CanPowerCycleCart():
1576
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="This feature only works with newer GBxCart RW hardware revisions.", standardButtons=QtWidgets.QMessageBox.Ok)
1577
- msgbox.exec()
1578
- return
1749
+ if (mode == "AGB" and "SRAM" in self.cmbAGBSaveTypeResult.currentText()) or (mode == "DMG" and "SRAM" in self.cmbDMGHeaderSaveTypeResult.currentText()):
1750
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Your device does not support automatic power cycling, so some tests may be skipped.", standardButtons=QtWidgets.QMessageBox.Ok)
1751
+ msgbox.exec()
1579
1752
 
1580
- if self.CONN.GetMode() == "AGB" and self.cmbAGBSaveTypeResult.currentIndex() < len(Util.AGB_Header_Save_Types) and \
1581
- ("Batteryless SRAM" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1582
- ("8M DACS" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]):
1753
+ 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 \
1754
+ ("8M DACS" in Util.AGB_Header_Save_Types[self.cmbAGBSaveTypeResult.currentIndex()]) or \
1755
+ (mode == "AGB" and "ereader" in self.CONN.INFO and self.CONN.INFO["ereader"] is True) or \
1756
+ (mode == "DMG" and "256M Multi Cart" in self.cmbDMGHeaderMapperResult.currentText() and not self.CONN.CanPowerCycleCart()):
1583
1757
  QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "Stress test is not supported for this save type.", QtWidgets.QMessageBox.Ok)
1584
1758
  return
1585
- if self.CONN.GetMode() == "AGB" and "ereader" in self.CONN.INFO and self.CONN.INFO["ereader"] is True:
1586
- QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "Stress test is not supported for this cartridge.", QtWidgets.QMessageBox.Ok)
1587
- return
1588
1759
  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)
1589
1760
  if answer == QtWidgets.QMessageBox.Cancel: return
1590
1761
  else:
@@ -1599,7 +1770,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1599
1770
  return
1600
1771
 
1601
1772
  buffer = None
1602
- if self.CONN.GetMode() == "AGB" and "ereader" in self.CONN.INFO and self.CONN.INFO["ereader"] is True:
1773
+ if mode == "AGB" and "ereader" in self.CONN.INFO and self.CONN.INFO["ereader"] is True:
1603
1774
  if self.CONN.GetFWBuildDate() == "": # Legacy Mode
1604
1775
  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)
1605
1776
  msgbox.exec()
@@ -1646,10 +1817,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1646
1817
 
1647
1818
  rtc = False
1648
1819
  rtc_advance = False
1649
- if not test and self.CONN.INFO["has_rtc"]:
1650
- if self.CONN.GetMode() == "DMG" and mbc in (0x10, 0x110) and not self.CONN.IsClkConnected():
1820
+ if not test and self.CONN.INFO["has_rtc"] is True:
1821
+ if mode == "DMG" and mbc in (0x10, 0x110) and not self.CONN.IsClkConnected():
1651
1822
  rtc = False
1652
- elif erase or Util.save_size_includes_rtc(mode=self.CONN.GetMode(), mbc=mbc, save_size=filesize, save_type=save_type):
1823
+ elif erase or Util.save_size_includes_rtc(mode=mode, mbc=mbc, save_size=filesize, save_type=save_type):
1653
1824
  msg = "A Real Time Clock cartridge was detected. Do you want the Real Time Clock register values to be written as well?"
1654
1825
  cb = QtWidgets.QCheckBox("&Adjust RTC", checked=True)
1655
1826
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel)
@@ -1667,14 +1838,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1667
1838
  self.grpDMGCartridgeInfo.setEnabled(False)
1668
1839
  self.grpAGBCartridgeInfo.setEnabled(False)
1669
1840
  self.grpActions.setEnabled(False)
1670
- self.btnTools.setEnabled(False)
1671
- self.btnConfig.setEnabled(False)
1841
+ self.mnuTools.setEnabled(False)
1842
+ self.mnuConfig.setEnabled(False)
1672
1843
  self.lblStatus4a.setText("Preparing...")
1673
1844
  self.grpStatus.setTitle("Transfer Status")
1674
1845
  self.lblStatus1aResult.setText("–")
1675
1846
  self.lblStatus2aResult.setText("–")
1676
1847
  self.lblStatus3aResult.setText("–")
1677
1848
  self.lblStatus4aResult.setText("")
1849
+ self.btnCancel.setEnabled(True)
1850
+ self.STATUS["stresstest_running"] = True
1678
1851
  qt_app.processEvents()
1679
1852
 
1680
1853
  test_patterns = [
@@ -1719,7 +1892,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1719
1892
  self.lblStatus4a.setText("Testing ({:s} 1/2)...".format(test_patterns_names[0]))
1720
1893
  self.SetProgressBars(min=0, max=len(test_patterns)+3, value=0)
1721
1894
  qt_app.processEvents()
1722
- args = { "mode":2, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False }
1895
+ args = { "mode":2, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "cart_type":cart_type }
1723
1896
  t = threading.Thread(target=lambda a: self.CONN.TransferData(args=a, signal=None), args=[args])
1724
1897
  t.start()
1725
1898
  while t.is_alive():
@@ -1734,6 +1907,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1734
1907
  self.lblStatus4a.setText("Waiting for power cycle ({:d})...".format(i))
1735
1908
  qt_app.processEvents()
1736
1909
  time.sleep(1)
1910
+ if "stresstest_running" not in self.STATUS: break
1737
1911
  self.CONN.CartPowerOn()
1738
1912
  else:
1739
1913
  time.sleep(1)
@@ -1746,27 +1920,32 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1746
1920
  time.sleep(0.02)
1747
1921
  t.join()
1748
1922
  save2 = self.CONN.INFO["data"]
1749
- except:
1923
+ except KeyError:
1924
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="An error occured. Please ensure you selected the correct save type.", standardButtons=QtWidgets.QMessageBox.Ok)
1925
+ msgbox.exec()
1750
1926
  save1 = None
1751
1927
 
1752
- if save1 is not None and save1 != save2:
1928
+ stop = False
1929
+ if (save1 is not None and save1 != save2) and "stresstest_running" in self.STATUS:
1930
+ with open(Util.CONFIG_PATH + "/debug_stress_test_1.bin", "wb") as f: f.write(save1)
1931
+ with open(Util.CONFIG_PATH + "/debug_stress_test_2.bin", "wb") as f: f.write(save2)
1753
1932
  msg = "Test {:d} ({:s}) failed!\nNote: SRAM requires a working battery to retain save data.\n\nContinue anyway?".format(test_ok+1, test_patterns_names[test_ok])
1754
1933
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
1755
- msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
1934
+ msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
1756
1935
  answer = msgbox.exec()
1757
1936
  if answer == QtWidgets.QMessageBox.No:
1758
- save1 = None
1759
- save2 = None
1937
+ stop = True
1760
1938
 
1761
- if save1 is not None:
1939
+ if not stop and save1 is not None:
1762
1940
  with open(backup_fn, "wb") as f: f.write(save1)
1763
1941
  test_ok += 1
1764
1942
  for i in range(0, len(test_patterns)):
1943
+ if "stresstest_running" not in self.STATUS: break
1765
1944
  self.lblStatus4a.setText("Testing ({:s})...".format(test_patterns_names[i+1]))
1766
1945
  self.SetProgressBars(min=0, max=len(test_patterns)+3, value=i+2)
1767
1946
  qt_app.processEvents()
1768
1947
  towrite = test_patterns[i]
1769
- args = { "mode":3, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "rtc_advance":rtc_advance, "erase":erase, "verify_write":False, "buffer":towrite }
1948
+ args = { "mode":3, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "rtc_advance":rtc_advance, "erase":erase, "verify_write":False, "buffer":towrite, "cart_type":cart_type }
1770
1949
  t = threading.Thread(target=lambda a: self.CONN.TransferData(args=a, signal=None), args=[args])
1771
1950
  t.start()
1772
1951
  while t.is_alive():
@@ -1778,7 +1957,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1778
1957
  self.CONN.CartPowerOff()
1779
1958
  time.sleep(0.5)
1780
1959
  self.CONN.CartPowerOn()
1781
- args = { "mode":2, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False }
1960
+ args = { "mode":2, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "cart_type":cart_type }
1782
1961
  t = threading.Thread(target=lambda a: self.CONN.TransferData(args=a, signal=None), args=[args])
1783
1962
  t.start()
1784
1963
  while t.is_alive():
@@ -1790,17 +1969,18 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1790
1969
  break
1791
1970
  test_ok += 1
1792
1971
 
1972
+ self.btnCancel.setEnabled(False)
1793
1973
  self.lblStatus4a.setText("Restoring original save data...")
1794
1974
  self.SetProgressBars(min=0, max=len(test_patterns)+3, value=len(test_patterns)+2)
1795
1975
  qt_app.processEvents()
1796
- args = { "mode":3, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "rtc_advance":rtc_advance, "erase":erase, "verify_write":False, "buffer":save1 }
1976
+ args = { "mode":3, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "rtc_advance":rtc_advance, "erase":erase, "verify_write":False, "buffer":save1, "cart_type":cart_type }
1797
1977
  t = threading.Thread(target=lambda a: self.CONN.TransferData(args=a, signal=None), args=[args])
1798
1978
  t.start()
1799
1979
  while t.is_alive():
1800
1980
  qt_app.processEvents()
1801
1981
  time.sleep(0.02)
1802
1982
  t.join()
1803
- args = { "mode":2, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False }
1983
+ args = { "mode":2, "path":path, "mbc":mbc, "save_type":save_type, "rtc":False, "cart_type":cart_type }
1804
1984
  t = threading.Thread(target=lambda a: self.CONN.TransferData(args=a, signal=None), args=[args])
1805
1985
  t.start()
1806
1986
  while t.is_alive():
@@ -1814,30 +1994,36 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1814
1994
  self.SetProgressBars(min=0, max=100, value=100)
1815
1995
  self.lblStatus4a.setText("Done.")
1816
1996
  qt_app.processEvents()
1817
- if test_ok == len(test_patterns)+1:
1818
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="All tests completed successfully!" + msg_te, standardButtons=QtWidgets.QMessageBox.Ok)
1819
- msgbox.exec()
1820
- else:
1821
- try:
1822
- if test_ok == 0:
1823
- towrite = save1
1824
- readback = save2
1825
- with open(Util.CONFIG_PATH + "/debug_stress_test_1.bin", "wb") as f: f.write(towrite[:len(readback)])
1826
- with open(Util.CONFIG_PATH + "/debug_stress_test_2.bin", "wb") as f: f.write(readback)
1827
- except:
1828
- pass
1829
- if test_ok > 0:
1830
- msg = "Test {:d} ({:s}) failed!".format(test_ok+1, test_patterns_names[test_ok])
1831
- msg += msg_te
1832
- msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Ok)
1997
+
1998
+ if "stresstest_running" in self.STATUS:
1999
+ if test_ok == len(test_patterns)+1:
2000
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="All tests completed successfully!" + msg_te, standardButtons=QtWidgets.QMessageBox.Ok)
1833
2001
  msgbox.exec()
1834
-
2002
+ else:
2003
+ try:
2004
+ if test_ok == 0:
2005
+ towrite = save1
2006
+ readback = save2
2007
+ with open(Util.CONFIG_PATH + "/debug_stress_test_1.bin", "wb") as f: f.write(towrite[:len(readback)])
2008
+ with open(Util.CONFIG_PATH + "/debug_stress_test_2.bin", "wb") as f: f.write(readback)
2009
+ except:
2010
+ pass
2011
+ if test_ok > 0:
2012
+ msg = "Test {:d} ({:s}) failed!".format(test_ok+1, test_patterns_names[test_ok])
2013
+ msg += msg_te
2014
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Ok)
2015
+ msgbox.exec()
2016
+ else:
2017
+ msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="The stress test process was cancelled.", standardButtons=QtWidgets.QMessageBox.Ok)
2018
+ msgbox.exec()
2019
+
1835
2020
  self.grpDMGCartridgeInfo.setEnabled(True)
1836
2021
  self.grpAGBCartridgeInfo.setEnabled(True)
1837
2022
  self.grpActions.setEnabled(True)
1838
- self.btnTools.setEnabled(True)
1839
- self.btnConfig.setEnabled(True)
1840
-
2023
+ self.mnuTools.setEnabled(True)
2024
+ self.mnuConfig.setEnabled(True)
2025
+ self.btnCancel.setEnabled(False)
2026
+
1841
2027
  if not self.CONN.IsConnected(): self.DisconnectDevice()
1842
2028
 
1843
2029
  else:
@@ -1873,7 +2059,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1873
2059
  self.CONN.FlashROM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
1874
2060
  #self.CONN._FlashROM(args=args)
1875
2061
  else:
1876
- args = { "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "rtc_advance":rtc_advance, "erase":erase, "verify_write":verify_write }
2062
+ #cart_type = self.cmbAGBCartridgeTypeResult.currentIndex()
2063
+ 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 }
1877
2064
  if buffer is not None:
1878
2065
  args["buffer"] = buffer
1879
2066
  args["path"] = None
@@ -1881,14 +2068,15 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1881
2068
  self.CONN.RestoreRAM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
1882
2069
  #args = { "mode":3, "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "rtc_advance":rtc_advance, "erase":erase, "verify_write":verify_write }
1883
2070
  #self.CONN._BackupRestoreRAM(args=args)
2071
+
1884
2072
  self.STATUS["time_start"] = time.time()
1885
2073
  self.STATUS["last_path"] = path
1886
2074
  self.STATUS["args"] = args
1887
2075
  self.grpDMGCartridgeInfo.setEnabled(False)
1888
2076
  self.grpAGBCartridgeInfo.setEnabled(False)
1889
2077
  self.grpActions.setEnabled(False)
1890
- self.btnTools.setEnabled(False)
1891
- self.btnConfig.setEnabled(False)
2078
+ self.mnuTools.setEnabled(False)
2079
+ self.mnuConfig.setEnabled(False)
1892
2080
  self.lblStatus4a.setText("Preparing...")
1893
2081
  self.grpStatus.setTitle("Transfer Status")
1894
2082
  self.lblStatus1aResult.setText("–")
@@ -1972,6 +2160,178 @@ class FlashGBX_GUI(QtWidgets.QWidget):
1972
2160
  del(dlg)
1973
2161
  return ret
1974
2162
 
2163
+ def EditRTC(self, _):
2164
+ data = self.CONN.INFO
2165
+ if "dump_info" not in data: return
2166
+ if "has_rtc" not in data or data["has_rtc"] is not True: return
2167
+ if "rtc_dict" not in data or len(data["rtc_dict"]) == 0: return
2168
+ rtc_data = data["rtc_dict"]
2169
+
2170
+ if self.CONN.GetMode() == "DMG":
2171
+ mbc = Util.get_mbc_name(Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex()))
2172
+ if mbc in ("MBC3", "MBC30"):
2173
+ dlg_args = {
2174
+ "title":"MBC3/MBC30 Real Time Clock Editor",
2175
+ "intro":"Enter the number of days, hours, minutes and seconds that passed since the RTC initially started.\n\nPlease note that all values are internal values. The game may use these only as a relative reference.",
2176
+ "params": [
2177
+ # ID, Type, Value(s), Default Index
2178
+ [ "rtc_d", "spb", "Days:", (0, 511), rtc_data["rtc_d"] ],
2179
+ [ "rtc_h", "spb", "Hours:", (0, 23), rtc_data["rtc_h"] ],
2180
+ [ "rtc_m", "spb", "Minutes:", (0, 59), rtc_data["rtc_m"] ],
2181
+ [ "rtc_s", "spb", "Seconds:", (0, 59), rtc_data["rtc_s"] ],
2182
+ [ "current", "chk", "Ignore above time values and use the current time instead", None, False ],
2183
+ ]
2184
+ }
2185
+ dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
2186
+ if dlg.exec_() == 1:
2187
+ result = dlg.GetResult()
2188
+ rtc_dict = {}
2189
+ for key, value in result.items():
2190
+ if isinstance(value, QtWidgets.QSpinBox):
2191
+ rtc_dict[key] = value.value()
2192
+ elif isinstance(value, QtWidgets.QCheckBox):
2193
+ rtc_dict[key] = value.isChecked()
2194
+ if result["current"].isChecked():
2195
+ dt = datetime.datetime.now() + datetime.timedelta(seconds=1)
2196
+ rtc_dict.update({
2197
+ "rtc_h":dt.hour,
2198
+ "rtc_m":dt.minute,
2199
+ "rtc_s":dt.second,
2200
+ })
2201
+ mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
2202
+ args = { "mbc":mbc, "rtc_dict":rtc_dict }
2203
+ else:
2204
+ return False
2205
+
2206
+ elif mbc in ("HuC-3"):
2207
+ dlg_args = {
2208
+ "title":"HuC-3 Real Time Clock Editor",
2209
+ "intro":"Enter the number of days since your last play, and the current time.\n\nPlease note that the day value is an internal value. The game may use it only as a relative reference.",
2210
+ "params": [
2211
+ # ID, Type, Value(s), Default Index
2212
+ [ "rtc_d", "spb", "Days:", (0, 4095), rtc_data["rtc_d"] ],
2213
+ [ "rtc_h", "spb", "Hours:", (0, 23), rtc_data["rtc_h"] ],
2214
+ [ "rtc_m", "spb", "Minutes:", (0, 59), rtc_data["rtc_m"] ],
2215
+ [ "current", "chk", "Ignore above time values and use the current time instead", None, False ],
2216
+ ]
2217
+ }
2218
+ dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
2219
+ if dlg.exec_() == 1:
2220
+ result = dlg.GetResult()
2221
+ rtc_dict = {}
2222
+ for key, value in result.items():
2223
+ if isinstance(value, QtWidgets.QSpinBox):
2224
+ rtc_dict[key] = value.value()
2225
+ elif isinstance(value, QtWidgets.QCheckBox):
2226
+ rtc_dict[key] = value.isChecked()
2227
+ if result["current"].isChecked():
2228
+ dt = datetime.datetime.now()
2229
+ rtc_dict.update({
2230
+ "rtc_h":dt.hour,
2231
+ "rtc_m":dt.minute
2232
+ })
2233
+ mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
2234
+ args = { "mbc":mbc, "rtc_dict":rtc_dict }
2235
+ else:
2236
+ return False
2237
+
2238
+ elif mbc in ("TAMA5"):
2239
+ dlg_args = {
2240
+ "title":"TAMA5 Real Time Clock Editor",
2241
+ "intro":"Enter the date and time used in the game.\n\nPlease note that the day value is an internal value. The game may use it only as a relative reference.",
2242
+ "params": [
2243
+ # ID, Type, Value(s), Default Index
2244
+ [ "rtc_y", "spb", "Years passed:", (0, 80), rtc_data["rtc_y"] - 19 ], # 19–99
2245
+ [ "rtc_leap_year_state", "spb", "Years since last leap year:", (0, 3), rtc_data["rtc_leap_year_state"] ],
2246
+ [ "rtc_m", "spb", "Month:", (1, 12), rtc_data["rtc_m"] ],
2247
+ [ "rtc_d", "spb", "Day:", (1, 31), rtc_data["rtc_d"] ],
2248
+ [ "rtc_h", "spb", "Hours:", (0, 23), rtc_data["rtc_h"] ],
2249
+ [ "rtc_i", "spb", "Minutes:", (0, 59), rtc_data["rtc_i"] ],
2250
+ [ "rtc_s", "spb", "Seconds:", (0, 59), rtc_data["rtc_s"] ],
2251
+ [ "current", "chk", "Ignore above values and use the current date and time instead", None, False ],
2252
+ ]
2253
+ }
2254
+ dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
2255
+ if dlg.exec_() == 1:
2256
+ result = dlg.GetResult()
2257
+ rtc_dict = {}
2258
+ for key, value in result.items():
2259
+ if isinstance(value, QtWidgets.QSpinBox):
2260
+ rtc_dict[key] = value.value()
2261
+ elif isinstance(value, QtWidgets.QCheckBox):
2262
+ rtc_dict[key] = value.isChecked()
2263
+ if result["current"].isChecked():
2264
+ dt = datetime.datetime.now() + datetime.timedelta(seconds=2)
2265
+ rtc_dict.update({
2266
+ "rtc_m":dt.month,
2267
+ "rtc_d":dt.day,
2268
+ "rtc_h":dt.hour,
2269
+ "rtc_i":dt.minute,
2270
+ "rtc_s":dt.second,
2271
+ })
2272
+ for y in range(dt.year, 0, -1):
2273
+ if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0):
2274
+ rtc_dict["rtc_leap_year_state"] = dt.year - y
2275
+ break
2276
+ mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
2277
+ rtc_dict["rtc_y"] += 19
2278
+ rtc_dict["rtc_buffer"] = rtc_data["rtc_buffer"]
2279
+ args = { "mbc":mbc, "rtc_dict":rtc_dict }
2280
+ else:
2281
+ return False
2282
+
2283
+ elif self.CONN.GetMode() == "AGB":
2284
+ dlg_args = {
2285
+ "title":"GBA Real Time Clock Editor",
2286
+ "intro":"Enter the date and time for the Real Time Clock.\n\nPlease note that all values are internal values. The game may use these only as a relative reference.",
2287
+ "params": [
2288
+ # ID, Type, Value(s), Default Index
2289
+ [ "rtc_y", "spb", "Year:", (2000, 2099), rtc_data["rtc_y"] + 2000 ],
2290
+ [ "rtc_m", "spb", "Month:", (1, 12), rtc_data["rtc_m"] ],
2291
+ [ "rtc_d", "spb", "Day:", (1, 31), rtc_data["rtc_d"] ],
2292
+ [ "rtc_h", "spb", "Hours:", (0, 23), rtc_data["rtc_h"] ],
2293
+ [ "rtc_i", "spb", "Minutes:", (0, 59), rtc_data["rtc_i"] ],
2294
+ [ "rtc_s", "spb", "Seconds:", (0, 59), rtc_data["rtc_s"] ],
2295
+ [ "rtc_w", "cmb", "Weekday:", list(calendar.day_name), rtc_data["rtc_w"] ],
2296
+ [ "current", "chk", "Ignore above values and use the current date and time instead", None, False ],
2297
+ ]
2298
+ }
2299
+ dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
2300
+ if dlg.exec_() == 1:
2301
+ result = dlg.GetResult()
2302
+ rtc_dict = {}
2303
+ for key, value in result.items():
2304
+ if isinstance(value, QtWidgets.QSpinBox):
2305
+ rtc_dict[key] = value.value()
2306
+ elif isinstance(value, QtWidgets.QComboBox):
2307
+ rtc_dict[key] = value.currentIndex()
2308
+ if result["current"].isChecked():
2309
+ dt = datetime.datetime.now() + datetime.timedelta(seconds=1)
2310
+ rtc_dict.update({
2311
+ "rtc_y":dt.year,
2312
+ "rtc_m":dt.month,
2313
+ "rtc_d":dt.day,
2314
+ "rtc_w":dt.weekday(),
2315
+ "rtc_h":dt.hour,
2316
+ "rtc_i":dt.minute,
2317
+ "rtc_s":dt.second,
2318
+ })
2319
+ rtc_dict["rtc_y"] -= 2000
2320
+ mbc = Util.ConvertMapperTypeToMapper(self.cmbDMGHeaderMapperResult.currentIndex())
2321
+ args = { "rtc_dict":rtc_dict }
2322
+ else:
2323
+ return False
2324
+
2325
+ self.STATUS["args"] = args
2326
+ ret = self.CONN.WriteRTC(args=args)
2327
+ self.ReadCartridge(resetStatus=False)
2328
+ if ret:
2329
+ QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), "The Real Time Clock register values have been updated.", QtWidgets.QMessageBox.Ok)
2330
+ return True
2331
+ else:
2332
+ QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "An error occured while updating the Real Time Clock register values.", QtWidgets.QMessageBox.Ok)
2333
+ return False
2334
+
1975
2335
  def CheckDeviceAlive(self, setMode=False):
1976
2336
  if self.CONN is not None:
1977
2337
  mode = self.CONN.GetMode()
@@ -2068,21 +2428,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2068
2428
  data = self.CONN.ReadInfo(setPinsAsInputs=True)
2069
2429
  except SerialException:
2070
2430
  self.DisconnectDevice()
2071
- 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)
2431
+ 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)
2072
2432
  return False
2073
2433
 
2074
- if resetStatus:
2075
- self.btnHeaderRefresh.setEnabled(True)
2076
- self.btnDetectCartridge.setEnabled(True)
2077
- self.btnBackupROM.setEnabled(True)
2078
- self.btnFlashROM.setEnabled(True)
2079
- self.btnBackupRAM.setEnabled(True)
2080
- self.btnRestoreRAM.setEnabled(True)
2081
- self.btnHeaderRefresh.setFocus()
2082
- self.SetProgressBars(min=0, max=100, value=0)
2083
- self.lblStatus4a.setText("Ready.")
2084
- qt_app.processEvents()
2085
-
2086
2434
  if data == False or len(data) == 0:
2087
2435
  self.DisconnectDevice()
2088
2436
  QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "Invalid response from the device.", QtWidgets.QMessageBox.Ok)
@@ -2114,7 +2462,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2114
2462
  self.lblDMGGameCodeRevisionResult.setText("{:s}-{:s}".format(data["db"]["gc"], str(data["version"])))
2115
2463
  temp = data["db"]["gn"]
2116
2464
  self.lblDMGGameNameResult.setText(temp)
2117
- while self.lblDMGGameNameResult.fontMetrics().boundingRect(self.lblDMGGameNameResult.text()).width() > 170:
2465
+ while self.lblDMGGameNameResult.fontMetrics().boundingRect(self.lblDMGGameNameResult.text()).width() > 200:
2118
2466
  temp = temp[:-1]
2119
2467
  self.lblDMGGameNameResult.setText(temp + "…")
2120
2468
  if temp != data["db"]["gn"]:
@@ -2128,6 +2476,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2128
2476
  self.lblDMGGameCodeRevisionResult.setText(str(data['version']))
2129
2477
 
2130
2478
  self.lblDMGHeaderRtcResult.setText(data["rtc_string"])
2479
+ if data["has_rtc"] is True and len(data["rtc_dict"]) > 0 and "rtc_valid" in data["rtc_dict"] and data["rtc_dict"]["rtc_valid"] is True:
2480
+ self.lblDMGHeaderRtcResult.setCursor(QtCore.Qt.PointingHandCursor)
2481
+ self.lblDMGHeaderRtcResult.setToolTip("Click here to edit the Real Time Clock register values")
2482
+ else:
2483
+ self.lblDMGHeaderRtcResult.setCursor(QtCore.Qt.ArrowCursor)
2484
+ self.lblDMGHeaderRtcResult.setToolTip("")
2131
2485
 
2132
2486
  if data['logo_correct'] and data['header_checksum_correct']:
2133
2487
  self.lblDMGHeaderBootlogoResult.setText("OK")
@@ -2162,7 +2516,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2162
2516
  self.lblDMGGameNameResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
2163
2517
 
2164
2518
  if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["mapper_raw"]) and resetStatus:
2165
- QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates in the Tools menu or the maker’s website.".format(APPNAME, self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
2519
+ QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a mapper that may not be completely supported by {:s} using your {:s} device.".format(APPNAME, self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
2166
2520
  if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU "):
2167
2521
  cart_types = self.CONN.GetSupportedCartridgesDMG()
2168
2522
  for i in range(0, len(cart_types[0])):
@@ -2175,6 +2529,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2175
2529
  self.lblDMGHeaderBootlogoResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
2176
2530
  self.lblDMGHeaderROMChecksumResult.setText("")
2177
2531
  self.lblDMGHeaderROMChecksumResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
2532
+ # cart_types = self.CONN.GetSupportedCartridgesDMG()
2533
+ # for i in range(0, len(cart_types[0])):
2534
+ # if "command_set" in cart_types[1][i] and cart_types[1][i]["command_set"] == "BLAZE_XPLODER":
2535
+ # self.cmbDMGCartridgeTypeResult.setCurrentIndex(i)
2178
2536
  elif data["mapper_raw"] == 0x205: # Datel
2179
2537
  self.lblDMGHeaderRtcResult.setText("")
2180
2538
  self.lblDMGHeaderBootlogoResult.setText("")
@@ -2199,7 +2557,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2199
2557
  else:
2200
2558
  if "logo" in data:
2201
2559
  if data['logo_correct']:
2202
- data["logo"].putpalette([ 255, 255, 255, self.TEXT_COLOR[0], self.TEXT_COLOR[1], self.TEXT_COLOR[2] ])
2560
+ rgb = ( self.TEXT_COLOR[0], self.TEXT_COLOR[1], self.TEXT_COLOR[2] ) # GUI font color
2561
+ rgb = tuple(min(255, int(c + (127.5 - c) * 0.25)) if c < 127.5 else max(0, int(c - (c - 127.5) * 0.25)) for c in rgb)
2562
+ data["logo"].putpalette([ 255, 255, 255, rgb[0], rgb[1], rgb[2] ])
2203
2563
  else:
2204
2564
  data["logo"].putpalette([ 255, 255, 255, 251, 0, 24 ])
2205
2565
  try:
@@ -2224,7 +2584,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2224
2584
  self.lblAGBHeaderGameCodeRevisionResult.setText("{:s}-{:s}".format(data["db"]["gc"], str(data["version"])))
2225
2585
  temp = data["db"]["gn"]
2226
2586
  self.lblAGBGameNameResult.setText(temp)
2227
- while self.lblAGBGameNameResult.fontMetrics().boundingRect(self.lblAGBGameNameResult.text()).width() > 170:
2587
+ while self.lblAGBGameNameResult.fontMetrics().boundingRect(self.lblAGBGameNameResult.text()).width() > 200:
2228
2588
  temp = temp[:-1]
2229
2589
  self.lblAGBGameNameResult.setText(temp + "…")
2230
2590
  if temp != data["db"]["gn"]:
@@ -2237,16 +2597,22 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2237
2597
  self.lblAGBGameNameResult.setText("(Not in database)")
2238
2598
 
2239
2599
  if data['logo_correct']:
2240
- self.lblAGBHeaderLogoValidResult.setText("OK")
2241
- self.lblAGBHeaderLogoValidResult.setStyleSheet(self.lblAGBRomTitleResult.styleSheet())
2600
+ self.lblAGBHeaderBootlogoResult.setText("OK")
2601
+ self.lblAGBHeaderBootlogoResult.setStyleSheet(self.lblAGBRomTitleResult.styleSheet())
2242
2602
  if not os.path.exists(Util.CONFIG_PATH + "/bootlogo_agb.bin"):
2243
2603
  with open(Util.CONFIG_PATH + "/bootlogo_agb.bin", "wb") as f:
2244
2604
  f.write(data['raw'][0x04:0xA0])
2245
2605
  else:
2246
- self.lblAGBHeaderLogoValidResult.setText("Invalid")
2247
- self.lblAGBHeaderLogoValidResult.setStyleSheet("QLabel { color: red; }")
2606
+ self.lblAGBHeaderBootlogoResult.setText("Invalid")
2607
+ self.lblAGBHeaderBootlogoResult.setStyleSheet("QLabel { color: red; }")
2248
2608
 
2249
2609
  self.lblAGBGpioRtcResult.setText(data["rtc_string"])
2610
+ if data["has_rtc"] is True and len(data["rtc_dict"]) > 0 and "rtc_valid" in data["rtc_dict"] and data["rtc_dict"]["rtc_valid"] is True:
2611
+ self.lblAGBGpioRtcResult.setCursor(QtCore.Qt.PointingHandCursor)
2612
+ self.lblAGBGpioRtcResult.setToolTip("Click here to edit the Real Time Clock register values")
2613
+ else:
2614
+ self.lblAGBGpioRtcResult.setCursor(QtCore.Qt.ArrowCursor)
2615
+ self.lblAGBGpioRtcResult.setToolTip("")
2250
2616
 
2251
2617
  if data['header_checksum_correct']:
2252
2618
  self.lblAGBHeaderChecksumResult.setText("Valid (0x{:02X})".format(data['header_checksum']))
@@ -2286,10 +2652,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2286
2652
  self.cmbAGBSaveTypeResult.setCurrentIndex(0)
2287
2653
  else:
2288
2654
  self.lblAGBGameNameResult.setStyleSheet(self.lblDMGRomTitleResult.styleSheet())
2289
- if data['logo_correct'] and data['3d_memory'] is True:
2655
+ if data['logo_correct']:
2290
2656
  cart_types = self.CONN.GetSupportedCartridgesAGB()
2291
2657
  for i in range(0, len(cart_types[0])):
2292
- if "3d_memory" in cart_types[1][i]:
2658
+ if ((data['3d_memory'] is True and "3d_memory" in cart_types[1][i]) or
2659
+ (data['vast_fame'] is True and "vast_fame" in cart_types[1][i])):
2293
2660
  self.cmbAGBCartridgeTypeResult.setCurrentIndex(i)
2294
2661
 
2295
2662
  if data["dacs_8m"] is True:
@@ -2300,7 +2667,19 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2300
2667
 
2301
2668
  if data['logo_correct'] and isinstance(data["db"], dict) and "rs" in data["db"] and data["db"]['rs'] == 0x4000000 and not self.CONN.IsSupported3dMemory() and resetStatus:
2302
2669
  QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a Memory Bank Controller that may not be completely supported by the firmware of the {:s} device. Please check for firmware updates in the Tools menu or the maker’s website.".format(self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
2303
-
2670
+
2671
+ if "logo" in data:
2672
+ if data['logo_correct']:
2673
+ rgb = ( self.TEXT_COLOR[0], self.TEXT_COLOR[1], self.TEXT_COLOR[2] ) # GUI font color
2674
+ rgb = tuple(min(255, int(c + (127.5 - c) * 0.25)) if c < 127.5 else max(0, int(c - (c - 127.5) * 0.25)) for c in rgb)
2675
+ data["logo"].putpalette([ 255, 255, 255, rgb[0], rgb[1], rgb[2] ])
2676
+ else:
2677
+ data["logo"].putpalette([ 255, 255, 255, 251, 0, 24 ])
2678
+ try:
2679
+ self.lblAGBHeaderBootlogoResult.setPixmap(QtGui.QPixmap.fromImage(ImageQt(data["logo"].convert("RGBA"))))
2680
+ except:
2681
+ pass
2682
+
2304
2683
  if resetStatus:
2305
2684
  self.lblStatus1aResult.setText("–")
2306
2685
  self.lblStatus2aResult.setText("–")
@@ -2308,6 +2687,16 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2308
2687
  self.lblStatus4a.setText("Ready.")
2309
2688
  self.grpStatus.setTitle("Transfer Status")
2310
2689
  self.FinishOperation()
2690
+ self.btnHeaderRefresh.setEnabled(True)
2691
+ self.btnDetectCartridge.setEnabled(True)
2692
+ self.btnBackupROM.setEnabled(True)
2693
+ self.btnFlashROM.setEnabled(True)
2694
+ self.btnBackupRAM.setEnabled(True)
2695
+ self.btnRestoreRAM.setEnabled(True)
2696
+ self.btnHeaderRefresh.setFocus()
2697
+ self.SetProgressBars(min=0, max=100, value=0)
2698
+ self.lblStatus4a.setText("Ready.")
2699
+ qt_app.processEvents()
2311
2700
 
2312
2701
  if data['game_title'][:11] == "YJencrypted" and resetStatus:
2313
2702
  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)
@@ -2361,7 +2750,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2361
2750
  supp_cart_types = self.CONN.GetSupportedCartridgesDMG()
2362
2751
  elif self.CONN.GetMode() == "AGB":
2363
2752
  supp_cart_types = self.CONN.GetSupportedCartridgesAGB()
2364
-
2753
+ except Exception as e:
2754
+ 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)
2755
+ msgbox.exec()
2756
+ return
2757
+
2758
+ try:
2365
2759
  if len(cart_types) > 0:
2366
2760
  cart_type = cart_type_id
2367
2761
  if self.CONN.GetMode() == "DMG":
@@ -2388,6 +2782,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2388
2782
 
2389
2783
  # Save Type
2390
2784
  msg_save_type_s = ""
2785
+ temp = ""
2391
2786
  if not canSkipMessage and save_type is not False and save_type is not None:
2392
2787
  if save_chip is not None:
2393
2788
  temp = "{:s} ({:s})".format(Util.AGB_Header_Save_Types[save_type], save_chip)
@@ -2454,7 +2849,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2454
2849
 
2455
2850
  else:
2456
2851
  if (len(flash_id.split("\n")) > 2) and ((self.CONN.GetMode() == "DMG") or ("dacs_8m" in header and header["dacs_8m"] is not True)):
2457
- msg_cart_type_s = "<b>Cartridge Type:</b> Unknown flash cartridge – Please submit the displayed information along with a picture of the cartridge’s circuit board."
2852
+ msg_cart_type_s = "<b>Cartridge Type:</b> Unknown flash cartridge"
2458
2853
  if ("[ 0/90]" in flash_id):
2459
2854
  try_this = "Generic Flash Cartridge (0/90)"
2460
2855
  elif ("[ AAA/AA]" in flash_id):
@@ -2496,7 +2891,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2496
2891
  show_details = False
2497
2892
 
2498
2893
  msg_gbmem = ""
2499
- if "gbmem_parsed" in header:
2894
+ if "gbmem_parsed" in header and header["gbmem_parsed"] is not None:
2500
2895
  msg_gbmem = "<br><b>NP GB-Memory Cartridge Data:</b><br>"
2501
2896
  if isinstance(header["gbmem_parsed"], list):
2502
2897
  msg_gbmem += "" \
@@ -2628,6 +3023,18 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2628
3023
  self.lblStatus4a.setText("Ready.")
2629
3024
  return cart_type
2630
3025
 
3026
+ def WaitProgress(self, args):
3027
+ if args["user_action"] == "REINSERT_CART":
3028
+ title = "{:s} {:s}".format(APPNAME, VERSION)
3029
+ if "title" in args:
3030
+ title += " – " + args["title"]
3031
+ msg = args["msg"]
3032
+ answer = QtWidgets.QMessageBox.warning(self, title, msg, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok)
3033
+ if answer == QtWidgets.QMessageBox.Ok:
3034
+ self.CONN.USER_ANSWER = True
3035
+ else:
3036
+ self.CONN.USER_ANSWER = False
3037
+
2631
3038
  def UpdateProgress(self, args):
2632
3039
  if args is None: return
2633
3040
  if self.CONN is None: return
@@ -2651,8 +3058,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2651
3058
  self.grpDMGCartridgeInfo.setEnabled(True)
2652
3059
  self.grpAGBCartridgeInfo.setEnabled(True)
2653
3060
  self.grpActions.setEnabled(True)
2654
- self.btnTools.setEnabled(True)
2655
- self.btnConfig.setEnabled(True)
3061
+ self.mnuTools.setEnabled(True)
3062
+ self.mnuConfig.setEnabled(True)
2656
3063
  self.btnCancel.setEnabled(False)
2657
3064
  msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=str(args["error"]), standardButtons=QtWidgets.QMessageBox.Ok)
2658
3065
  if not '\n' in str(args["error"]): msgbox.setTextFormat(QtCore.Qt.RichText)
@@ -2662,8 +3069,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2662
3069
  self.grpDMGCartridgeInfo.setEnabled(False)
2663
3070
  self.grpAGBCartridgeInfo.setEnabled(False)
2664
3071
  self.grpActions.setEnabled(False)
2665
- self.btnTools.setEnabled(False)
2666
- self.btnConfig.setEnabled(False)
3072
+ self.mnuTools.setEnabled(False)
3073
+ self.mnuConfig.setEnabled(False)
2667
3074
 
2668
3075
  pos = 0
2669
3076
  size = 0
@@ -2746,8 +3153,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2746
3153
  self.grpDMGCartridgeInfo.setEnabled(True)
2747
3154
  self.grpAGBCartridgeInfo.setEnabled(True)
2748
3155
  self.grpActions.setEnabled(True)
2749
- self.btnTools.setEnabled(True)
2750
- self.btnConfig.setEnabled(True)
3156
+ self.mnuTools.setEnabled(True)
3157
+ self.mnuConfig.setEnabled(True)
2751
3158
  self.grpStatus.setTitle("Transfer Status")
2752
3159
  self.lblStatus1aResult.setText("–")
2753
3160
  self.lblStatus2aResult.setText("–")
@@ -2818,8 +3225,25 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2818
3225
  def ShowFirmwareUpdateWindow(self):
2819
3226
  if self.CONN is None:
2820
3227
  try:
2821
- from . import fw_GBxCartRW_v1_4
2822
- FirmwareUpdater = fw_GBxCartRW_v1_4.FirmwareUpdaterWindow
3228
+ dev_types = {
3229
+ "GBxCart RW v1.4 or v1.4a/b/c":hw_GBxCartRW.GbxDevice.GetFirmwareUpdaterClass(None),
3230
+ "GBFlash":hw_GBFlash.GbxDevice.GetFirmwareUpdaterClass(None),
3231
+ "Joey Jr":hw_JoeyJr.GbxDevice.GetFirmwareUpdaterClass(None),
3232
+ }
3233
+ dlg_args = {
3234
+ "title":"Select Device Type",
3235
+ "intro":"Please select the device that you are using below.",
3236
+ "params": [
3237
+ # ID, Type, Value(s), Default Index
3238
+ [ "dev_type", "cmb", "Device Type:", dev_types.keys(), 0 ]
3239
+ ]
3240
+ }
3241
+ dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
3242
+ if dlg.exec_() == 1:
3243
+ result = dlg.GetResult()
3244
+ FirmwareUpdater = list(dev_types.values())[result["dev_type"].currentIndex()][1]
3245
+ else:
3246
+ return False
2823
3247
  except:
2824
3248
  return False
2825
3249
  else:
@@ -2828,6 +3252,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2828
3252
  return False
2829
3253
  else:
2830
3254
  FirmwareUpdater = self.CONN.GetFirmwareUpdaterClass()[1]
3255
+
2831
3256
  self.FWUPWIN = None
2832
3257
  self.FWUPWIN = FirmwareUpdater(self, app_path=Util.APP_PATH, icon=self.windowIcon(), device=self.CONN)
2833
3258
  self.FWUPWIN.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
@@ -2837,9 +3262,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2837
3262
  def ShowPocketCameraWindow(self):
2838
3263
  data = None
2839
3264
  if self.CONN is not None:
2840
- if self.CONN.GetMode() is None:
2841
- self.optDMG.setChecked(True)
2842
- self.SetMode()
3265
+ if self.CONN.GetMode() is None and "DMG" in self.CONN.GetSupprtedModes():
3266
+ answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), "Is a Game Boy Camera cartridge currently inserted?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
3267
+ if answer == QtWidgets.QMessageBox.Yes:
3268
+ self.optDMG.setChecked(True)
3269
+ self.SetMode()
2843
3270
  if self.CONN.GetMode() == "DMG":
2844
3271
  header = self.CONN.ReadInfo(setPinsAsInputs=True)
2845
3272
  if header["mapper_raw"] == 252: # GBD
@@ -2920,25 +3347,30 @@ class FlashGBX_GUI(QtWidgets.QWidget):
2920
3347
  self.show()
2921
3348
 
2922
3349
  if callable(getattr(qt_app, "exec", None)): # PySide6
2923
- qt_app.exec()
2924
- # Taskbar Progress on Windows only
2925
3350
  try:
2926
- from PySide6.QtWin import QtWinTaskbarButton, QtWin
2927
- myappid = 'lesserkuma.flashgbx'
2928
- QtWin.setAppUserModelId(myappid)
2929
- taskbar_button = QtWinTaskbarButton()
2930
- self.TBPROG = taskbar_button.progress()
2931
- self.TBPROG.setRange(0, 100)
2932
- taskbar_button.setWindow(self.windowHandle())
2933
- self.TBPROG.setVisible(False)
2934
- except ImportError:
3351
+ if platform.system() == "Windows":
3352
+ qt_app.setStyle("windowsvista")
3353
+ except:
2935
3354
  pass
3355
+ qt_app.exec()
3356
+ # # Taskbar Progress on Windows only
3357
+ # try:
3358
+ # from PySide6.QtWin import QtWinTaskbarButton, QtWin
3359
+ # myappid = 'lesserkuma.flashgbx'
3360
+ # QtWin.setAppUserModelId(myappid)
3361
+ # taskbar_button = QtWinTaskbarButton()
3362
+ # self.TBPROG = taskbar_button.progress()
3363
+ # self.TBPROG.setRange(0, 100)
3364
+ # taskbar_button.setWindow(self.windowHandle())
3365
+ # self.TBPROG.setVisible(False)
3366
+ # except ImportError:
3367
+ # pass
2936
3368
 
2937
3369
  else: # PySide2
2938
3370
  qt_app.exec_()
2939
3371
  # Taskbar Progress on Windows only
2940
3372
  try:
2941
- from PySide2.QtWinExtras import QWinTaskbarButton, QtWin
3373
+ from PySide2.QtWinExtras import QWinTaskbarButton, QtWin # type: ignore
2942
3374
  myappid = 'lesserkuma.flashgbx'
2943
3375
  QtWin.setCurrentProcessExplicitAppUserModelID(myappid)
2944
3376
  taskbar_button = QWinTaskbarButton()