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/FlashGBX.py +1 -1
- FlashGBX/FlashGBX_CLI.py +24 -17
- FlashGBX/FlashGBX_GUI.py +346 -139
- FlashGBX/Flashcart.py +2 -1
- FlashGBX/GBMemory.py +3 -1
- FlashGBX/LK_Device.py +301 -216
- FlashGBX/Mapper.py +4 -4
- FlashGBX/PocketCameraWindow.py +31 -1
- FlashGBX/RomFileAGB.py +4 -1
- FlashGBX/RomFileDMG.py +7 -2
- FlashGBX/Util.py +30 -8
- FlashGBX/fw_JoeyJr.py +8 -1
- FlashGBX/hw_GBFlash.py +2 -2
- FlashGBX/hw_GBxCartRW.py +15 -15
- FlashGBX/hw_JoeyJr.py +2 -2
- FlashGBX/res/config.zip +0 -0
- FlashGBX/res/fw_GBFlash.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4a.zip +0 -0
- FlashGBX/res/fw_JoeyJr.zip +0 -0
- {FlashGBX-4.3.dist-info → FlashGBX-4.4.dist-info}/METADATA +91 -52
- FlashGBX-4.4.dist-info/RECORD +43 -0
- {FlashGBX-4.3.dist-info → FlashGBX-4.4.dist-info}/WHEEL +1 -1
- FlashGBX-4.3.dist-info/RECORD +0 -43
- {FlashGBX-4.3.dist-info → FlashGBX-4.4.dist-info}/LICENSE +0 -0
- {FlashGBX-4.3.dist-info → FlashGBX-4.4.dist-info}/entry_points.txt +0 -0
- {FlashGBX-4.3.dist-info → FlashGBX-4.4.dist-info}/top_level.txt +0 -0
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
|
-
#
|
|
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:
|
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):
|
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
|
|
@@ -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.
|
|
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 = ""
|
|
@@ -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
|
-
|
|
414
|
-
|
|
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"]
|
|
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="
|
|
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:
|
|
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"]
|
|
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:
|
|
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=
|
|
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,
|
|
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 >=
|
|
53
|
-
self.ChangeBaudRate(baudrate=
|
|
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
|
-
|
|
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 ==
|
|
177
|
-
self._write(self.DEVICE_CMD["
|
|
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"]
|
|
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"]
|
|
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 =
|
|
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"]
|
|
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
|
FlashGBX/res/fw_GBFlash.zip
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
FlashGBX/res/fw_JoeyJr.zip
CHANGED
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: FlashGBX
|
|
3
|
-
Version: 4.
|
|
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
|
|
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
|
|
26
|
+
Requires-Dist: PySide2>=5.14; extra == "qt5"
|
|
27
27
|
Provides-Extra: qt6
|
|
28
|
-
Requires-Dist: PySide6
|
|
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
|
|
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://
|
|
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
|
|