FlashGBX 4.3__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/Mapper.py CHANGED
@@ -1647,7 +1647,7 @@ class AGB_GPIO:
1647
1647
  temp = self.CartRead(self.GPIO_REG_DAT) & 0xFF
1648
1648
  bit = (temp & 2) >> 1
1649
1649
  data = (data >> 1) | (bit << 7)
1650
- # print("RTCReadData(): i={:d}/temp={:X}/bit={:x}/data={:x}".format(i, temp, bit, data))
1650
+ # dprint("RTCReadData(): i={:d}/temp={:X}/bit={:x}/data={:x}".format(i, temp, bit, data))
1651
1651
  return data
1652
1652
 
1653
1653
  def RTCWriteData(self, data):
@@ -1703,7 +1703,8 @@ class AGB_GPIO:
1703
1703
  status = self.RTCReadStatus()
1704
1704
  else:
1705
1705
  status = buffer[0]
1706
-
1706
+
1707
+ dprint("Status:", bin(status))
1707
1708
  if (status >> 7) == 1:
1708
1709
  dprint("No RTC because of set RTC Status Register Power Flag:", status >> 7 & 1)
1709
1710
  return 1
@@ -1723,7 +1724,7 @@ class AGB_GPIO:
1723
1724
  else:
1724
1725
  rom2 = buffer[1:7]
1725
1726
 
1726
- 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))
1727
1728
  if (rom1 == rom2):
1728
1729
  dprint("No RTC because ROM data didn’t change:", rom1, rom2)
1729
1730
  return 3
@@ -1908,7 +1909,6 @@ class AGB_GPIO:
1908
1909
  }
1909
1910
 
1910
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:
1911
- #raise ValueError("Invalid RTC data")
1912
1912
  d["string"] = "Invalid RTC data"
1913
1913
  d["rtc_valid"] = False
1914
1914
  else:
@@ -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):
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
- data["unchanged"] = copy.copy(data)
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
- data["unchanged"] = copy.copy(data)
144
- if not unchanged:
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
@@ -437,6 +438,10 @@ class RomFileDMG:
437
438
  data["ram_size_raw"] = 0x03
438
439
  data["mapper_raw"] = 0x206
439
440
 
441
+ # Photo!
442
+ if data["game_title"] == "PHOTO":
443
+ data["ram_size_raw"] = 0x204
444
+
440
445
  if data["mapper_raw"] in Util.DMG_Header_Mapper:
441
446
  data["mapper"] = Util.DMG_Header_Mapper[data["mapper_raw"]]
442
447
  elif data["logo_correct"]:
FlashGBX/Util.py CHANGED
@@ -8,9 +8,9 @@ from enum import Enum
8
8
 
9
9
  # Common constants
10
10
  APPNAME = "FlashGBX"
11
- VERSION_PEP440 = "4.3"
11
+ VERSION_PEP440 = "4.4"
12
12
  VERSION = "v{:s}".format(VERSION_PEP440)
13
- VERSION_TIMESTAMP = 1731015769
13
+ VERSION_TIMESTAMP = 1748007939
14
14
  DEBUG = False
15
15
  DEBUG_LOG = []
16
16
  APP_PATH = ""
@@ -28,9 +28,9 @@ DMG_Mapper_Types = { "None":[ 0x00, 0x08, 0x09 ], "MBC1":[ 0x01, 0x02, 0x03 ], "
28
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
29
  DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C ]
30
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)" ]
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
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
 
@@ -410,8 +410,14 @@ def EncodeBCD(value):
410
410
  def ParseCFI(buffer):
411
411
  buffer = copy.copy(buffer)
412
412
  info = {}
413
- magic = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
414
- if magic != "QRY": # nothing swapped
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
415
421
  return False
416
422
 
417
423
  try:
@@ -522,7 +528,10 @@ def ConvertMapperTypeToMapper(mapper_type):
522
528
  return 0
523
529
 
524
530
  def GetDumpReport(di, device):
525
- header = di["header"]["unchanged"]
531
+ header = di["header"]
532
+ if "unchanged" in di["header"]:
533
+ header = di["header"]["unchanged"]
534
+
526
535
  if "db" in di["header"]: header["db"] = di["header"]["db"]
527
536
  if di["system"] == "DMG":
528
537
  mode = "DMG"
@@ -925,6 +934,19 @@ def find_size(data, max_size, min_size=0x20):
925
934
  break
926
935
  return offset
927
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
+
928
950
  def dprint(*args, **kwargs):
929
951
  stack = traceback.extract_stack()
930
952
  stack = stack[len(stack)-2]
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:
@@ -120,8 +121,14 @@ class FirmwareUpdater():
120
121
  fncSetStatus(text="Connecting...")
121
122
  try:
122
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
123
130
  except:
124
- fncSetStatus(text="Device not accessible.", enableUI=True)
131
+ fncSetStatus(text="Unknown error while accessing the device.", enableUI=True)
125
132
  return 2
126
133
  dev.reset_input_buffer()
127
134
 
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:1730731680, 10:1730731680, 11:1730731680, 12:1730731680, 13:1730731680 }
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):
@@ -197,7 +197,7 @@ class GbxDevice(LK_Device):
197
197
  if self.FW["pcb_ver"] == 5 or self.FW["fw_ts"] < 1730592000: # unofficial firmware
198
198
  self.FW_UPDATE_REQ = True
199
199
  return True
200
- if self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
200
+ if self.FW["fw_ts"] != self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
201
201
  return True
202
202
  self.FW_UPDATE_REQ = False
203
203
  return False
FlashGBX/hw_GBxCartRW.py CHANGED
@@ -9,13 +9,13 @@ class GbxDevice(LK_Device):
9
9
  DEVICE_NAME = "GBxCart RW"
10
10
  DEVICE_MIN_FW = 1
11
11
  DEVICE_MAX_FW = 1
12
- DEVICE_LATEST_FW_TS = { 4:1709317610, 5:1722774120, 6:1722774120, 2:0, 90:0, 100:0 }
12
+ DEVICE_LATEST_FW_TS = { 4:1709317610, 5:1747991884, 6:1747991884, 2:0, 90:0, 100:0 }
13
13
  PCB_VERSIONS = { 5:'v1.4', 6:'v1.4a/b/c', 2:'v1.1/v1.2', 4:'v1.3', 90:'XMAS v1.0', 100:'Mini v1.0' }
14
14
  BAUDRATE = 1000000
15
15
  MAX_BUFFER_READ = 0x1000
16
16
  MAX_BUFFER_WRITE = 0x400
17
17
 
18
- def Initialize(self, flashcarts=None, port=None, max_baud=2000000):
18
+ def Initialize(self, flashcarts=None, port=None, max_baud=1500000):
19
19
  if self.IsConnected(): self.DEVICE.close()
20
20
  if platform.system() == "Darwin": max_baud = 1000000
21
21
 
@@ -31,7 +31,7 @@ class GbxDevice(LK_Device):
31
31
  if len(ports) == 0: return False
32
32
 
33
33
  for i in range(0, len(ports)):
34
- for baudrate in (1000000, 1700000, 2000000):
34
+ for baudrate in (1000000, 1500000):
35
35
  if max_baud < baudrate: continue
36
36
  try:
37
37
  if self.TryConnect(ports[i], baudrate):
@@ -49,8 +49,8 @@ class GbxDevice(LK_Device):
49
49
  continue
50
50
 
51
51
  if self.FW is None or self.FW == {}: continue
52
- if max_baud >= 1700000 and self.FW is not None and "pcb_ver" in self.FW and self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE < 1700000:
53
- self.ChangeBaudRate(baudrate=1700000)
52
+ if max_baud >= 1500000 and self.FW is not None and "pcb_ver" in self.FW and self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE < 1500000:
53
+ self.ChangeBaudRate(baudrate=1500000)
54
54
  self.DEVICE.close()
55
55
  dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1)
56
56
  self.DEVICE = dev
@@ -74,8 +74,8 @@ class GbxDevice(LK_Device):
74
74
  self.MAX_BUFFER_WRITE = 0x100
75
75
 
76
76
  conn_msg.append([0, "For help with your GBxCart RW device, please visit the insideGadgets Discord: https://gbxcart.com/discord"])
77
- if self.FW["pcb_ver"] == 4:
78
- conn_msg.append([0, "Note: Your GBxCart RW hardware revision does not fully support the latest features due to technical limitations. Please consider upgrading to a newer device."])
77
+ #if self.FW["pcb_ver"] == 4:
78
+ # conn_msg.append([0, "Note: Your GBxCart RW hardware revision does not fully support the latest features due to technical limitations. Please consider upgrading to a newer device."])
79
79
 
80
80
  self.PORT = ports[i]
81
81
  self.DEVICE.timeout = self.DEVICE_TIMEOUT
@@ -173,8 +173,8 @@ class GbxDevice(LK_Device):
173
173
  def ChangeBaudRate(self, baudrate):
174
174
  if not self.IsConnected(): return
175
175
  dprint("Changing baud rate to", baudrate)
176
- if baudrate == 1700000:
177
- self._write(self.DEVICE_CMD["OFW_USART_1_7M_SPEED"])
176
+ if baudrate == 1500000:
177
+ self._write(self.DEVICE_CMD["OFW_USART_1_5M_SPEED"])
178
178
  elif baudrate == 1000000:
179
179
  self._write(self.DEVICE_CMD["OFW_USART_1_0M_SPEED"])
180
180
  self.BAUDRATE = baudrate
@@ -253,10 +253,10 @@ class GbxDevice(LK_Device):
253
253
  return ["DMG"]
254
254
  else:
255
255
  return ["DMG", "AGB"]
256
-
256
+
257
257
  def IsSupported3dMemory(self):
258
258
  return True
259
-
259
+
260
260
  def IsClkConnected(self):
261
261
  return self.FW["pcb_ver"] in (5, 6, 101)
262
262
 
@@ -270,7 +270,7 @@ class GbxDevice(LK_Device):
270
270
  dprint("LinkNLoad detected:", is_lnl)
271
271
  if is_lnl: return False
272
272
  return self.FW["pcb_ver"] in (2, 4, 5, 6, 90, 100, 101)
273
-
273
+
274
274
  def FirmwareUpdateAvailable(self):
275
275
  if self.FW["fw_ver"] == 0 and self.FW["pcb_ver"] in (2, 4, 90, 100, 101):
276
276
  if self.FW["pcb_ver"] == 4:
@@ -279,12 +279,12 @@ class GbxDevice(LK_Device):
279
279
  self.FW_UPDATE_REQ = 2
280
280
  return True
281
281
  if self.FW["pcb_ver"] not in (4, 5, 6): return False
282
- if self.FW["pcb_ver"] in (5, 6) and self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
282
+ if self.FW["pcb_ver"] in (5, 6) and self.FW["fw_ts"] != self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
283
283
  return True
284
- if self.FW["pcb_ver"] == 4 and self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
284
+ if self.FW["pcb_ver"] == 4 and self.FW["fw_ts"] != self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
285
285
  self.FW_UPDATE_REQ = True
286
286
  return True
287
-
287
+
288
288
  def GetFirmwareUpdaterClass(self):
289
289
  if self is None or self.FW["pcb_ver"] in (5, 6): # v1.4 / v1.4a/b/c
290
290
  try:
FlashGBX/hw_JoeyJr.py CHANGED
@@ -9,7 +9,7 @@ class GbxDevice(LK_Device):
9
9
  DEVICE_NAME = "Joey Jr"
10
10
  DEVICE_MIN_FW = 1
11
11
  DEVICE_MAX_FW = 12
12
- DEVICE_LATEST_FW_TS = 1722774120
12
+ DEVICE_LATEST_FW_TS = 1747991884
13
13
  PCB_VERSIONS = { -1:"", 0x01:"V2", 0x81:"V2", 0x02:"V2C", 0x82:"V2C", 0x03:"V2CC", 0x83:"V2CC/V2++" }
14
14
 
15
15
  def __init__(self):
@@ -256,7 +256,7 @@ class GbxDevice(LK_Device):
256
256
  if self.FW["cfw_id"] == "G":
257
257
  self.FW_UPDATE_REQ = True
258
258
  return True
259
- if self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS:
259
+ if self.FW["fw_ts"] != self.DEVICE_LATEST_FW_TS:
260
260
  return True
261
261
  self.FW_UPDATE_REQ = False
262
262
  return False
FlashGBX/res/config.zip CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: FlashGBX
3
- Version: 4.3
3
+ Version: 4.4
4
4
  Summary: Reads and writes Game Boy and Game Boy Advance cartridge data
5
5
  Home-page: https://github.com/lesserkuma/FlashGBX
6
6
  Author: Lesserkuma
@@ -17,15 +17,25 @@ Classifier: Intended Audience :: Developers
17
17
  Requires-Python: >=3.7
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: pyserial >=3.5
20
+ Requires-Dist: pyserial>=3.5
21
21
  Requires-Dist: Pillow
22
22
  Requires-Dist: setuptools
23
23
  Requires-Dist: requests
24
24
  Requires-Dist: python-dateutil
25
25
  Provides-Extra: qt5
26
- Requires-Dist: PySide2 >=5.14 ; extra == 'qt5'
26
+ Requires-Dist: PySide2>=5.14; extra == "qt5"
27
27
  Provides-Extra: qt6
28
- Requires-Dist: PySide6 ; extra == 'qt6'
28
+ Requires-Dist: PySide6; extra == "qt6"
29
+ Dynamic: author
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: project-url
35
+ Dynamic: provides-extra
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
29
39
 
30
40
  # FlashGBX (by Lesserkuma)
31
41
 
@@ -51,47 +61,6 @@ for Windows, Linux, macOS (→ [Download](https://github.com/lesserkuma/FlashGBX
51
61
  - [GBFlash](https://github.com/simonkwng/GBFlash) (tested with v1.2 and v1.3)
52
62
  - [Joey Jr](https://bennvenn.myshopify.com/collections/game-cart-to-pc-interface/products/usb-gb-c-cart-dumper-the-joey-jr) (tested with V2++)
53
63
 
54
- ## Installing and running
55
-
56
- ### Pre-compiled binaries and packages
57
-
58
- Available in the GitHub [Releases](https://github.com/lesserkuma/FlashGBX/releases) section are pre-compiled downloads available for:
59
-
60
- * **Windows (64-bit)**
61
- * Setup: An installer that will add the application to the start menu and optionally create a desktop icon
62
- * Portable: Have everything in one place including the config files
63
-
64
- *(For users of Windows 7, legacy “Qt5” versions are provided as well.)*
65
-
66
- * **Linux**
67
- * Ubuntu (.deb file): Install using `dpkg -i /path/to/FlashGBX_x.x_Ubuntu-all.deb`.<br>*(Based on a contribution by [JJ-Fox](https://github.com/JJ-Fox))*
68
- * Other distributions: Pre-made packages are contributed by JJ-Fox [here](https://github.com/JJ-Fox/FlashGBX-Linux-builds/releases/latest).
69
-
70
- * **macOS**
71
- * x86-64/arm64 (.dmg file): Install by opening the .dmg file and copying over the “FlashGBX” application to the desktop.<br>If it doesn’t run, it probably got quarantined during download. Run the following command in a Terminal window to unquarantine it: `xattr -d com.apple.quarantine /path/to/FlashGBX.app`.<br>*(Based on a contribution by [Cliffback](https://github.com/Cliffback))*
72
-
73
- *(If you have a Joey Jr and use macOS, please run the [Joey Jr Firmware Updater](https://github.com/lesserkuma/JoeyJr_FWUpdater) before using FlashGBX.)*
74
-
75
- ### Run via Python
76
-
77
- FlashGBX can also be run in a Python environment like so:
78
-
79
- 1. Download and install [Python](https://www.python.org/downloads/) (version 3.10.11 is recommended)
80
- 2. Open a Terminal or Command Prompt window
81
- 3. Install FlashGBX with this command:<br>`pip3 install "FlashGBX[qt6]"`
82
- * If installation fails, try this command instead:<br>`pip3 install "FlashGBX[qt5]"`
83
- * If installation still fails, you can install the minimal version (command line interface) with this command:<br>`pip3 install FlashGBX`
84
- * Update to the latest version by replacing `install` with `install -U`.
85
-
86
- #### Running
87
- Use this command in a Terminal or Command Prompt window to launch the installed FlashGBX application:
88
-
89
- `python3 -m FlashGBX`
90
-
91
- *FlashGBX should work on pretty much any operating system that supports Qt-GUI applications built using [Python](https://www.python.org/downloads/) with [PySide2](https://pypi.org/project/PySide2/) or [PySide6](https://pypi.org/project/PySide6/), [pyserial](https://pypi.org/project/pyserial/), [Pillow](https://pypi.org/project/Pillow/), [setuptools](https://pypi.org/project/setuptools/), [requests](https://pypi.org/project/requests/) and [python-dateutil](https://pypi.org/project/python-dateutil/) packages. To run FlashGBX in portable mode without installing, you can also download the source code archive and call `python3 run.py` after installing the prerequisites yourself.*
92
-
93
- *Note: On Linux systems, the `brltty` module may render serial communication devices non-accessible. See the troubleshooting section for details.*
94
-
95
64
  ## Cartridge Compatibility
96
65
  ### Supported cartridge memory mappers
97
66
  - Game Boy
@@ -131,7 +100,8 @@ Use this command in a Terminal or Command Prompt window to launch the installed
131
100
 
132
101
  - 29LV Series Flash BOY with 29LV160DB
133
102
  - Action Replay
134
- - BennVenn MBC3000 RTC cart
103
+ - BennVenn MBC3000 v4 RTC cart
104
+ - BennVenn MBC3000 v5 RTC cart
135
105
  - BLAZE Xploder GB¹
136
106
  - BUNG Doctor GB Card 4M
137
107
  - BUNG Doctor GB Card 16M
@@ -151,10 +121,12 @@ Use this command in a Terminal or Command Prompt window to launch the installed
151
121
  - DIY cart with MX29LV640
152
122
  - DIY cart with SST39SF040
153
123
  - DMG-MBC5-32M-FLASH (G/A) Development Cartridge, E201264
124
+ - DMG-MBC5-32M-FLASH (G/A-I) Development Cartridge, E201264
154
125
  - Ferrante Crafts cart 32 KB
155
126
  - Ferrante Crafts cart 64 KB
156
127
  - Ferrante Crafts cart 512 KB
157
128
  - FunnyPlaying MidnightTrace 4 MiB Flash Cart
129
+ - Gamebank-web DMG-29W-04 with M29W320EB
158
130
  - Gamebank-web DMG-29W-04 with M29W320ET
159
131
  - GameShark Pro
160
132
  - GB-CART32K-A with SST39SF020A
@@ -174,6 +146,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
174
146
  - insideGadgets 4 MiB, 32 KiB FRAM, MBC3+RTC
175
147
  - insideGadgets 4 MiB (2× 2 MiB), 32 KiB FRAM, MBC5
176
148
  - insideGadgets MegaDuck 32K
149
+ - ModRetro Chromatic Cartridge with 39VF1681
177
150
  - Mr Flash 64M
178
151
  - Sillyhatday MBC5-DUAL-FLASH-4/8MB
179
152
  - Squareboi 4 MiB (2× 2 MiB)
@@ -220,8 +193,8 @@ Use this command in a Terminal or Command Prompt window to launch the installed
220
193
  - DRV with 29LV320DB and ALTERA CPLD
221
194
  - DRV with AM29LV160DB and ALTERA CPLD
222
195
  - DRV with AM29LV160DT and ALTERA CPLD
223
- - DVP DRV with MX29LV320CB
224
- - DVP DRV with MX29LV320CT
196
+ - DVP DRV with MX29LV320CB¹
197
+ - DVP DRV with MX29LV320CT¹
225
198
  - ES29LV160_DRV with 29DL32TF-70
226
199
  - GB-M968 with 29LV160DB
227
200
  - GB-M968 with M29W160EB
@@ -349,6 +322,68 @@ Many different reproduction cartridges share their flash chip command set, so ev
349
322
 
350
323
  *¹ = Cannot always be auto-detected, select cartridge type manually*
351
324
 
325
+ ## Installing and running
326
+
327
+ ### Pre-compiled binaries and packages
328
+
329
+ Available in the GitHub [Releases](https://github.com/lesserkuma/FlashGBX/releases) section are pre-compiled downloads available for:
330
+
331
+ * **Windows (64-bit)** *(Windows 8 or newer)*
332
+ * Setup: An installer that will add the application to the start menu and optionally create a desktop icon
333
+ * Portable: Have everything in one place including the config files
334
+
335
+ * **Linux**
336
+ * Pre-made packages are contributed by JJ-Fox [here](https://github.com/JJ-Fox/FlashGBX-Linux-builds/releases/latest).
337
+
338
+ * **macOS** *(Monterey 12 or newer)*
339
+ * x86-64/arm64 (.dmg file): Install by opening the .dmg file and copying over the “FlashGBX” application to the desktop.<br>If it doesn’t run, it probably got quarantined during download. Run the following command in a Terminal window to unquarantine it: `xattr -d com.apple.quarantine /path/to/FlashGBX.app`.<br>*(Based on a contribution by [Cliffback](https://github.com/Cliffback))*
340
+
341
+ *(If you have a Joey Jr and use macOS, please run the [Joey Jr Firmware Updater](https://github.com/lesserkuma/JoeyJr_FWUpdater) before using FlashGBX.)*
342
+
343
+ ### Run via Python
344
+
345
+ FlashGBX can also be run in a Python environment like so:
346
+
347
+ 1. Download and install [Python](https://www.python.org/downloads/) (version 3.10.11 is recommended)
348
+ 2. Open a Terminal or Command Prompt window
349
+ 3. Install FlashGBX with this command:<br>`pip3 install "FlashGBX[qt6]"`
350
+ * If installation fails, try this command instead:<br>`pip3 install "FlashGBX[qt5]"`
351
+ * If installation still fails, you can install the minimal version (command line interface) with this command:<br>`pip3 install FlashGBX`
352
+ * Update to the latest version by replacing `install` with `install -U`.
353
+
354
+ #### Running
355
+ Use this command in a Terminal or Command Prompt window to launch the installed FlashGBX application:
356
+
357
+ `python3 -m FlashGBX`
358
+
359
+ *FlashGBX should work on pretty much any operating system that supports Qt-GUI applications built using [Python](https://www.python.org/downloads/) with [PySide2](https://pypi.org/project/PySide2/) or [PySide6](https://pypi.org/project/PySide6/), [pyserial](https://pypi.org/project/pyserial/), [Pillow](https://pypi.org/project/Pillow/), [setuptools](https://pypi.org/project/setuptools/), [requests](https://pypi.org/project/requests/) and [python-dateutil](https://pypi.org/project/python-dateutil/) packages. To run FlashGBX in portable mode without installing, you can also download the source code archive and call `python3 run.py` after installing the prerequisites yourself.*
360
+
361
+ *Note: When using GBxCart RW or GBFlash on some Linux systems, the `brltty` module may render serial communication devices non-accessible. See the troubleshooting section for details.*
362
+
363
+ ### Steam Deck
364
+ #### Installation
365
+ 1. Boot your Steam Deck in Desktop Mode.
366
+ 2. Open System Settings → Users.
367
+ 3. Set a password for your user account (“deck”), if you do not have one set yet. (Make sure you do not forget this password in the future.)
368
+ 4. Create a new folder where you want to install FlashGBX.
369
+ 5. Right click the folder and select “Open Terminal Here”.
370
+ 6. Enter the following commands in order:<br>
371
+ `python3 -m venv FlashGBX_venv`<br>
372
+ `source FlashGBX_venv/bin/activate`<br>
373
+ `wget https://bootstrap.pypa.io/get-pip.py`<br>
374
+ `python3 get-pip.py`<br>
375
+ `python3 -m pip install "FlashGBX[qt6]"`<br>
376
+ `deactivate`<br>
377
+ `sudo usermod -a -G uucp $USER`<br>
378
+ (You may need to enter the password you set earlier.)
379
+ 7. Restart your Steam Deck.
380
+
381
+ #### Running
382
+ 1. In Desktop Mode, right click your FlashGBX folder and select “Open Terminal Here”.
383
+ 2. Use these commands to launch the installed FlashGBX application:<br>
384
+ `source FlashGBX_venv/bin/activate`<br>
385
+ `python3 -m FlashGBX`<br>
386
+
352
387
  ### Troubleshooting
353
388
 
354
389
  * If something doesn’t work as expected, first try to clean the game cartridge contacts (best with IPA 99.9%+ on a cotton swab) and reconnect the device. An unstable cartridge connection is the most common reason for read or write errors.
@@ -369,7 +404,9 @@ Many different reproduction cartridges share their flash chip command set, so ev
369
404
 
370
405
  * If you’re using macOS version 10.13 or older, there may be no driver for serial communication devices installed on your system. You can either upgrade your macOS version to 10.14+ or manually install a driver which is available [here](https://github.com/adrianmihalko/ch340g-ch34g-ch34x-mac-os-x-driver).
371
406
 
372
- * If you’re using macOS and get a “Segmentation Fault: 11.” error, try the “Run using Python” method with Python version 3.10.11.
407
+ * If you’re using macOS and get a “Segmentation Fault: 11.” error, try the “Run via Python” method with Python version 3.10.11 and install FlashGBX with `pip3 install "FlashGBX[qt5]"`.
408
+
409
+ * If the save data detection says “512K FLASH (64 KiB) or 1M FLASH (128 KiB)”, that means the size can not be determined until actual save data is written to the cartridge.
373
410
 
374
411
  ## Miscellaneous
375
412
 
@@ -381,11 +418,13 @@ Many different reproduction cartridges share their flash chip command set, so ev
381
418
 
382
419
  The author would like to thank the following very kind people for their help, contributions or documentation (in alphabetical order):
383
420
 
384
- 2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, aronson, Ausar, bbsan, BennVenn, ccs21, chobby, ClassicOldSong, Cliffback, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gandalf1980, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, inYourBackline, iyatemu, Jayro, Jenetrix, JFox, joyrider3774, jrharbort, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Lu, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, Mufsta, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, RibShark, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, simonK, Sithdown, skite2001, Smelly-Ghost, Sonikks, Squiddy, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, Warez Waldo, wickawack, Winter1760, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr
421
+ 2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, aronson, Ausar, bbsan, BennVenn, ccs21, chobby, ClassicOldSong, Cliffback, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, delibird_deals, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, Gahr, gandalf1980, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, infinest, inYourBackline, iyatemu, Jayro, Jenetrix, JFox, joyrider3774, jrharbort, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Lu, Luca DS, LucentW, luxkiller65, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, Mufsta, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, RibShark, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, simonK, Sithdown, skite2001, Smelly-Ghost, Sonikks, Squiddy, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, Warez Waldo, wickawack, Winter1760, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr
422
+
423
+ Thanks to the No-Intro project for their game databases which FlashGBX’s databases are partly based on.
385
424
 
386
425
  ## Third Party Notices and Licenses
387
426
 
388
- Please view the <a href="https://raw.githubusercontent.com/lesserkuma/FlashGBX/master/Third Party Notices.md">Third Party Notices</a>.
427
+ Please view the <a href="https://github.com/lesserkuma/FlashGBX/blob/master/Third%20Party%20Notices.md">Third Party Notices</a>.
389
428
 
390
429
  ## DISCLAIMER
391
430