FlashGBX 4.2__py3-none-any.whl → 4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- FlashGBX/DataTransfer.py +1 -2
- FlashGBX/FlashGBX.py +2 -2
- FlashGBX/FlashGBX_CLI.py +40 -29
- FlashGBX/FlashGBX_GUI.py +484 -191
- FlashGBX/Flashcart.py +3 -1
- FlashGBX/GBMemory.py +3 -1
- FlashGBX/LK_Device.py +408 -199
- FlashGBX/Mapper.py +46 -7
- FlashGBX/PocketCamera.py +2 -2
- FlashGBX/PocketCameraWindow.py +35 -5
- FlashGBX/RomFileAGB.py +4 -1
- FlashGBX/RomFileDMG.py +13 -2
- FlashGBX/Util.py +86 -16
- FlashGBX/fw_GBFlash.py +0 -5
- FlashGBX/fw_GBxCartRW_v1_4.py +2 -5
- FlashGBX/fw_JoeyJr.py +23 -8
- FlashGBX/hw_GBFlash.py +16 -7
- FlashGBX/hw_GBxCartRW.py +18 -16
- FlashGBX/hw_JoeyJr.py +2 -2
- FlashGBX/res/Third Party Notices.md +21 -1
- FlashGBX/res/config.zip +0 -0
- FlashGBX/res/fw_GBFlash.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4a.zip +0 -0
- FlashGBX/res/fw_JoeyJr.zip +0 -0
- {FlashGBX-4.2.dist-info → FlashGBX-4.4.dist-info}/METADATA +101 -52
- FlashGBX-4.4.dist-info/RECORD +43 -0
- {FlashGBX-4.2.dist-info → FlashGBX-4.4.dist-info}/WHEEL +1 -1
- FlashGBX-4.2.dist-info/RECORD +0 -43
- {FlashGBX-4.2.dist-info → FlashGBX-4.4.dist-info}/LICENSE +0 -0
- {FlashGBX-4.2.dist-info → FlashGBX-4.4.dist-info}/entry_points.txt +0 -0
- {FlashGBX-4.2.dist-info → FlashGBX-4.4.dist-info}/top_level.txt +0 -0
FlashGBX/Mapper.py
CHANGED
|
@@ -75,8 +75,8 @@ class DMG_MBC:
|
|
|
75
75
|
return DMG_Unlicensed_Sachen(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
|
76
76
|
elif mbc_id == 0x205: # 0x205:'Datel Orbit V2',
|
|
77
77
|
return DMG_Unlicensed_DatelOrbitV2(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
elif mbc_id == 0x206: # 0x206:'MBCX',
|
|
79
|
+
return DMG_Unlicensed_MBCX(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
|
80
80
|
else:
|
|
81
81
|
self.__init__(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
|
82
82
|
return self
|
|
@@ -269,7 +269,7 @@ class DMG_MBC3(DMG_MBC):
|
|
|
269
269
|
|
|
270
270
|
def HasRTC(self):
|
|
271
271
|
dprint("Checking for RTC")
|
|
272
|
-
if self.MBC_ID not in (0x0F, 0x10, 0x110):
|
|
272
|
+
if self.MBC_ID not in (0x0F, 0x10, 0x110, 0x206):
|
|
273
273
|
dprint("No RTC because mapper value is not used for RTC:", self.MBC_ID)
|
|
274
274
|
return False
|
|
275
275
|
self.EnableRAM(enable=False)
|
|
@@ -831,6 +831,7 @@ class DMG_GMMC1(DMG_MBC5):
|
|
|
831
831
|
def CalcChecksum(self, buffer):
|
|
832
832
|
header = RomFileDMG(buffer[:0x180]).GetHeader()
|
|
833
833
|
target_chk_value = 0
|
|
834
|
+
target_sha1_value = 0
|
|
834
835
|
if header["game_title"] == "NP M-MENU MENU":
|
|
835
836
|
target_sha1_value = "15f5d445c0b2fdf4221cf2a986a4a5cb8dfda131"
|
|
836
837
|
target_chk_value = 0x19E8
|
|
@@ -1531,6 +1532,44 @@ class DMG_Unlicensed_DatelOrbitV2(DMG_MBC):
|
|
|
1531
1532
|
def GetMaxROMSize(self):
|
|
1532
1533
|
return 128*1024
|
|
1533
1534
|
|
|
1535
|
+
class DMG_Unlicensed_MBCX(DMG_MBC3):
|
|
1536
|
+
def GetName(self):
|
|
1537
|
+
return "MBCX"
|
|
1538
|
+
|
|
1539
|
+
def HasFlashBanks(self):
|
|
1540
|
+
return True
|
|
1541
|
+
|
|
1542
|
+
def SelectBankFlash(self, index):
|
|
1543
|
+
dprint(self.GetName(), "|SelectBankFlash()|", index)
|
|
1544
|
+
|
|
1545
|
+
commands = [
|
|
1546
|
+
[ 0x0000, 0x05 ],
|
|
1547
|
+
[ 0x4000, 0x82 ],
|
|
1548
|
+
[ 0xA000, index ],
|
|
1549
|
+
[ 0x0000, 0x00 ]
|
|
1550
|
+
]
|
|
1551
|
+
self.CURRENT_FLASH_BANK = index
|
|
1552
|
+
self.CartWrite(commands, delay=0.1)
|
|
1553
|
+
|
|
1554
|
+
def SelectBankROM(self, index):
|
|
1555
|
+
dprint(self.GetName(), index)
|
|
1556
|
+
|
|
1557
|
+
if (index % 512 == 0) or (self.CURRENT_FLASH_BANK != math.floor(index / 512)):
|
|
1558
|
+
self.SelectBankFlash(math.floor(index / 512))
|
|
1559
|
+
self.CURRENT_ROM_BANK = index
|
|
1560
|
+
index = index % 512
|
|
1561
|
+
|
|
1562
|
+
commands = [
|
|
1563
|
+
[ 0x3000, ((index >> 8) & 0xFF) ],
|
|
1564
|
+
[ 0x2100, index & 0xFF ],
|
|
1565
|
+
]
|
|
1566
|
+
|
|
1567
|
+
self.CartWrite(commands)
|
|
1568
|
+
return (0x4000, self.ROM_BANK_SIZE)
|
|
1569
|
+
|
|
1570
|
+
def GetMaxROMSize(self):
|
|
1571
|
+
return 32*1024*1024
|
|
1572
|
+
|
|
1534
1573
|
|
|
1535
1574
|
class AGB_GPIO:
|
|
1536
1575
|
CART_WRITE_FNCPTR = None
|
|
@@ -1608,7 +1647,7 @@ class AGB_GPIO:
|
|
|
1608
1647
|
temp = self.CartRead(self.GPIO_REG_DAT) & 0xFF
|
|
1609
1648
|
bit = (temp & 2) >> 1
|
|
1610
1649
|
data = (data >> 1) | (bit << 7)
|
|
1611
|
-
#
|
|
1650
|
+
# dprint("RTCReadData(): i={:d}/temp={:X}/bit={:x}/data={:x}".format(i, temp, bit, data))
|
|
1612
1651
|
return data
|
|
1613
1652
|
|
|
1614
1653
|
def RTCWriteData(self, data):
|
|
@@ -1664,7 +1703,8 @@ class AGB_GPIO:
|
|
|
1664
1703
|
status = self.RTCReadStatus()
|
|
1665
1704
|
else:
|
|
1666
1705
|
status = buffer[0]
|
|
1667
|
-
|
|
1706
|
+
|
|
1707
|
+
dprint("Status:", bin(status))
|
|
1668
1708
|
if (status >> 7) == 1:
|
|
1669
1709
|
dprint("No RTC because of set RTC Status Register Power Flag:", status >> 7 & 1)
|
|
1670
1710
|
return 1
|
|
@@ -1684,7 +1724,7 @@ class AGB_GPIO:
|
|
|
1684
1724
|
else:
|
|
1685
1725
|
rom2 = buffer[1:7]
|
|
1686
1726
|
|
|
1687
|
-
dprint(' '.join(format(x, '02X') for x in rom1), "/", ' '.join(format(x, '02X') for x in rom2))
|
|
1727
|
+
dprint("RTC Data:", ' '.join(format(x, '02X') for x in rom1), "/", ' '.join(format(x, '02X') for x in rom2))
|
|
1688
1728
|
if (rom1 == rom2):
|
|
1689
1729
|
dprint("No RTC because ROM data didn’t change:", rom1, rom2)
|
|
1690
1730
|
return 3
|
|
@@ -1869,7 +1909,6 @@ class AGB_GPIO:
|
|
|
1869
1909
|
}
|
|
1870
1910
|
|
|
1871
1911
|
if rtc_y == 0 and rtc_m == 0 and rtc_d == 0 and rtc_h == 0 and rtc_i == 0 and rtc_s == 0:
|
|
1872
|
-
#raise ValueError("Invalid RTC data")
|
|
1873
1912
|
d["string"] = "Invalid RTC data"
|
|
1874
1913
|
d["rtc_valid"] = False
|
|
1875
1914
|
else:
|
FlashGBX/PocketCamera.py
CHANGED
|
@@ -110,7 +110,7 @@ class PocketCamera:
|
|
|
110
110
|
offset = 0x2000 + (index * 0x1000)
|
|
111
111
|
elif index == 30:
|
|
112
112
|
offset = 0x11FC
|
|
113
|
-
|
|
113
|
+
else:
|
|
114
114
|
offset = 0
|
|
115
115
|
imgbuffer = self.DATA[offset:offset+0x1000]
|
|
116
116
|
return self.ConvertPicture(imgbuffer)
|
|
@@ -138,7 +138,7 @@ class PocketCamera:
|
|
|
138
138
|
frame.paste(pic, (left, top))
|
|
139
139
|
pic = frame
|
|
140
140
|
|
|
141
|
-
pic = pic.resize((pic.width * scale, pic.height * scale), Image.NEAREST)
|
|
141
|
+
pic = pic.resize((pic.width * scale, pic.height * scale), Image.Resampling.NEAREST)
|
|
142
142
|
|
|
143
143
|
ext = os.path.splitext(path)[1]
|
|
144
144
|
if ext == "" or ext.lower() == ".png":
|
FlashGBX/PocketCameraWindow.py
CHANGED
|
@@ -7,6 +7,7 @@ from PIL.ImageQt import ImageQt
|
|
|
7
7
|
from PIL import Image, ImageDraw
|
|
8
8
|
from .pyside import QtCore, QtWidgets, QtGui, QDesktopWidget
|
|
9
9
|
from .PocketCamera import PocketCamera
|
|
10
|
+
from .UserInputDialog import UserInputDialog
|
|
10
11
|
|
|
11
12
|
class PocketCameraWindow(QtWidgets.QDialog):
|
|
12
13
|
CUR_PIC = None
|
|
@@ -20,6 +21,7 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
20
21
|
APP_PATH = "."
|
|
21
22
|
CONFIG_PATH = "."
|
|
22
23
|
APP = None
|
|
24
|
+
FORCE_EXIT = False
|
|
23
25
|
PALETTES = [
|
|
24
26
|
[ 255, 255, 255, 176, 176, 176, 104, 104, 104, 0, 0, 0 ], # Grayscale
|
|
25
27
|
[ 208, 217, 60, 120, 164, 106, 84, 88, 84, 36, 70, 36 ], # Game Boy
|
|
@@ -33,6 +35,8 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
33
35
|
QtWidgets.QDialog.__init__(self)
|
|
34
36
|
self.setAcceptDrops(True)
|
|
35
37
|
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
|
|
38
|
+
|
|
39
|
+
self.FORCE_EXIT = False
|
|
36
40
|
self.CUR_FILE = file
|
|
37
41
|
self.CONFIG_PATH = config_path
|
|
38
42
|
self.APP_PATH = app_path
|
|
@@ -188,7 +192,9 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
188
192
|
self.CUR_PALETTE = len(self.PALETTES) - 1
|
|
189
193
|
|
|
190
194
|
if self.CUR_FILE is not None:
|
|
191
|
-
self.OpenFile(self.CUR_FILE)
|
|
195
|
+
if self.OpenFile(self.CUR_FILE) is False:
|
|
196
|
+
self.FORCE_EXIT = True
|
|
197
|
+
return
|
|
192
198
|
|
|
193
199
|
self.CUR_EXPORT_PATH = self.APP.SETTINGS.value("LastDirPocketCamera")
|
|
194
200
|
if self.CUR_EXPORT_PATH is None:
|
|
@@ -201,6 +207,9 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
201
207
|
self.btnSaveAll.setFocus()
|
|
202
208
|
|
|
203
209
|
def run(self):
|
|
210
|
+
if self.FORCE_EXIT:
|
|
211
|
+
self.reject()
|
|
212
|
+
return
|
|
204
213
|
self.layout.update()
|
|
205
214
|
self.layout.activate()
|
|
206
215
|
screenGeometry = QDesktopWidget().screenGeometry(self)
|
|
@@ -217,6 +226,26 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
217
226
|
self.UpdateViewer(self.CUR_INDEX)
|
|
218
227
|
|
|
219
228
|
def OpenFile(self, file):
|
|
229
|
+
if isinstance(file, bytearray) and len(file) == 0x100000 or isinstance(file, str) and os.path.getsize(file) == 0x100000:
|
|
230
|
+
dlg_args = {
|
|
231
|
+
"title":"Photo!",
|
|
232
|
+
"intro":"A Photo! save file was detected.\n\nPlease select the roll of pictures that you would like to load.",
|
|
233
|
+
"params": [
|
|
234
|
+
[ "index", "cmb", "Roll:", [ "Current Save Data" ] + [ "Flash Directory Slot {:d}".format(l) for l in range(1, 8) ], 0 ],
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
dlg = UserInputDialog(self, icon=self.windowIcon(), args=dlg_args)
|
|
238
|
+
if dlg.exec_() == 1:
|
|
239
|
+
result = dlg.GetResult()
|
|
240
|
+
index = result["index"].currentIndex()
|
|
241
|
+
if isinstance(file, str):
|
|
242
|
+
with open(file, "rb") as f:
|
|
243
|
+
file = bytearray(f.read())
|
|
244
|
+
file = file[0x20000 * index:][:0x20000]
|
|
245
|
+
else:
|
|
246
|
+
self.CUR_PC = None
|
|
247
|
+
return False
|
|
248
|
+
|
|
220
249
|
try:
|
|
221
250
|
self.CUR_PC = PocketCamera()
|
|
222
251
|
if self.CUR_PC.LoadFile(file) == False:
|
|
@@ -285,6 +314,7 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
285
314
|
self.SavePicture(self.CUR_INDEX)
|
|
286
315
|
|
|
287
316
|
def btnClose_Clicked(self, event):
|
|
317
|
+
self.FORCE_EXIT = True
|
|
288
318
|
self.reject()
|
|
289
319
|
|
|
290
320
|
def hideEvent(self, event):
|
|
@@ -309,13 +339,13 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
309
339
|
draw.line([0, 112, 128, 0], fill=(255, 0, 0, 192), width=8)
|
|
310
340
|
pic.paste(draw_bg, mask=draw_bg)
|
|
311
341
|
self.lblPhoto[i].setToolTip("This picture was marked as “deleted” and may be overwritten when you take new pictures.")
|
|
312
|
-
self.CUR_THUMBS[i] = ImageQt(pic.resize((47, 41), Image.HAMMING))
|
|
342
|
+
self.CUR_THUMBS[i] = ImageQt(pic.resize((47, 41), Image.Resampling.HAMMING))
|
|
313
343
|
qpixmap = QtGui.QPixmap.fromImage(self.CUR_THUMBS[i])
|
|
314
344
|
self.lblPhoto[i].setPixmap(qpixmap)
|
|
315
345
|
|
|
316
346
|
def UpdateViewer(self, index):
|
|
317
|
-
resampler = Image.NEAREST
|
|
318
|
-
if self.CUR_BICUBIC: resampler = Image.BICUBIC
|
|
347
|
+
resampler = Image.Resampling.NEAREST
|
|
348
|
+
if self.CUR_BICUBIC: resampler = Image.Resampling.BICUBIC
|
|
319
349
|
cam = self.CUR_PC
|
|
320
350
|
if cam is None: return
|
|
321
351
|
|
|
@@ -323,7 +353,7 @@ class PocketCameraWindow(QtWidgets.QDialog):
|
|
|
323
353
|
self.lblPhoto[i].setStyleSheet("border-top: 1px solid #adadad; border-left: 1px solid #adadad; border-bottom: 1px solid #ffffff; border-right: 1px solid #ffffff;")
|
|
324
354
|
|
|
325
355
|
if index >= 30:
|
|
326
|
-
self.CUR_PIC = ImageQt(cam.GetPicture(index).convert("RGBA").resize((256, 224), Image.BICUBIC if index == 31 else resampler))
|
|
356
|
+
self.CUR_PIC = ImageQt(cam.GetPicture(index).convert("RGBA").resize((256, 224), Image.Resampling.BICUBIC if index == 31 else resampler))
|
|
327
357
|
else:
|
|
328
358
|
self.CUR_PIC = ImageQt(cam.GetPicture(index).convert("RGBA").resize((256, 224), resampler))
|
|
329
359
|
self.lblPhoto[index].setStyleSheet("border: 3px solid green; padding: 1px;")
|
FlashGBX/RomFileAGB.py
CHANGED
|
@@ -121,6 +121,7 @@ class RomFileAGB:
|
|
|
121
121
|
def GetHeader(self, unchanged=False):
|
|
122
122
|
buffer = bytearray(self.ROMFILE)
|
|
123
123
|
data = {}
|
|
124
|
+
if len(buffer) < 0x180: return {}
|
|
124
125
|
hash = hashlib.sha1(buffer[0:0x180]).digest()
|
|
125
126
|
nocart_hashes = []
|
|
126
127
|
nocart_hashes.append(bytearray([ 0x4F, 0xE9, 0x3E, 0xEE, 0xBC, 0x55, 0x93, 0xFE, 0x2E, 0x23, 0x1A, 0x39, 0x86, 0xCE, 0x86, 0xC9, 0x5C, 0x11, 0x00, 0xDD ])) # Method 0
|
|
@@ -187,7 +188,9 @@ class RomFileAGB:
|
|
|
187
188
|
(data["game_title"] == "CARDE READER" and data["game_code"] == "PSAE" and data["header_checksum"] == 0x95):
|
|
188
189
|
data["ereader"] = True
|
|
189
190
|
|
|
190
|
-
|
|
191
|
+
if unchanged:
|
|
192
|
+
data["unchanged"] = copy.copy(data)
|
|
193
|
+
|
|
191
194
|
self.DATA = data
|
|
192
195
|
data["db"] = self.GetDatabaseEntry()
|
|
193
196
|
|
FlashGBX/RomFileDMG.py
CHANGED
|
@@ -140,8 +140,9 @@ class RomFileDMG:
|
|
|
140
140
|
data["rom_checksum_calc"] = self.CalcChecksumGlobal()
|
|
141
141
|
data["rom_checksum_correct"] = data["rom_checksum"] == data["rom_checksum_calc"]
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
if unchanged:
|
|
144
|
+
data["unchanged"] = copy.copy(data)
|
|
145
|
+
else:
|
|
145
146
|
# MBC2
|
|
146
147
|
if data["mapper_raw"] == 0x06:
|
|
147
148
|
data["ram_size_raw"] = 0x100
|
|
@@ -431,6 +432,16 @@ class RomFileDMG:
|
|
|
431
432
|
temp = self.LogoToImage(buffer[0x104:0x104+0x30])
|
|
432
433
|
if temp is not False: data["logo_sachen"] = temp
|
|
433
434
|
|
|
435
|
+
# GBFlash MBCX
|
|
436
|
+
if data["game_title"] == "MBCX_MENU":
|
|
437
|
+
data["rom_size_raw"] = 0x0A
|
|
438
|
+
data["ram_size_raw"] = 0x03
|
|
439
|
+
data["mapper_raw"] = 0x206
|
|
440
|
+
|
|
441
|
+
# Photo!
|
|
442
|
+
if data["game_title"] == "PHOTO":
|
|
443
|
+
data["ram_size_raw"] = 0x204
|
|
444
|
+
|
|
434
445
|
if data["mapper_raw"] in Util.DMG_Header_Mapper:
|
|
435
446
|
data["mapper"] = Util.DMG_Header_Mapper[data["mapper_raw"]]
|
|
436
447
|
elif data["logo_correct"]:
|
FlashGBX/Util.py
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
# FlashGBX
|
|
3
3
|
# Author: Lesserkuma (github.com/lesserkuma)
|
|
4
4
|
|
|
5
|
-
import math, time, datetime, copy, configparser, threading, os, platform, traceback, io, struct, re, statistics, random
|
|
5
|
+
import math, time, datetime, copy, configparser, threading, os, platform, traceback, io, struct, re, statistics, random, sys
|
|
6
6
|
from io import StringIO
|
|
7
7
|
from enum import Enum
|
|
8
8
|
|
|
9
9
|
# Common constants
|
|
10
10
|
APPNAME = "FlashGBX"
|
|
11
|
-
VERSION_PEP440 = "4.
|
|
11
|
+
VERSION_PEP440 = "4.4"
|
|
12
12
|
VERSION = "v{:s}".format(VERSION_PEP440)
|
|
13
|
-
VERSION_TIMESTAMP =
|
|
13
|
+
VERSION_TIMESTAMP = 1748007939
|
|
14
14
|
DEBUG = False
|
|
15
15
|
DEBUG_LOG = []
|
|
16
16
|
APP_PATH = ""
|
|
@@ -23,14 +23,14 @@ AGB_Header_Save_Sizes = [ 0, 512, 8192, 32768, 65536, 131072, 1048576, 65536, 13
|
|
|
23
23
|
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS", 0xBF5B:"Unlicensed SST49LF080A", 0xFFFF:"Unlicensed 0xFFFF" }
|
|
24
24
|
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000 ]
|
|
25
25
|
|
|
26
|
-
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x0F:'MBC3+RTC+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x110:'MBC30+RTC+SRAM+BATTERY', 0x12:'MBC3+SRAM', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'MAC-GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper' }
|
|
27
|
-
DMG_Mapper_Types = { "None":[ 0x00, 0x08, 0x09 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x05, 0x06 ], "MBC3":[ 0x0F, 0x10, 0x11, 0x12, 0x13 ], "MBC30":[ 0x110 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "MAC-GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "Unlicensed 256M Multi Cart Mapper":[ 0x201 ], "Unlicensed Wisdom Tree Mapper":[ 0x202 ], "Unlicensed Xploder GB Mapper":[ 0x203 ], "Unlicensed Sachen Mapper":[ 0x204 ], "Unlicensed Datel Orbit V2 Mapper":[ 0x205 ] }
|
|
28
|
-
DMG_Header_ROM_Sizes = [ "32 KiB", "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB", "8 MiB", "16 MiB", "32 MiB" ]
|
|
29
|
-
DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A ]
|
|
30
|
-
DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000 ]
|
|
31
|
-
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "16K SRAM (2 KiB)", "64K SRAM (8 KiB)", "256K SRAM (32 KiB)", "512K SRAM (64 KiB)", "1M SRAM (128 KiB)", "MBC6 SRAM+FLASH (1.03 MiB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KiB)", "Unlicensed 1M EEPROM (128 KiB)" ]
|
|
32
|
-
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x100, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201, 0x203, 0x204 ]
|
|
33
|
-
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x800, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000, 0x20000, 0x80000 ] # RAM size in bytes
|
|
26
|
+
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x0F:'MBC3+RTC+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x110:'MBC30+RTC+SRAM+BATTERY', 0x12:'MBC3+SRAM', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'MAC-GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper', 0x206:'Unlicensed MBCX Mapper' }
|
|
27
|
+
DMG_Mapper_Types = { "None":[ 0x00, 0x08, 0x09 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x05, 0x06 ], "MBC3":[ 0x0F, 0x10, 0x11, 0x12, 0x13 ], "MBC30":[ 0x110 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "MAC-GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "Unlicensed 256M Multi Cart Mapper":[ 0x201 ], "Unlicensed Wisdom Tree Mapper":[ 0x202 ], "Unlicensed Xploder GB Mapper":[ 0x203 ], "Unlicensed Sachen Mapper":[ 0x204 ], "Unlicensed Datel Orbit V2 Mapper":[ 0x205 ], "Unlicensed MBCX Mapper":[ 0x206 ] }
|
|
28
|
+
DMG_Header_ROM_Sizes = [ "32 KiB", "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB", "8 MiB", "16 MiB", "32 MiB", "64 MiB", "128 MiB" ]
|
|
29
|
+
DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C ]
|
|
30
|
+
DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000 ]
|
|
31
|
+
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "16K SRAM (2 KiB)", "64K SRAM (8 KiB)", "256K SRAM (32 KiB)", "512K SRAM (64 KiB)", "1M SRAM (128 KiB)", "MBC6 SRAM+FLASH (1.03 MiB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KiB)", "Unlicensed 1M EEPROM (128 KiB)", "Unlicensed Photo! Directory (1 MiB)", "Unlicensed Batteryless SRAM" ]
|
|
32
|
+
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x100, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201, 0x203, 0x204, 0x205 ]
|
|
33
|
+
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x800, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000, 0x20000, 0x100000, 0x80000 ] # RAM size in bytes
|
|
34
34
|
DMG_Header_SGB = { 0x00:'No support', 0x03:'Supported' }
|
|
35
35
|
DMG_Header_CGB = { 0x00:'No support', 0x80:'Supported', 0xC0:'Required' }
|
|
36
36
|
|
|
@@ -169,6 +169,10 @@ class Progress():
|
|
|
169
169
|
self.PROGRESS["time_start"] = args["time_start"]
|
|
170
170
|
else:
|
|
171
171
|
self.PROGRESS["time_start"] = now
|
|
172
|
+
if "abortable" in args:
|
|
173
|
+
self.PROGRESS["abortable"] = args["abortable"]
|
|
174
|
+
else:
|
|
175
|
+
self.PROGRESS["abortable"] = True
|
|
172
176
|
self.PROGRESS["time_last_emit"] = now
|
|
173
177
|
self.PROGRESS["time_last_update_speed"] = now
|
|
174
178
|
self.PROGRESS["time_left"] = 0
|
|
@@ -218,7 +222,7 @@ class Progress():
|
|
|
218
222
|
self.PROGRESS["sector_pos"] = args["sector_pos"]
|
|
219
223
|
if "abortable" in args:
|
|
220
224
|
self.PROGRESS["abortable"] = args["abortable"]
|
|
221
|
-
|
|
225
|
+
|
|
222
226
|
if ((now - self.PROGRESS["time_last_emit"]) > 0.06) or "force_update" in args and args["force_update"] is True:
|
|
223
227
|
self.PROGRESS["time_elapsed"] = now - self.PROGRESS["time_start"]
|
|
224
228
|
time_delta = now - self.PROGRESS["time_last_update_speed"]
|
|
@@ -254,6 +258,11 @@ class Progress():
|
|
|
254
258
|
self.UPDATER(self.PROGRESS)
|
|
255
259
|
self.PROGRESS["time_last_emit"] = now
|
|
256
260
|
|
|
261
|
+
elif args["action"] == "UPDATE_INFO":
|
|
262
|
+
self.PROGRESS["text"] = args["text"]
|
|
263
|
+
self.PROGRESS["action"] = args["action"]
|
|
264
|
+
self.UPDATER(self.PROGRESS)
|
|
265
|
+
|
|
257
266
|
elif args["action"] == "FINISHED":
|
|
258
267
|
self.PROGRESS["pos"] = self.PROGRESS["size"]
|
|
259
268
|
self.UPDATER(self.PROGRESS)
|
|
@@ -401,8 +410,14 @@ def EncodeBCD(value):
|
|
|
401
410
|
def ParseCFI(buffer):
|
|
402
411
|
buffer = copy.copy(buffer)
|
|
403
412
|
info = {}
|
|
404
|
-
|
|
405
|
-
|
|
413
|
+
magic_8bit = "{:s}{:s}{:s}".format(chr(buffer[0x10]), chr(buffer[0x11]), chr(buffer[0x12]))
|
|
414
|
+
magic_16bit = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
|
|
415
|
+
buffer_conv = bytearray()
|
|
416
|
+
if magic_8bit == "QRY":
|
|
417
|
+
buffer_conv = bytearray(b for x in buffer for b in (x, x))
|
|
418
|
+
buffer = buffer_conv
|
|
419
|
+
|
|
420
|
+
if magic_8bit != "QRY" and magic_16bit != "QRY": # nothing swapped
|
|
406
421
|
return False
|
|
407
422
|
|
|
408
423
|
try:
|
|
@@ -513,7 +528,10 @@ def ConvertMapperTypeToMapper(mapper_type):
|
|
|
513
528
|
return 0
|
|
514
529
|
|
|
515
530
|
def GetDumpReport(di, device):
|
|
516
|
-
header = di["header"]
|
|
531
|
+
header = di["header"]
|
|
532
|
+
if "unchanged" in di["header"]:
|
|
533
|
+
header = di["header"]["unchanged"]
|
|
534
|
+
|
|
517
535
|
if "db" in di["header"]: header["db"] = di["header"]["db"]
|
|
518
536
|
if di["system"] == "DMG":
|
|
519
537
|
mode = "DMG"
|
|
@@ -535,6 +553,8 @@ def GetDumpReport(di, device):
|
|
|
535
553
|
di["rom_size"] = "{:s}".format(AGB_Header_ROM_Sizes[AGB_Header_ROM_Sizes_Map.index(di["rom_size"])])
|
|
536
554
|
else:
|
|
537
555
|
di["rom_size"] = "{:,} bytes".format(di["rom_size"])
|
|
556
|
+
else:
|
|
557
|
+
raise NotImplementedError
|
|
538
558
|
|
|
539
559
|
di["cart_type"] = list(device.SUPPORTED_CARTS[mode].keys())[di["cart_type"]]
|
|
540
560
|
if di["file_name"] is None:
|
|
@@ -795,7 +815,10 @@ def GetDumpReport(di, device):
|
|
|
795
815
|
if "st" in db: s += "* Save Type: {:s}\n".format(AGB_Header_Save_Types[db["st"]])
|
|
796
816
|
#if "ss" in db: s += "* Save Size: {:s}\n".format(formatFileSize(size=db["ss"], asInt=True))
|
|
797
817
|
|
|
798
|
-
|
|
818
|
+
if platform.system() == "Windows":
|
|
819
|
+
return s.replace("\n", "\r\n")
|
|
820
|
+
else:
|
|
821
|
+
return s
|
|
799
822
|
|
|
800
823
|
def GenerateFileName(mode, header, settings=None):
|
|
801
824
|
fe_ni = True
|
|
@@ -911,6 +934,19 @@ def find_size(data, max_size, min_size=0x20):
|
|
|
911
934
|
break
|
|
912
935
|
return offset
|
|
913
936
|
|
|
937
|
+
def bitmap2pixmap(data, scale_factor=4):
|
|
938
|
+
try:
|
|
939
|
+
from .pyside import QtGui
|
|
940
|
+
from PIL.ImageQt import ImageQt
|
|
941
|
+
from PIL import Image
|
|
942
|
+
data_converted = data.convert("RGBA")
|
|
943
|
+
pixmap = QtGui.QPixmap.fromImage(ImageQt(data_converted.resize((data_converted.width * scale_factor, data_converted.height * scale_factor), Image.NEAREST)))
|
|
944
|
+
pixmap.setDevicePixelRatio(scale_factor)
|
|
945
|
+
return pixmap
|
|
946
|
+
except:
|
|
947
|
+
print("Couldn’t convert bitmap to pixmap.")
|
|
948
|
+
return False
|
|
949
|
+
|
|
914
950
|
def dprint(*args, **kwargs):
|
|
915
951
|
stack = traceback.extract_stack()
|
|
916
952
|
stack = stack[len(stack)-2]
|
|
@@ -921,3 +957,37 @@ def dprint(*args, **kwargs):
|
|
|
921
957
|
if DEBUG:
|
|
922
958
|
msg = "{:s}{:s}".format(ANSI.CLEAR_LINE, msg)
|
|
923
959
|
print(msg)
|
|
960
|
+
|
|
961
|
+
def write_debug_log(device=False):
|
|
962
|
+
dprint("{:s} version: {:s} ({:d})".format(APPNAME, VERSION_PEP440, VERSION_TIMESTAMP))
|
|
963
|
+
dprint("Platform: {:s}".format(platform.platform()))
|
|
964
|
+
if device is not False:
|
|
965
|
+
if device is not None:
|
|
966
|
+
dprint("Connected device: {:s}".format(device))
|
|
967
|
+
else:
|
|
968
|
+
dprint("No device connected")
|
|
969
|
+
dprint("Now writing debug log file")
|
|
970
|
+
try:
|
|
971
|
+
fn = CONFIG_PATH + "/debug.log"
|
|
972
|
+
with open(fn, "wb") as f:
|
|
973
|
+
if platform.system() == "Windows":
|
|
974
|
+
f.write("\r\n".join(DEBUG_LOG).encode("UTF-8-SIG"))
|
|
975
|
+
else:
|
|
976
|
+
f.write("\n".join(DEBUG_LOG).encode("UTF-8-SIG"))
|
|
977
|
+
print("debug.log written")
|
|
978
|
+
return True
|
|
979
|
+
except:
|
|
980
|
+
return False
|
|
981
|
+
|
|
982
|
+
def exception_hook(exc_type, exc_value, exc_traceback):
|
|
983
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
984
|
+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
985
|
+
return
|
|
986
|
+
|
|
987
|
+
s = "⚠️ EXCEPTION OCCURED ⚠️\n"
|
|
988
|
+
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
|
|
989
|
+
for line in lines:
|
|
990
|
+
s += f"{line:s}"
|
|
991
|
+
print(s)
|
|
992
|
+
dprint(s)
|
|
993
|
+
write_debug_log()
|
FlashGBX/fw_GBFlash.py
CHANGED
|
@@ -230,11 +230,6 @@ try:
|
|
|
230
230
|
self.DEVICE = device
|
|
231
231
|
else:
|
|
232
232
|
self.APP.QT_APP.processEvents()
|
|
233
|
-
text = "This Firmware Updater is for GBFlash devices only. Please only proceed if your device is a GBFlash."
|
|
234
|
-
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
|
235
|
-
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
|
236
|
-
answer = msgbox.exec()
|
|
237
|
-
if answer == QtWidgets.QMessageBox.Cancel: return
|
|
238
233
|
self.FWUPD = FirmwareUpdater(app_path, None)
|
|
239
234
|
|
|
240
235
|
self.layout = QtWidgets.QGridLayout()
|
FlashGBX/fw_GBxCartRW_v1_4.py
CHANGED
|
@@ -110,11 +110,6 @@ try:
|
|
|
110
110
|
self.DEVICE = device
|
|
111
111
|
else:
|
|
112
112
|
self.APP.QT_APP.processEvents()
|
|
113
|
-
text = "This Firmware Updater is for insideGadgets GBxCart RW v1.4 devices only. Please only proceed if your device matches this hardware revision.\n\nOlder GBxCart RW revisions can be updated only after connecting to them first."
|
|
114
|
-
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
|
115
|
-
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
|
116
|
-
answer = msgbox.exec()
|
|
117
|
-
if answer == QtWidgets.QMessageBox.Cancel: return
|
|
118
113
|
self.FWUPD = FirmwareUpdater(app_path, None)
|
|
119
114
|
|
|
120
115
|
self.layout = QtWidgets.QGridLayout()
|
|
@@ -217,8 +212,10 @@ try:
|
|
|
217
212
|
self.lblDeviceFWVerResult.setText(self.FW_VER)
|
|
218
213
|
if self.PCB_VER == "v1.4":
|
|
219
214
|
self.optDevicePCBVer14.setChecked(True)
|
|
215
|
+
self.optDevicePCBVer14a.setEnabled(False)
|
|
220
216
|
elif self.PCB_VER == "v1.4a/b/c":
|
|
221
217
|
self.optDevicePCBVer14a.setChecked(True)
|
|
218
|
+
self.optDevicePCBVer14.setEnabled(False)
|
|
222
219
|
self.SetPCBVersion()
|
|
223
220
|
|
|
224
221
|
def SetPCBVersion(self):
|
FlashGBX/fw_JoeyJr.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Author: Lesserkuma (github.com/lesserkuma)
|
|
4
4
|
|
|
5
5
|
import zipfile, time, os, struct, serial, platform
|
|
6
|
+
from serial import SerialException
|
|
6
7
|
try:
|
|
7
8
|
from . import Util
|
|
8
9
|
except ImportError:
|
|
@@ -30,7 +31,9 @@ class FirmwareUpdater():
|
|
|
30
31
|
path = os.path.dirname(path) + "/"
|
|
31
32
|
fncSetStatus(text="Connecting... This may take a moment.")
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
filename = os.path.split(file)[1]
|
|
35
|
+
filepath = os.path.split(file)[0]
|
|
36
|
+
with open(filepath + "/" + filename, "rb") as f: temp = f.read().decode("UTF-8", "ignore")
|
|
34
37
|
if not temp.startswith("UPDATE"):
|
|
35
38
|
with open(file, "wb") as f:
|
|
36
39
|
temp = bytearray(b"UPDATE")
|
|
@@ -46,10 +49,16 @@ class FirmwareUpdater():
|
|
|
46
49
|
return 2
|
|
47
50
|
|
|
48
51
|
try:
|
|
49
|
-
with open(
|
|
50
|
-
except FileNotFoundError:
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
with open(filepath + "/" + filename, "rb") as f: temp = f.read().decode("UTF-8", "ignore")
|
|
53
|
+
except FileNotFoundError as e:
|
|
54
|
+
try:
|
|
55
|
+
if filename == "MODE.TXT":
|
|
56
|
+
with open(filepath + "/" + "MODE!.TXT", "rb") as f: temp = f.read().decode("UTF-8", "ignore")
|
|
57
|
+
else:
|
|
58
|
+
raise FileNotFoundError from e
|
|
59
|
+
except FileNotFoundError:
|
|
60
|
+
fncSetStatus(text="Couldn’t access MODE.TXT. Remove cartridge and try again.")
|
|
61
|
+
return 2
|
|
53
62
|
|
|
54
63
|
if not temp.startswith("UPDATE"):
|
|
55
64
|
fncSetStatus(text="Couldn’t enter UPDATE mode, please try again.")
|
|
@@ -112,8 +121,14 @@ class FirmwareUpdater():
|
|
|
112
121
|
fncSetStatus(text="Connecting...")
|
|
113
122
|
try:
|
|
114
123
|
dev = serial.Serial(port, 2000000, timeout=0.2)
|
|
124
|
+
except SerialException as e:
|
|
125
|
+
if "Errno 13" in str(e) and platform.system() == "Linux":
|
|
126
|
+
fncSetStatus(text="No permission to use device! See README file.", enableUI=True)
|
|
127
|
+
else:
|
|
128
|
+
fncSetStatus(text="Device not accessible.", enableUI=True)
|
|
129
|
+
return 2
|
|
115
130
|
except:
|
|
116
|
-
fncSetStatus(text="
|
|
131
|
+
fncSetStatus(text="Unknown error while accessing the device.", enableUI=True)
|
|
117
132
|
return 2
|
|
118
133
|
dev.reset_input_buffer()
|
|
119
134
|
|
|
@@ -272,7 +287,7 @@ try:
|
|
|
272
287
|
self.rowUpdate.addStretch()
|
|
273
288
|
|
|
274
289
|
self.rowUpdate2 = QtWidgets.QHBoxLayout()
|
|
275
|
-
self.lblUpdateDisclaimer = QtWidgets.QLabel("Please note that FlashGBX is not officially supported by BennVenn
|
|
290
|
+
self.lblUpdateDisclaimer = QtWidgets.QLabel("Please note that FlashGBX is not officially supported by BennVenn.")
|
|
276
291
|
self.lblUpdateDisclaimer.setWordWrap(True)
|
|
277
292
|
self.lblUpdateDisclaimer.setAlignment(QtGui.Qt.AlignmentFlag.AlignCenter)
|
|
278
293
|
self.rowUpdate2.addWidget(self.lblUpdateDisclaimer)
|
|
@@ -467,7 +482,7 @@ try:
|
|
|
467
482
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
|
468
483
|
answer = msgbox.exec()
|
|
469
484
|
return False
|
|
470
|
-
answer = QtWidgets.QMessageBox.information(self, "FlashGBX", "If your Joey Jr device is currently running the Drag'n'Drop firmware, please continue and choose its <b>MODE.TXT</b> file.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok)
|
|
485
|
+
answer = QtWidgets.QMessageBox.information(self, "FlashGBX", "If your Joey Jr device is currently running the Drag'n'Drop firmware, please continue and choose its <b>MODE.TXT</b> (or <b>MODE!.TXT</b>) file.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok)
|
|
471
486
|
if answer == QtWidgets.QMessageBox.Cancel:
|
|
472
487
|
self.SetStatus("No device found.", enableUI=True)
|
|
473
488
|
return False
|
FlashGBX/hw_GBFlash.py
CHANGED
|
@@ -9,7 +9,7 @@ class GbxDevice(LK_Device):
|
|
|
9
9
|
DEVICE_NAME = "GBFlash"
|
|
10
10
|
DEVICE_MIN_FW = 1
|
|
11
11
|
DEVICE_MAX_FW = 12
|
|
12
|
-
DEVICE_LATEST_FW_TS = { 5:
|
|
12
|
+
DEVICE_LATEST_FW_TS = { 5:1747991884, 10:1747991884, 11:1747991884, 12:1747991884, 13:1747991884 }
|
|
13
13
|
PCB_VERSIONS = { 5:'', 12:'v1.2', 13:'v1.3' }
|
|
14
14
|
|
|
15
15
|
def __init__(self):
|
|
@@ -111,8 +111,10 @@ class GbxDevice(LK_Device):
|
|
|
111
111
|
self.FW["cart_power_ctrl"] = True if self._read(1) == 1 else False
|
|
112
112
|
|
|
113
113
|
# Reset to bootloader support
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
temp = self._read(1)
|
|
115
|
+
self.FW["bootloader_reset"] = True if temp & 1 == 1 else False
|
|
116
|
+
self.FW["unregistered"] = True if temp >> 7 == 1 else False
|
|
117
|
+
|
|
116
118
|
return True
|
|
117
119
|
|
|
118
120
|
except Exception as e:
|
|
@@ -192,10 +194,10 @@ class GbxDevice(LK_Device):
|
|
|
192
194
|
return True
|
|
193
195
|
|
|
194
196
|
def FirmwareUpdateAvailable(self):
|
|
195
|
-
if self.FW["pcb_ver"] == 5: # unofficial firmware
|
|
197
|
+
if self.FW["pcb_ver"] == 5 or self.FW["fw_ts"] < 1730592000: # unofficial firmware
|
|
196
198
|
self.FW_UPDATE_REQ = True
|
|
197
199
|
return True
|
|
198
|
-
if self.FW["fw_ts"]
|
|
200
|
+
if self.FW["fw_ts"] != self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
|
|
199
201
|
return True
|
|
200
202
|
self.FW_UPDATE_REQ = False
|
|
201
203
|
return False
|
|
@@ -239,6 +241,13 @@ class GbxDevice(LK_Device):
|
|
|
239
241
|
|
|
240
242
|
def GetFullName(self):
|
|
241
243
|
if self.FW["pcb_ver"] < 13 and self.CanPowerCycleCart():
|
|
242
|
-
|
|
244
|
+
s = "{:s} {:s} + PLUGIN 01".format(self.GetName(), self.GetPCBVersion())
|
|
243
245
|
else:
|
|
244
|
-
|
|
246
|
+
s = "{:s} {:s}".format(self.GetName(), self.GetPCBVersion())
|
|
247
|
+
if self.IsUnregistered():
|
|
248
|
+
s += " (unregistered)"
|
|
249
|
+
return s
|
|
250
|
+
|
|
251
|
+
def GetRegisterInformation(self):
|
|
252
|
+
text = f"Your GBFlash device reported a registration error, which means it may be an illegitimate clone.\n\nThe device’s integrated piracy detection may limit the device in performance and functionality until proper registration. The {Util.APPNAME:s} software has no control over this.\n\nPlease visit <a href=\"https://gbflash.geeksimon.com/\">https://gbflash.geeksimon.com/</a> for more information.".replace("\n", "<br>")
|
|
253
|
+
return text
|