mirror of
https://github.com/lesserkuma/FlashGBX.git
synced 2026-04-25 16:19:24 -05:00
2.6
This commit is contained in:
parent
4883088a1e
commit
5b517c9526
13
CHANGES.md
13
CHANGES.md
|
|
@ -1,6 +1,15 @@
|
|||
# Release notes
|
||||
### v2.6 (released 2021-08-09)
|
||||
- Bundles GBxCart RW v1.4 firmware version R31+L2
|
||||
- Added a firmware updater for GBxCart v1.4 devices
|
||||
- Configuration path bug fix for systems that do not support PySide2 *(thanks JFox)*
|
||||
- Added support for S29GL032N90T and ALTERA CPLD (configured for MBC1) *(thanks t5b6_de)*
|
||||
- Flashing rumble enabled Game Boy Advance flash cartridges by insideGadgets is no longer noisy *(thanks AlexiG)*
|
||||
- Confirmed support for DMG-GBRW-20 with 29LV320ETMI-70G
|
||||
- Confirmed support for AGB-E20-30 with S29GL256N10TFI01
|
||||
|
||||
### v2.5 (released 2021-07-15)
|
||||
- Added support for 4350Q2 with 4050V0YBQ1
|
||||
- Added support for 4350Q2 with 4050V0YBQ1 *(thanks Shinichi999)*
|
||||
- Fixed Real Time Clock register access for MBC3B and MBC30 cartridges on the GBxCart RW v1.4 hardware
|
||||
- Added support for SD007_BV5 with 29LV160TE-70PFTN *(thanks RetroGorek)*
|
||||
|
||||
|
|
@ -23,7 +32,7 @@
|
|||
- Added support for SD007_TSOP_29LV017D with M29W320DT *(thanks marv17)*
|
||||
- Added support for SD007_TSOP_29LV017D with S29GL032M90T *(thanks marv17)*
|
||||
- Fixed detection of Classic NES Series and Famicom Mini game cartridges for Game Boy Advance *(thanks LucentW)*
|
||||
- Added support for 29LV128DBT2C-90Q and ALTERA CPLD *(thanks Sgt.DoudouMiel)*
|
||||
- Added support for 29LV128DBT2C-90Q and ALTERA CPLD (configured for MBC5) *(thanks Sgt.DoudouMiel)*
|
||||
- Added support for 0121 with 0121M0Y0BE *(thanks RetroGorek)*
|
||||
- Fixed detection of Pocket Monsters Ruby v1.1 (AGB-AXVJ-JPN) and Pocket Monsters Sapphire v1.1 (AGB-AXPJ-JPN) genuine game cartridges *(thanks Icesythe7)*
|
||||
- Fixed flash chip query for flash cartridges that have no CFI data but swapped pins *(thanks marv17)*
|
||||
|
|
|
|||
|
|
@ -88,11 +88,10 @@ def main(portableMode=False):
|
|||
app_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
try:
|
||||
from . import FlashGBX_GUI
|
||||
from PySide2 import QtCore
|
||||
cp = { "subdir":app_path + "/config", "appdata":QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppConfigLocation) }
|
||||
except:
|
||||
cp = { "subdir":app_path + "/config" }
|
||||
cp = { "subdir":app_path + "/config", "appdata":os.path.expanduser('~') }
|
||||
|
||||
if portableMode:
|
||||
cfgdir_default = "subdir"
|
||||
|
|
@ -117,10 +116,10 @@ def main(portableMode=False):
|
|||
parser.add_argument_group('')
|
||||
ap_config = parser.add_argument_group('configuration arguments')
|
||||
if "appdata" in cp: ap_config.add_argument("--cfgdir", choices=["appdata", "subdir"], type=str.lower, default=cfgdir_default, help="sets the config directory to either the OS-provided local app config directory (" + cp['appdata'] + "), or a subdirectory of this application (" + cp['subdir'].replace("\\", "/") + ")")
|
||||
|
||||
|
||||
ap_cli1 = parser.add_argument_group('main command line interface arguments')
|
||||
ap_cli1.add_argument("--mode", choices=["dmg", "agb"], type=str.lower, default=None, help="set cartridge mode to \"dmg\" (Game Boy) or \"agb\" (Game Boy Advance)")
|
||||
ap_cli1.add_argument("--action", choices=["info", "backup-rom", "flash-rom", "backup-save", "restore-save", "erase-save", "gbcamera-extract", "debug-test-save"], type=str.lower, default=None, help="select program action")
|
||||
ap_cli1.add_argument("--action", choices=["info", "backup-rom", "flash-rom", "backup-save", "restore-save", "erase-save", "gbcamera-extract", "fwupdate-gbxcartrw", "debug-test-save"], type=str.lower, default=None, help="select program action")
|
||||
ap_cli1.add_argument("--overwrite", action="store_true", help="overwrite without asking if target file already exists")
|
||||
ap_cli1.add_argument("path", nargs="?", default="auto", help="target or source file path (optional when reading, required when writing)")
|
||||
|
||||
|
|
@ -141,6 +140,7 @@ def main(portableMode=False):
|
|||
ap_cli2.add_argument("--save-filename-add-datetime", action="store_true", help="adds a timestamp to the file name of save data backups")
|
||||
ap_cli2.add_argument("--gbcamera-palette", choices=["grayscale", "dmg", "sgb", "cgb1", "cgb2", "cgb3"], type=str.lower, default="grayscale", help="sets the palette of pictures extracted from Game Boy Camera saves")
|
||||
ap_cli2.add_argument("--gbcamera-outfile-format", choices=["png", "bmp", "gif", "jpg"], type=str.lower, default="png", help="sets the file format of saved pictures extracted from Game Boy Camera saves")
|
||||
ap_cli2.add_argument("--fwupdate-port", help="override device port for the firmware updater", default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
if "appdata" in cp:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# FlashGBX
|
||||
# Author: Lesserkuma (github.com/lesserkuma)
|
||||
|
||||
import datetime, shutil, platform, os, json, math, traceback, re, time
|
||||
import datetime, shutil, platform, os, json, math, traceback, re, time, serial, zipfile
|
||||
try:
|
||||
# pylint: disable=import-error
|
||||
import readline
|
||||
|
|
@ -26,9 +26,11 @@ class FlashGBX_CLI():
|
|||
CONN = None
|
||||
DEVICE = None
|
||||
PROGRESS = None
|
||||
FWUPD_R = False
|
||||
|
||||
def __init__(self, args):
|
||||
self.ARGS = args
|
||||
self.APP_PATH = args['app_path']
|
||||
self.CONFIG_PATH = args['config_path']
|
||||
self.FLASHCARTS = args["flashcarts"]
|
||||
self.PROGRESS = Util.Progress(self.UpdateProgress)
|
||||
|
|
@ -55,11 +57,13 @@ class FlashGBX_CLI():
|
|||
|
||||
# Ask interactively if no args set
|
||||
if args.action is None:
|
||||
actions = ["info", "backup-rom", "flash-rom", "backup-save", "restore-save", "erase-save", "gbcamera-extract", "debug-test-save"]
|
||||
print("Select Operation:\n 1) Read Cartridge Information\n 2) Backup ROM\n 3) Write ROM\n 4) Backup Save Data\n 5) Restore Save Data\n 6) Erase Save Data\n 7) Extract Game Boy Camera Pictures\n")
|
||||
args.action = input("Enter number 1-7 [1]: ").lower().strip()
|
||||
print("")
|
||||
actions = ["info", "backup-rom", "flash-rom", "backup-save", "restore-save", "erase-save", "gbcamera-extract", "fwupdate-gbxcartrw", "debug-test-save"]
|
||||
print("Select Operation:\n 1) Read Cartridge Information\n 2) Backup ROM\n 3) Write ROM\n 4) Backup Save Data\n 5) Restore Save Data\n 6) Erase Save Data\n 7) Extract Game Boy Camera Pictures\n 8) Firmware Update (for GBxCart RW v1.4 only)\n")
|
||||
args.action = input("Enter number 1-8 [1]: ").lower().strip()
|
||||
try:
|
||||
if int(args.action) == 0:
|
||||
print("Canceled.")
|
||||
return
|
||||
args.action = actions[int(args.action) - 1]
|
||||
except:
|
||||
if args.action == "":
|
||||
|
|
@ -68,7 +72,7 @@ class FlashGBX_CLI():
|
|||
print("Canceled.")
|
||||
return
|
||||
|
||||
if args.action is None or args.action not in ("gbcamera-extract"):
|
||||
if args.action is None or args.action not in ("gbcamera-extract", "fwupdate-gbxcartrw"):
|
||||
if not self.FindDevices():
|
||||
print("No devices found.")
|
||||
return
|
||||
|
|
@ -109,7 +113,11 @@ class FlashGBX_CLI():
|
|||
print("\n{:s}Couldn’t parse the save data file.{:s}\n".format(ANSI.RED, ANSI.RESET))
|
||||
return
|
||||
|
||||
if args.mode is None:
|
||||
if args.action == "fwupdate-gbxcartrw":
|
||||
self.UpdateFirmwareGBxCartRW(pcb=5, port=args.fwupdate_port)
|
||||
return 0
|
||||
|
||||
elif args.mode is None:
|
||||
print("Select Cartridge Mode:\n 1) Game Boy or Game Boy Color\n 2) Game Boy Advance\n")
|
||||
answer = input("Enter number 1-2 [2]: ").lower().strip()
|
||||
print("")
|
||||
|
|
@ -385,13 +393,11 @@ class FlashGBX_CLI():
|
|||
str += "{:s}\n".format(Util.DMG_Header_SGB[data['sgb']])
|
||||
else:
|
||||
str += "Unknown (0x{:02X})\n".format(data['sgb'])
|
||||
bad_read = True
|
||||
str += "Game Boy Color: "
|
||||
if data['cgb'] in Util.DMG_Header_CGB:
|
||||
str += "{:s}\n".format(Util.DMG_Header_CGB[data['cgb']])
|
||||
else:
|
||||
str += "Unknown (0x{:02X})\n".format(data['cgb'])
|
||||
bad_read = True
|
||||
if data["logo_correct"]:
|
||||
str += "Nintendo Logo: OK\n"
|
||||
else:
|
||||
|
|
@ -728,7 +734,7 @@ class FlashGBX_CLI():
|
|||
if cart_type == 0:
|
||||
msg_5v = ""
|
||||
if mode == "DMG": msg_5v = "If your flash cartridge requires 5V to work, you can use the “--force-5v” command line switch, however please note that 5V can be unsafe for some flash chips."
|
||||
print("\n{:s}Auto-detection failed. Please use the “--flashcart-handler” command line switch to select the flash cartridge type manually.\n{:s}{:s}{:s}".format(ANSI.RED, ANSI.YELLOW, msg_5v, ANSI.RESET))
|
||||
print("\n{:s}Auto-detection failed. Please use the “--flashcart-handler” command line switch to select the flash cartridge type manually.\n{:s}{:s}{:s}".format(ANSI.RED, ANSI.RESET, msg_5v, ANSI.RESET))
|
||||
return
|
||||
elif cart_type < 0: return
|
||||
elif cart_type == 0 and args.flashcart_handler != "autodetect":
|
||||
|
|
@ -955,10 +961,25 @@ class FlashGBX_CLI():
|
|||
self.CONN._TransferData(args={ 'mode':2, 'path':self.CONFIG_PATH + "/test3.bin", 'mbc':mbc, 'save_type':save_type }, signal=self.PROGRESS.SetProgress)
|
||||
time.sleep(0.1)
|
||||
with open(self.CONFIG_PATH + "/test3.bin", "rb") as f: test3 = bytearray(f.read())
|
||||
print("\nPower cycling.")
|
||||
self.CONN.CartPowerOff()
|
||||
time.sleep(1)
|
||||
self.CONN.CartPowerOn()
|
||||
time.sleep(0.2)
|
||||
print("\nReading back and comparing data again.")
|
||||
self.CONN._TransferData(args={ 'mode':2, 'path':self.CONFIG_PATH + "/test4.bin", 'mbc':mbc, 'save_type':save_type }, signal=self.PROGRESS.SetProgress)
|
||||
time.sleep(0.1)
|
||||
with open(self.CONFIG_PATH + "/test4.bin", "rb") as f: test4 = bytearray(f.read())
|
||||
print("Restoring original save data.")
|
||||
self.CONN._TransferData(args={ 'mode':3, 'path':self.CONFIG_PATH + "/test1.bin", 'mbc':mbc, 'save_type':save_type, 'erase':False }, signal=self.PROGRESS.SetProgress)
|
||||
time.sleep(0.1)
|
||||
|
||||
if test3 != test4:
|
||||
diffcount = 0
|
||||
for i in range(0, len(test3)):
|
||||
if test3[i] != test4[i]: diffcount += 1
|
||||
print("\n{:s}Differences found after two consecutive reads: {:d}{:s}".format(ANSI.RED, diffcount, ANSI.RESET))
|
||||
|
||||
if mbc == 6:
|
||||
for i in range(0, len(test2)):
|
||||
test2[i] &= 0x0F
|
||||
|
|
@ -992,7 +1013,79 @@ class FlashGBX_CLI():
|
|||
except:
|
||||
pass
|
||||
|
||||
#input("\nPress ENTER to erase the temporary files.")
|
||||
os.unlink(self.CONFIG_PATH + "/test1.bin")
|
||||
os.unlink(self.CONFIG_PATH + "/test2.bin")
|
||||
os.unlink(self.CONFIG_PATH + "/test3.bin")
|
||||
os.unlink(self.CONFIG_PATH + "/test4.bin")
|
||||
|
||||
def UpdateFirmwareGBxCartRW_PrintText(self, text, enableUI=False, setProgress=None):
|
||||
if setProgress is not None:
|
||||
self.FWUPD_R = True
|
||||
print("\r{:s} ({:d}%)".format(text, int(setProgress)), flush=True, end="")
|
||||
else:
|
||||
if self.FWUPD_R is True:
|
||||
print("")
|
||||
print(text, flush=True)
|
||||
|
||||
def UpdateFirmwareGBxCartRW(self, pcb=5, port=False):
|
||||
if pcb != 5: return False
|
||||
print("\nFirmware Updater for GBxCart RW v1.4")
|
||||
print("====================================\n")
|
||||
with zipfile.ZipFile(self.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip") as zip:
|
||||
with zip.open("fw.ini") as f: ini_file = f.read()
|
||||
ini_file = ini_file.decode(encoding="utf-8")
|
||||
self.INI = Util.IniSettings(ini=ini_file, main_section="Firmware")
|
||||
fw_ver = self.INI.GetValue("fw_ver")
|
||||
fw_buildts = self.INI.GetValue("fw_buildts")
|
||||
fw_text = self.INI.GetValue("fw_text")
|
||||
|
||||
print("Available firmware version:\n{:s}\n".format("{:s} (dated {:s})".format(fw_ver, datetime.datetime.fromtimestamp(int(fw_buildts)).astimezone().replace(microsecond=0).isoformat())))
|
||||
print("Please follow these steps to proceed with the firmware update:\n1. Disconnect the USB cable of your GBxCart RW v1.4 device.\n2. On the circuit board of your GBxCart RW v1.4, press and hold down\n the small button while connecting the USB cable again.\n3. Keep the small button held for at least 2 seconds, then let go of it.\n If done right, the green LED labeled “Done” should remain lit.\n4. Press ENTER or RETURN to continue.")
|
||||
if len(input("").strip()) != 0:
|
||||
print("Canceled.")
|
||||
return False
|
||||
|
||||
try:
|
||||
ports = []
|
||||
if port is None or port is False:
|
||||
comports = serial.tools.list_ports.comports()
|
||||
for i in range(0, len(comports)):
|
||||
if comports[i].vid == 0x1A86 and comports[i].pid == 0x7523:
|
||||
ports.append(comports[i].device)
|
||||
if len(ports) == 0:
|
||||
print("No device found.")
|
||||
return False
|
||||
port = ports[0]
|
||||
|
||||
from . import fw_GBxCartRW_v1_4
|
||||
while True:
|
||||
try:
|
||||
print("Using port {:s}.\n".format(port))
|
||||
FirmwareUpdater = fw_GBxCartRW_v1_4.FirmwareUpdater
|
||||
FWUPD = FirmwareUpdater(port=port)
|
||||
ret = FWUPD.WriteFirmware(self.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip", self.UpdateFirmwareGBxCartRW_PrintText)
|
||||
break
|
||||
except serial.serialutil.SerialException:
|
||||
port = input("Couldn’t access port {:s}.\nEnter new port: ".format(port)).strip()
|
||||
if len(port) == 0:
|
||||
print("Canceled.")
|
||||
return False
|
||||
continue
|
||||
except Exception as err:
|
||||
traceback.print_exception(type(err), err, err.__traceback__)
|
||||
print(err)
|
||||
return False
|
||||
|
||||
if ret == 1:
|
||||
print("The firmware update is complete!")
|
||||
return True
|
||||
elif ret == 3:
|
||||
print("Please re-install the application.")
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as err:
|
||||
traceback.print_exception(type(err), err, err.__traceback__)
|
||||
print(err)
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -174,8 +174,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.mnuTools.addAction("Game Boy Camera Album Viewer", lambda: self.ShowPocketCameraWindow())
|
||||
#self.mnuTools.addAction("GB Memory Cartridge Manager", lambda: self.ShowGBMemoryWindow())
|
||||
self.mnuTools.addSeparator()
|
||||
self.mnuTools.addAction("Firmware &Updater", lambda: self.ShowFirmwareUpdateWindow()) # GBxCart RW v1.3
|
||||
self.mnuTools.actions()[2].setEnabled(False)
|
||||
self.mnuTools.addAction("Firmware &Updater", lambda: self.ShowFirmwareUpdateWindow())
|
||||
#self.mnuTools.actions()[2].setEnabled(False)
|
||||
self.btnTools.setMenu(self.mnuTools)
|
||||
|
||||
btnText = "C&onfig"
|
||||
|
|
@ -558,7 +558,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.btnRestoreRAM.setEnabled(False)
|
||||
self.btnConnect.setText("&Connect")
|
||||
self.lblDevice.setText("Disconnected.")
|
||||
self.mnuTools.actions()[2].setEnabled(False)
|
||||
#self.mnuTools.actions()[2].setEnabled(False)
|
||||
|
||||
def OpenConfigDir(self):
|
||||
path = 'file://{0:s}'.format(self.CONFIG_PATH)
|
||||
|
|
@ -656,7 +656,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
print(msg, end="")
|
||||
|
||||
if dev.SupportsFirmwareUpdates():
|
||||
self.mnuTools.actions()[2].setEnabled(True)
|
||||
#self.mnuTools.actions()[2].setEnabled(True)
|
||||
if dev.FirmwareUpdateAvailable():
|
||||
dontShowAgain = str(self.SETTINGS.value("SkipFirmwareUpdate", default="disabled")).lower() == "enabled"
|
||||
if not dontShowAgain or dev.FW_UPDATE_REQ:
|
||||
|
|
@ -680,7 +680,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if answer == QtWidgets.QMessageBox.Yes:
|
||||
self.ShowFirmwareUpdateWindow()
|
||||
else:
|
||||
self.mnuTools.actions()[2].setEnabled(False)
|
||||
#self.mnuTools.actions()[2].setEnabled(False)
|
||||
pass
|
||||
|
||||
return True
|
||||
return False
|
||||
|
|
@ -918,7 +919,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
if answer == QtWidgets.QMessageBox.No:
|
||||
return 0
|
||||
else:
|
||||
self.lblStatus4a.setText("Scanning...")
|
||||
qt_app.processEvents()
|
||||
detected = self.CONN.AutoDetectFlash(limitVoltage)
|
||||
self.lblStatus4a.setText("Ready.")
|
||||
if len(detected) == 0:
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="No pre-configured flash cartridge type was detected. You can still try and manually select one from the list -- look for similar PCB text and/or flash chip markings. However, chances are this cartridge is currently not supported for ROM writing with " + APPNAME + ".\n\nWould you like " + APPNAME + " to run a flash chip query? This may help adding support for your flash cartridge in the future.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
|
||||
|
|
@ -1106,6 +1110,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderCGBResult.styleSheet())
|
||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderCGBResult.styleSheet())
|
||||
|
||||
self.lblStatus4a.setText("Preparing...")
|
||||
qt_app.processEvents()
|
||||
args = { "path":path, "mbc":mbc, "rom_banks":rom_banks, "agb_rom_size":rom_size, "fast_read_mode":fast_read_mode, "cart_type":cart_type }
|
||||
self.CONN.BackupROM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
|
||||
self.grpStatus.setTitle("Transfer Status")
|
||||
|
|
@ -1234,6 +1240,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
# QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The file you selected could not be read.", QtWidgets.QMessageBox.Ok)
|
||||
# return
|
||||
|
||||
self.lblStatus4a.setText("Preparing...")
|
||||
qt_app.processEvents()
|
||||
args = { "path":path, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":fast_read_mode, "verify_flash":verify_flash }
|
||||
self.CONN.FlashROM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
|
||||
self.grpStatus.setTitle("Transfer Status")
|
||||
|
|
@ -1298,6 +1306,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
|
||||
self.SETTINGS.setValue(setting_name, os.path.dirname(path))
|
||||
|
||||
self.lblStatus4a.setText("Preparing...")
|
||||
qt_app.processEvents()
|
||||
args = { "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc }
|
||||
self.CONN.BackupRAM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
|
||||
self.grpStatus.setTitle("Transfer Status")
|
||||
|
|
@ -1374,6 +1384,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
rtc_advance = cb.isChecked()
|
||||
rtc = (answer == QtWidgets.QMessageBox.Yes)
|
||||
|
||||
self.lblStatus4a.setText("Preparing...")
|
||||
qt_app.processEvents()
|
||||
args = { "path":path, "mbc":mbc, "save_type":save_type, "rtc":rtc, "rtc_advance":rtc_advance, "erase":erase }
|
||||
self.CONN.RestoreRAM(fncSetProgress=self.PROGRESS.SetProgress, args=args)
|
||||
self.grpStatus.setTitle("Transfer Status")
|
||||
|
|
@ -1822,13 +1834,20 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
|||
self.TBPROG.setPaused(False)
|
||||
|
||||
def ShowFirmwareUpdateWindow(self):
|
||||
FirmwareUpdater = self.CONN.GetFirmwareUpdaterClass()
|
||||
if self.CONN is None:
|
||||
try:
|
||||
from . import fw_GBxCartRW_v1_4
|
||||
FirmwareUpdater = fw_GBxCartRW_v1_4.FirmwareUpdaterWindow
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
FirmwareUpdater = self.CONN.GetFirmwareUpdaterClass()[1]
|
||||
self.FWUPWIN = None
|
||||
self.FWUPWIN = FirmwareUpdater(self, app_path=self.APP_PATH, icon=self.windowIcon(), device=self.CONN)
|
||||
self.FWUPWIN.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
|
||||
self.FWUPWIN.setModal(True)
|
||||
self.FWUPWIN.run()
|
||||
|
||||
|
||||
def ShowPocketCameraWindow(self):
|
||||
self.CAMWIN = None
|
||||
self.CAMWIN = PocketCameraWindow(self, icon=self.windowIcon())
|
||||
|
|
|
|||
|
|
@ -118,11 +118,11 @@ class Flashcart:
|
|||
else:
|
||||
cfi = self.ReadCFI()
|
||||
if cfi is False:
|
||||
print("CFI error")
|
||||
print("CFI ERROR: Couldn’t retrieve buffer size from the cartridge.")
|
||||
return False
|
||||
if not "buffer_size" in cfi: return False
|
||||
buffer_size = cfi["buffer_size"]
|
||||
dprint("Buffer size was read from Common Flash Interface (CFI) data:", cfi["buffer_size"])
|
||||
dprint("Buffer size was read from CFI data:", cfi["buffer_size"])
|
||||
self.CONFIG["buffer_size"] = buffer_size
|
||||
return buffer_size
|
||||
else:
|
||||
|
|
@ -142,9 +142,10 @@ class Flashcart:
|
|||
#dprint(full_reset, "reset_every" in self.CONFIG)
|
||||
if full_reset and "reset_every" in self.CONFIG:
|
||||
for j in range(0, self.CONFIG["flash_size"], self.CONFIG["reset_every"]):
|
||||
dprint("reset_every @ 0x{:X}".format(j))
|
||||
for command in self.CONFIG["commands"]["reset"]:
|
||||
self.CartWrite([[j, command[1]]])
|
||||
time.sleep(0.001)
|
||||
time.sleep(0.01)
|
||||
elif "reset" in self.CONFIG["commands"]:
|
||||
self.CartWrite(self.CONFIG["commands"]["reset"])
|
||||
time.sleep(0.001)
|
||||
|
|
@ -207,11 +208,11 @@ class Flashcart:
|
|||
else:
|
||||
cfi = self.ReadCFI()
|
||||
if cfi is False:
|
||||
print("CFI error")
|
||||
print("CFI ERROR: Couldn’t retrieve sector size map from the cartridge.")
|
||||
return False
|
||||
sector_size = cfi["erase_sector_blocks"]
|
||||
if cfi["tb_boot_sector_raw"] == 0x03: sector_size.reverse()
|
||||
dprint("Sector map was read from Common Flash Interface (CFI) data:", cfi["erase_sector_blocks"])
|
||||
dprint("Sector size map was read from CFI data:", cfi["erase_sector_blocks"])
|
||||
self.CONFIG["sector_size"] = sector_size
|
||||
return sector_size
|
||||
else:
|
||||
|
|
@ -239,7 +240,7 @@ class Flashcart:
|
|||
self.CartWrite([[addr, sr_data]])
|
||||
self.CartRead(addr, 2) # dummy read (fixes some bootlegs)
|
||||
wait_for = struct.unpack("<H", self.CartRead(addr, 2))[0]
|
||||
dprint("Status Register Check: 0x{:X} & 0x{:X} == 0x{:X}? {:s}".format(wait_for, self.CONFIG["commands"]["chip_erase_wait_for"][i][2], data, str(wait_for == data)))
|
||||
dprint("Status Register Check: 0x{:X} & 0x{:X} == 0x{:X}? {:s}".format(wait_for, self.CONFIG["commands"]["chip_erase_wait_for"][i][2], data, str((wait_for & self.CONFIG["commands"]["chip_erase_wait_for"][i][2]) == data)))
|
||||
wait_for = wait_for & self.CONFIG["commands"]["chip_erase_wait_for"][i][2]
|
||||
if wait_for == data: break
|
||||
time.sleep(0.5)
|
||||
|
|
|
|||
|
|
@ -75,13 +75,13 @@ class DMG_MBC:
|
|||
if delay is not False: time.sleep(delay)
|
||||
|
||||
def GetName(self):
|
||||
return "Unknown MBC"
|
||||
return "Unknown MBC {:d}".format(self.MBC_ID)
|
||||
|
||||
def GetFullName(self):
|
||||
try:
|
||||
return Util.DMG_Header_Mapper[self.MBC_ID]
|
||||
except:
|
||||
return "Unknown MBC"
|
||||
return "Unknown MBC {:d}".format(self.MBC_ID)
|
||||
|
||||
def GetROMBank(self):
|
||||
return self.CURRENT_ROM_BANK
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from enum import Enum
|
|||
|
||||
# Common constants
|
||||
APPNAME = "FlashGBX"
|
||||
VERSION_PEP440 = "2.5"
|
||||
VERSION_PEP440 = "2.6"
|
||||
VERSION = "v{:s}".format(VERSION_PEP440)
|
||||
DEBUG = False
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
"type":"AGB",
|
||||
"names":[
|
||||
"insideGadgets 32 MB (28EW256A) + RTC/Rumble",
|
||||
"insideGadgets 32 MB (S29GL512N) + RTC"
|
||||
"insideGadgets 32 MB (S29GL512N) + RTC",
|
||||
"AGB-E20-30 with S29GL256N10TFI01"
|
||||
],
|
||||
"flash_ids":[
|
||||
[ 0x89, 0x00, 0x7E, 0x22 ],
|
||||
[ 0x01, 0x00, 0x7E, 0x22 ],
|
||||
[ 0x01, 0x00, 0x7E, 0x22 ]
|
||||
],
|
||||
"voltage":3.3,
|
||||
|
|
@ -14,6 +16,7 @@
|
|||
"chip_erase_timeout":300,
|
||||
"single_write_first_256_bytes":true,
|
||||
"rtc":true,
|
||||
"rumble":true,
|
||||
"command_set":"AMD",
|
||||
"commands":{
|
||||
"reset":[
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
[0x2000, 8],
|
||||
[0x10000, 63]
|
||||
],
|
||||
"chip_erase_timeout":60,
|
||||
"chip_erase_timeout":120,
|
||||
"mbc":5,
|
||||
"command_set":"AMD",
|
||||
"commands":{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
{
|
||||
"type":"DMG",
|
||||
"names":[
|
||||
"DMG-DHCN-20 with MX29LV320ET"
|
||||
"DMG-DHCN-20 with MX29LV320ET",
|
||||
"DMG-GBRW-20 with 29LV320ETMI-70G"
|
||||
],
|
||||
"flash_ids":[
|
||||
[ 0xC2, 0xC2, 0xA7, 0xA7 ],
|
||||
[ 0xC2, 0xC2, 0xA7, 0xA7 ]
|
||||
],
|
||||
"voltage":3.3,
|
||||
|
|
|
|||
80
FlashGBX/config/fc_DMG_S29GL032N90T_MBC1.txt
Normal file
80
FlashGBX/config/fc_DMG_S29GL032N90T_MBC1.txt
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"type":"DMG",
|
||||
"names":[
|
||||
"S29GL032N90T and ALTERA CPLD (MBC1)"
|
||||
],
|
||||
"flash_ids":[
|
||||
[ 0x02, 0x02, 0x7D, 0x7D ]
|
||||
],
|
||||
"manual_select":true,
|
||||
"voltage":3.3,
|
||||
"flash_size":0x80000,
|
||||
"start_addr":0x4000,
|
||||
"first_bank":1,
|
||||
"write_pin":"WR",
|
||||
"sector_size":[
|
||||
[0x2000, 8],
|
||||
[0x10000, 63]
|
||||
],
|
||||
"chip_erase_timeout":40,
|
||||
"mbc":1,
|
||||
"pulse_reset_after_write":true,
|
||||
"command_set":"AMD",
|
||||
"commands":{
|
||||
"reset":[
|
||||
[ 0x7000, 0xF0 ]
|
||||
],
|
||||
"read_identifier":[
|
||||
[ 0x7AAA, 0xA9 ],
|
||||
[ 0x7555, 0x56 ],
|
||||
[ 0x7AAA, 0x90 ]
|
||||
],
|
||||
"read_cfi":[
|
||||
[ 0x7AAA, 0x98 ]
|
||||
],
|
||||
"chip_erase":[
|
||||
[ 0x7AAA, 0xA9 ],
|
||||
[ 0x7555, 0x56 ],
|
||||
[ 0x7AAA, 0x80 ],
|
||||
[ 0x7AAA, 0xA9 ],
|
||||
[ 0x7555, 0x56 ],
|
||||
[ 0x7AAA, 0x10 ]
|
||||
],
|
||||
"chip_erase_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ 0, 0xFF, 0xFF ]
|
||||
],
|
||||
"_sector_erase":[
|
||||
[ 0x7AAA, 0xA9 ],
|
||||
[ 0x7555, 0x56 ],
|
||||
[ 0x7AAA, 0x80 ],
|
||||
[ 0x7AAA, 0xA9 ],
|
||||
[ 0x7555, 0x56 ],
|
||||
[ "SA", 0x30 ]
|
||||
],
|
||||
"sector_erase_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ "SA", 0xFF, 0xFF ]
|
||||
],
|
||||
"single_write":[
|
||||
[ 0x7AAA, 0xA9 ],
|
||||
[ 0x7555, 0x56 ],
|
||||
[ 0x7AAA, 0xA0 ],
|
||||
[ "PA", "PD" ]
|
||||
],
|
||||
"single_write_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import zipfile, os, serial, struct, time, re, math, platform
|
|||
from PySide2 import QtCore, QtWidgets, QtGui
|
||||
from . import Util
|
||||
|
||||
class FirmwareUpdater(QtWidgets.QDialog):
|
||||
class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||
APP = None
|
||||
DEVICE = None
|
||||
PORT = ""
|
||||
|
|
@ -14,6 +14,7 @@ class FirmwareUpdater(QtWidgets.QDialog):
|
|||
def __init__(self, app, app_path, file=None, icon=None, device=None):
|
||||
QtWidgets.QDialog.__init__(self)
|
||||
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
|
||||
self.setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }")
|
||||
self.APP = app
|
||||
self.APP_PATH = app_path
|
||||
self.DEVICE = device
|
||||
|
|
@ -250,7 +251,7 @@ class FirmwareUpdater(QtWidgets.QDialog):
|
|||
chk = chk & 0xFF
|
||||
chk = (~chk + 1) & 0xFF
|
||||
if (chk != data["checksum"]):
|
||||
fncSetStatus("Status: Firmware checksum error.")
|
||||
self.SetStatus("Status: Firmware checksum error.")
|
||||
self.prgStatus.setValue(0)
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
|
|
@ -261,7 +262,7 @@ class FirmwareUpdater(QtWidgets.QDialog):
|
|||
buffer += bytearray(data["data"])
|
||||
|
||||
if len(buffer) >= 7168:
|
||||
fncSetStatus("Status: Firmware file is too large.")
|
||||
self.SetStatus("Status: Firmware file is too large.")
|
||||
self.prgStatus.setValue(0)
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
|
|
@ -518,7 +519,7 @@ class FirmwareUpdater(QtWidgets.QDialog):
|
|||
self.grpAvailableFwUpdates.setEnabled(True)
|
||||
flash_ok = True
|
||||
text = "The firmware update is complete!"
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
self.reject()
|
||||
return 1
|
||||
|
|
|
|||
307
FlashGBX/fw_GBxCartRW_v1_4.py
Normal file
307
FlashGBX/fw_GBxCartRW_v1_4.py
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# FlashGBX
|
||||
# Author: Lesserkuma (github.com/lesserkuma)
|
||||
|
||||
import zipfile, serial, struct, time, random, hashlib, datetime
|
||||
from PySide2 import QtCore, QtWidgets, QtGui
|
||||
try:
|
||||
from . import Util
|
||||
except ImportError:
|
||||
import Util
|
||||
|
||||
class FirmwareUpdater():
|
||||
PORT = ""
|
||||
|
||||
def __init__(self, app_path=".", port=None):
|
||||
self.APP_PATH = app_path
|
||||
self.PORT = port
|
||||
|
||||
def WriteFirmware(self, zipfn, fncSetStatus):
|
||||
with zipfile.ZipFile(zipfn) as archive:
|
||||
with archive.open("fw.ini") as f: buffer1 = bytearray(f.read())
|
||||
with archive.open("fw.bin") as f: buffer2 = bytearray(f.read())
|
||||
while len(buffer1) < len(buffer2): buffer1 = buffer1 + buffer1
|
||||
random.seed(struct.unpack("<I", buffer2[-0x18:-0x14])[0])
|
||||
chk = buffer2[-0x14:]
|
||||
buffer = bytearray()
|
||||
for i in range(0, len(buffer2[0:-0x18])):
|
||||
r = int(random.random()*256) % 256
|
||||
buffer.append(buffer2[0:-0x18][len(buffer2[0:-0x18]) - i - 1] ^ r ^ buffer1[len(buffer1) - i - 1])
|
||||
if (chk != hashlib.sha1(buffer).digest()):
|
||||
fncSetStatus("The firmware update file is corrupted.")
|
||||
return 3
|
||||
|
||||
if self.PORT is None:
|
||||
ports = []
|
||||
comports = serial.tools.list_ports.comports()
|
||||
for i in range(0, len(comports)):
|
||||
if comports[i].vid == 0x1A86 and comports[i].pid == 0x7523:
|
||||
ports.append(comports[i].device)
|
||||
if len(ports) == 0:
|
||||
fncSetStatus("No device found.")
|
||||
return 2
|
||||
port = ports[0]
|
||||
else:
|
||||
port = self.PORT
|
||||
delay = 0
|
||||
lives = 10
|
||||
data = buffer
|
||||
buffer = bytearray()
|
||||
|
||||
fncSetStatus(text="Connecting...")
|
||||
try:
|
||||
dev = serial.Serial(port=port, baudrate=57600, timeout=1)
|
||||
except:
|
||||
fncSetStatus(text="Device not accessible.", enableUI=True)
|
||||
return 2
|
||||
dev.reset_input_buffer()
|
||||
|
||||
# Write firmware
|
||||
fncSetStatus("Updating firmware...", setProgress=0)
|
||||
|
||||
size = len(data)
|
||||
failed = 0
|
||||
counter = 0
|
||||
last_percent = 0
|
||||
while counter < size:
|
||||
byte = data[counter:counter+1]
|
||||
dev.write(byte)
|
||||
tmp_byte = dev.read(1)
|
||||
if (tmp_byte != byte):
|
||||
try:
|
||||
tmp_byte = int.from_bytes(tmp_byte, byteorder="little")
|
||||
except:
|
||||
tmp_byte = 0
|
||||
byte = int.from_bytes(byte, byteorder="little")
|
||||
if counter == 0:
|
||||
fncSetStatus(text="Update failed!".format(counter, byte, tmp_byte), enableUI=True)
|
||||
else:
|
||||
fncSetStatus(text="Update failed at offset 0x{:04X}!".format(counter, byte, tmp_byte), enableUI=True)
|
||||
return 2
|
||||
|
||||
counter += 1
|
||||
percent = float(counter)/size*100
|
||||
#if int(percent) > int(last_percent):
|
||||
fncSetStatus(text="Updating firmware... Do not unplug the device!", setProgress=percent)
|
||||
last_percent = percent
|
||||
|
||||
dev.close()
|
||||
time.sleep(0.8)
|
||||
fncSetStatus("Done.")
|
||||
time.sleep(0.2)
|
||||
flash_ok = True
|
||||
return 1
|
||||
|
||||
class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||
APP = None
|
||||
DEVICE = None
|
||||
FWUPD = None
|
||||
DEV_NAME = ""
|
||||
FW_VER = ""
|
||||
PCB_VER = ""
|
||||
|
||||
def __init__(self, app, app_path, file=None, icon=None, device=None):
|
||||
QtWidgets.QDialog.__init__(self)
|
||||
if icon is not None: self.setWindowIcon(QtGui.QIcon(icon))
|
||||
self.setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }")
|
||||
self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW v1.4")
|
||||
self.setWindowFlags((self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint) & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||
|
||||
self.APP = app
|
||||
if device is not None:
|
||||
self.FWUPD = FirmwareUpdater(app_path, device.GetPort())
|
||||
self.DEV_NAME = device.GetName()
|
||||
self.FW_VER = device.GetFirmwareVersion(more=True)
|
||||
self.PCB_VER = device.GetPCBVersion()
|
||||
self.DEVICE = device
|
||||
else:
|
||||
self.APP.QT_APP.processEvents()
|
||||
text = "This Firmware Updater is for insideGadgets GBxCart RW v1.4 devices only. Please only proceed if your device matches this hardware revision.\n\nIf you want to update another GBxCart RW hardware revision, please use the official firmware updater by insideGadgets instead."
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||
self.FWUPD = FirmwareUpdater(app_path, None)
|
||||
|
||||
with zipfile.ZipFile(app_path + "/res/fw_GBxCart_RW_v1_4.zip") as zip:
|
||||
with zip.open("fw.ini") as f: ini_file = f.read()
|
||||
ini_file = ini_file.decode(encoding="utf-8")
|
||||
self.INI = Util.IniSettings(ini=ini_file, main_section="Firmware")
|
||||
self.OFW_VER = self.INI.GetValue("fw_ver")
|
||||
self.OFW_BUILDTS = self.INI.GetValue("fw_buildts")
|
||||
self.OFW_TEXT = self.INI.GetValue("fw_text")
|
||||
|
||||
self.layout = QtWidgets.QGridLayout()
|
||||
self.layout.setContentsMargins(-1, 8, -1, 8)
|
||||
self.layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||
self.layout_device = QtWidgets.QVBoxLayout()
|
||||
|
||||
# ↓↓↓ Current Device Information
|
||||
self.grpDeviceInfo = QtWidgets.QGroupBox("Current Firmware")
|
||||
self.grpDeviceInfo.setMinimumWidth(420)
|
||||
self.grpDeviceInfoLayout = QtWidgets.QVBoxLayout()
|
||||
self.grpDeviceInfoLayout.setContentsMargins(-1, 3, -1, -1)
|
||||
rowDeviceInfo1 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceName = QtWidgets.QLabel("Device:")
|
||||
self.lblDeviceName.setMinimumWidth(120)
|
||||
self.lblDeviceNameResult = QtWidgets.QLabel("GBxCart RW")
|
||||
rowDeviceInfo1.addWidget(self.lblDeviceName)
|
||||
rowDeviceInfo1.addWidget(self.lblDeviceNameResult)
|
||||
rowDeviceInfo1.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo1)
|
||||
rowDeviceInfo2 = QtWidgets.QHBoxLayout()
|
||||
self.lblDevicePCBVer = QtWidgets.QLabel("PCB version:")
|
||||
self.lblDevicePCBVer.setMinimumWidth(120)
|
||||
self.lblDevicePCBVerResult = QtWidgets.QLabel("v1.4")
|
||||
rowDeviceInfo2.addWidget(self.lblDevicePCBVer)
|
||||
rowDeviceInfo2.addWidget(self.lblDevicePCBVerResult)
|
||||
rowDeviceInfo2.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo2)
|
||||
rowDeviceInfo3 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceFWVer = QtWidgets.QLabel("Firmware version:")
|
||||
self.lblDeviceFWVer.setMinimumWidth(120)
|
||||
self.lblDeviceFWVerResult = QtWidgets.QLabel("R30+L1")
|
||||
rowDeviceInfo3.addWidget(self.lblDeviceFWVer)
|
||||
rowDeviceInfo3.addWidget(self.lblDeviceFWVerResult)
|
||||
rowDeviceInfo3.addStretch(1)
|
||||
self.grpDeviceInfoLayout.addLayout(rowDeviceInfo3)
|
||||
self.grpDeviceInfo.setLayout(self.grpDeviceInfoLayout)
|
||||
self.layout_device.addWidget(self.grpDeviceInfo)
|
||||
# ↑↑↑ Current Device Information
|
||||
|
||||
# ↓↓↓ Available Firmware Updates
|
||||
self.grpAvailableFwUpdates = QtWidgets.QGroupBox("Available Firmware")
|
||||
self.grpAvailableFwUpdates.setMinimumWidth(400)
|
||||
self.grpAvailableFwUpdatesLayout = QtWidgets.QVBoxLayout()
|
||||
self.grpAvailableFwUpdatesLayout.setContentsMargins(-1, 3, -1, -1)
|
||||
|
||||
rowDeviceInfo4 = QtWidgets.QHBoxLayout()
|
||||
self.lblDeviceFWVer2 = QtWidgets.QLabel("Firmware version:")
|
||||
self.lblDeviceFWVer2.setMinimumWidth(120)
|
||||
self.lblDeviceFWVer2Result = QtWidgets.QLabel("R31+L2")
|
||||
rowDeviceInfo4.addWidget(self.lblDeviceFWVer2)
|
||||
rowDeviceInfo4.addWidget(self.lblDeviceFWVer2Result)
|
||||
rowDeviceInfo4.addStretch(1)
|
||||
self.grpAvailableFwUpdatesLayout.addLayout(rowDeviceInfo4)
|
||||
|
||||
self.rowUpdate = QtWidgets.QHBoxLayout()
|
||||
self.btnUpdate = QtWidgets.QPushButton("Install Firmware Update")
|
||||
self.btnUpdate.setMinimumWidth(200)
|
||||
self.btnUpdate.setContentsMargins(20, 20, 20, 20)
|
||||
self.connect(self.btnUpdate, QtCore.SIGNAL("clicked()"), lambda: [ self.UpdateFirmware() ])
|
||||
self.rowUpdate.addStretch()
|
||||
self.rowUpdate.addWidget(self.btnUpdate)
|
||||
self.rowUpdate.addStretch()
|
||||
|
||||
self.grpAvailableFwUpdatesLayout.addSpacing(3)
|
||||
self.grpAvailableFwUpdatesLayout.addItem(self.rowUpdate)
|
||||
self.grpAvailableFwUpdates.setLayout(self.grpAvailableFwUpdatesLayout)
|
||||
self.layout_device.addWidget(self.grpAvailableFwUpdates)
|
||||
# ↑↑↑ Available Firmware Updates
|
||||
|
||||
self.grpStatus = QtWidgets.QGroupBox("")
|
||||
self.grpStatusLayout = QtWidgets.QGridLayout()
|
||||
self.prgStatus = QtWidgets.QProgressBar()
|
||||
self.prgStatus.setMinimum(0)
|
||||
self.prgStatus.setMaximum(1000)
|
||||
self.prgStatus.setValue(0)
|
||||
self.lblStatus = QtWidgets.QLabel("Ready.")
|
||||
|
||||
self.grpStatusLayout.addWidget(self.prgStatus, 1, 0)
|
||||
self.grpStatusLayout.addWidget(self.lblStatus, 2, 0)
|
||||
|
||||
self.grpStatus.setLayout(self.grpStatusLayout)
|
||||
self.layout_device.addWidget(self.grpStatus)
|
||||
|
||||
self.grpFooterLayout = QtWidgets.QHBoxLayout()
|
||||
self.btnClose = QtWidgets.QPushButton("&Close")
|
||||
self.connect(self.btnClose, QtCore.SIGNAL("clicked()"), lambda: [ self.reject() ])
|
||||
self.grpFooterLayout.addStretch()
|
||||
self.grpFooterLayout.addWidget(self.btnClose)
|
||||
self.layout_device.addItem(self.grpFooterLayout)
|
||||
|
||||
self.layout.addLayout(self.layout_device, 0, 0)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.lblDeviceNameResult.setText(self.DEV_NAME)
|
||||
self.lblDeviceFWVerResult.setText(self.FW_VER)
|
||||
self.lblDevicePCBVerResult.setText(self.PCB_VER)
|
||||
|
||||
self.lblDeviceFWVer2Result.setText("{:s} (dated {:s})".format(self.OFW_VER, datetime.datetime.fromtimestamp(int(self.OFW_BUILDTS)).astimezone().replace(microsecond=0).isoformat()))
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.layout.update()
|
||||
self.layout.activate()
|
||||
screenGeometry = QtWidgets.QDesktopWidget().screenGeometry()
|
||||
x = (screenGeometry.width() - self.width()) / 2
|
||||
y = (screenGeometry.height() - self.height()) / 2
|
||||
self.move(x, y)
|
||||
self.show()
|
||||
except:
|
||||
return
|
||||
|
||||
def hideEvent(self, event):
|
||||
if self.DEVICE is None:
|
||||
self.APP.ConnectDevice()
|
||||
self.APP.activateWindow()
|
||||
|
||||
def reject(self):
|
||||
if self.CloseDialog():
|
||||
super().reject()
|
||||
|
||||
def CloseDialog(self):
|
||||
if self.btnClose.isEnabled() is False:
|
||||
text = "<b>WARNING:</b> If you close this window while a firmware update is still running, it may leave the device in an unbootable state. You can still recover it by running the Firmware Updater again later.<br><br>Are you sure you want to close this window?"
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.No: return False
|
||||
return True
|
||||
|
||||
def UpdateFirmware(self):
|
||||
self.APP.DisconnectDevice()
|
||||
|
||||
text = "Please follow these steps to proceed with the firmware update:<ol><li>Disconnect the USB cable of your GBxCart RW v1.4 device.</li><li>On the circuit board of your GBxCart RW v1.4, press and hold down the small button while connecting the USB cable again.</li><li>Keep the small button held for at least 2 seconds, then let go of it. If done right, the green LED labeled “Done” should remain lit.</li><li>Click OK to continue.</li></ol>"
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
msgbox.setTextFormat(QtCore.Qt.TextFormat.RichText)
|
||||
answer = msgbox.exec()
|
||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||
self.btnUpdate.setEnabled(False)
|
||||
self.btnClose.setEnabled(False)
|
||||
|
||||
while True:
|
||||
ret = self.FWUPD.WriteFirmware(self.FWUPD.APP_PATH + "/res/fw_GBxCart_RW_v1_4.zip", self.SetStatus)
|
||||
if ret == 1:
|
||||
text = "The firmware update is complete!"
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
self.DEVICE = None
|
||||
self.reject()
|
||||
return True
|
||||
elif ret == 2:
|
||||
text = "The firmware update is has failed. Please try again."
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
elif ret == 3:
|
||||
text = "The firmware update file is corrupted. Please re-install the application."
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)
|
||||
answer = msgbox.exec()
|
||||
return False
|
||||
|
||||
def SetStatus(self, text, enableUI=False, setProgress=None):
|
||||
self.lblStatus.setText("Status: {:s}".format(text))
|
||||
if setProgress is not None:
|
||||
self.prgStatus.setValue(setProgress * 10)
|
||||
if enableUI:
|
||||
self.btnUpdate.setEnabled(True)
|
||||
self.btnClose.setEnabled(True)
|
||||
self.APP.QT_APP.processEvents()
|
||||
|
|
@ -15,7 +15,7 @@ from . import Util
|
|||
class GbxDevice:
|
||||
DEVICE_NAME = "GBxCart RW"
|
||||
DEVICE_MIN_FW = 1
|
||||
DEVICE_MAX_FW = 1
|
||||
DEVICE_MAX_FW = 2
|
||||
|
||||
DEVICE_CMD = {
|
||||
"NULL":0x30,
|
||||
|
|
@ -29,6 +29,8 @@ class GbxDevice:
|
|||
"OFW_QUERY_CART_PWR":0x5D,
|
||||
"OFW_DONE_LED_ON":0x3D,
|
||||
"OFW_ERROR_LED_ON":0x3F,
|
||||
"OFW_GB_CART_MODE":0x47,
|
||||
"OFW_GB_FLASH_BANK_1_COMMAND_WRITES":0x4E,
|
||||
"QUERY_FW_INFO":0xA1,
|
||||
"SET_MODE_AGB":0xA2,
|
||||
"SET_MODE_DMG":0xA3,
|
||||
|
|
@ -54,6 +56,7 @@ class GbxDevice:
|
|||
"AGB_CART_WRITE_EEPROM":0xC6,
|
||||
"AGB_CART_WRITE_FLASH_DATA":0xC7,
|
||||
"AGB_CART_READ_3D_MEMORY":0xC8,
|
||||
"AGB_BOOTUP_SEQUENCE":0xC9,
|
||||
"DMG_FLASH_WRITE_BYTE":0xD1,
|
||||
"AGB_FLASH_WRITE_BYTE":0xD2,
|
||||
"FLASH_PROGRAM":0xD3,
|
||||
|
|
@ -309,18 +312,24 @@ class GbxDevice:
|
|||
|
||||
def SupportsFirmwareUpdates(self):
|
||||
#if self.FW["pcb_ver"] != 4: return False
|
||||
return self.FW["pcb_ver"] == 4
|
||||
return self.FW["pcb_ver"] in (4, 5)
|
||||
|
||||
def FirmwareUpdateAvailable(self):
|
||||
#if self.FW["pcb_ver"] != 4: return False
|
||||
#return self.FW["fw_ver"] < self.DEVICE_MAX_FW
|
||||
return (self.FW["pcb_ver"] == 4 and self.FW["fw_ver"] < self.DEVICE_MAX_FW)
|
||||
return (self.FW["pcb_ver"] in (4, 5) and self.FW["fw_ver"] < self.DEVICE_MAX_FW)
|
||||
|
||||
def GetFirmwareUpdaterClass(self):
|
||||
if self.FW["pcb_ver"] == 4: # v1.3
|
||||
try:
|
||||
from . import fw_GBxCartRW_v1_3
|
||||
return fw_GBxCartRW_v1_3.FirmwareUpdater
|
||||
return (None, fw_GBxCartRW_v1_3.FirmwareUpdaterWindow)
|
||||
except:
|
||||
return False
|
||||
elif self.FW["pcb_ver"] == 5: # v1.4
|
||||
try:
|
||||
from . import fw_GBxCartRW_v1_4
|
||||
return (fw_GBxCartRW_v1_4.FirmwareUpdater, fw_GBxCartRW_v1_4.FirmwareUpdaterWindow)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
|
|
@ -416,7 +425,7 @@ class GbxDevice:
|
|||
return self.ReadROM(address, length)
|
||||
|
||||
def _cart_write(self, address, value, flashcart=False, sram=False):
|
||||
dprint("Writing to cartridge: 0x{:X} = 0x{:X}".format(address, value & 0xFF))
|
||||
dprint("Writing to cartridge: 0x{:X} = 0x{:X}".format(address, value & 0xFF), flashcart, sram)
|
||||
if self.MODE == "DMG":
|
||||
if flashcart:
|
||||
buffer = bytearray([self.DEVICE_CMD["DMG_FLASH_WRITE_BYTE"]])
|
||||
|
|
@ -463,7 +472,6 @@ class GbxDevice:
|
|||
return True
|
||||
|
||||
def CartPowerOff(self):
|
||||
#return
|
||||
if self.FW["pcb_ver"] == 5:
|
||||
self._write(self.DEVICE_CMD["OFW_CART_PWR_OFF"])
|
||||
time.sleep(0.05)
|
||||
|
|
@ -525,6 +533,9 @@ class GbxDevice:
|
|||
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
||||
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
||||
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
||||
elif self.MODE == "AGB":
|
||||
if self.FW["pcb_ver"] == 5 and self.FW["fw_ver"] > 1:
|
||||
self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=True)
|
||||
|
||||
header = self.ReadROM(0, 0x180)
|
||||
if header is False or len(header) != 0x180: raise Exception("Couldn’t read the cartridge information. Please try again.")
|
||||
|
|
@ -533,9 +544,10 @@ class GbxDevice:
|
|||
|
||||
# Check for DACS
|
||||
dacs_8m = False
|
||||
if header[0x04:0x04+0x9C] == bytearray([0x00] * 0x9C):
|
||||
self.ReadROM(0x1FFFFE0, 20) # Unlock DACS
|
||||
header = self.ReadROM(0, 0x180)
|
||||
if self.FW["pcb_ver"] != 5 or self.FW["fw_ver"] == 1:
|
||||
if header[0x04:0x04+0x9C] == bytearray([0x00] * 0x9C):
|
||||
self.ReadROM(0x1FFFFE0, 20) # Unlock DACS
|
||||
header = self.ReadROM(0, 0x180)
|
||||
temp = self.ReadROM(0x1FFE000, 0x0C)
|
||||
if temp == b"AGBFLASHDACS":
|
||||
dacs_8m = True
|
||||
|
|
@ -742,6 +754,10 @@ class GbxDevice:
|
|||
if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
|
||||
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
||||
|
||||
if self.MODE == "DMG":
|
||||
self._set_fw_variable("ADDRESS", 0)
|
||||
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
||||
|
||||
return True
|
||||
|
||||
def WriteFlash_MBC6(self, address, buffer, mapper):
|
||||
|
|
@ -826,7 +842,7 @@ class GbxDevice:
|
|||
|
||||
self.NO_PROG_UPDATE = False
|
||||
|
||||
def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False):
|
||||
def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False):
|
||||
length = len(buffer)
|
||||
if self.FW["pcb_ver"] != 5:
|
||||
max_length = 256
|
||||
|
|
@ -847,6 +863,10 @@ class GbxDevice:
|
|||
self._set_fw_variable("BUFFER_SIZE", flash_buffer_size)
|
||||
|
||||
for i in range(0, num):
|
||||
if rumble_stop:
|
||||
dprint("Sending rumble stop command")
|
||||
self._cart_write(address=0xC6, value=0x00, flashcart=True)
|
||||
|
||||
data = bytearray(buffer[i*length:i*length+length])
|
||||
if (num_of_chunks == 1 or flash_buffer_size == 0) and (data == bytearray([0xFF] * len(data))):
|
||||
skip_init = False
|
||||
|
|
@ -869,7 +889,7 @@ class GbxDevice:
|
|||
ret = self._write(data, wait=True)
|
||||
|
||||
if ret not in (0x01, 0x03):
|
||||
print("{:s}Flash error in iteration {:d} of {:d} while trying to write a total of 0x{:X} bytes (response = {:s}){:s}".format(ANSI.RED, i, num, len(buffer), str(ret), ANSI.RESET))
|
||||
print("{:s}Flash error at 0x{:X} in iteration {:d} of {:d} while trying to write a total of 0x{:X} bytes (response = {:s}){:s}".format(ANSI.RED, address, i, num, len(buffer), str(ret), ANSI.RESET))
|
||||
self.SKIPPING = False
|
||||
return False
|
||||
pos += len(data)
|
||||
|
|
@ -1031,7 +1051,7 @@ class GbxDevice:
|
|||
flash_id_found = True
|
||||
flash_type = f
|
||||
flash_types.append(flash_type)
|
||||
flashcart.Reset(full_reset=True)
|
||||
flashcart.Reset(full_reset=False)
|
||||
dprint("Found the correct cartridge type!")
|
||||
|
||||
if self.MODE == "DMG" and not flash_id_found:
|
||||
|
|
@ -1213,6 +1233,13 @@ class GbxDevice:
|
|||
|
||||
if cfi['raw'] == b'':
|
||||
for we in we_pins:
|
||||
if we == "WR":
|
||||
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
|
||||
elif we in ("AUDIO", "VIN"):
|
||||
self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
|
||||
elif we == "WR+RESET":
|
||||
self._set_fw_variable("FLASH_WE_PIN", 0x03) # FLASH_WE_PIN_WR_RESET
|
||||
|
||||
for method in flash_commands:
|
||||
for i in range(0, len(method['reset'])):
|
||||
self._cart_write(method['reset'][i][0], method["reset"][i][1], flashcart=True)
|
||||
|
|
@ -1746,15 +1773,15 @@ class GbxDevice:
|
|||
]
|
||||
self._cart_write_flash(cmds)
|
||||
sr = 0
|
||||
lives = 1000
|
||||
lives = 50
|
||||
while True:
|
||||
time.sleep(0.001)
|
||||
time.sleep(0.01)
|
||||
sr = self._cart_read(sector_address << 12, agb_save_flash=True)
|
||||
dprint("Data Check: 0x{:X} == 0xFFFF? {:s}".format(sr, str(sr == 0xFFFF)))
|
||||
if sr == 0xFFFF: break
|
||||
lives -= 1
|
||||
if lives == 0:
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An error occured while writing to the cartridge. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.", "abortable":False})
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Accessing the save data flash chip failed. Please make sure you selected the correct save type. If you are using a reproduction cartridge, check if it really is equipped with a flash chip for save data, or if it uses SRAM for save data instead.", "abortable":False})
|
||||
return False
|
||||
self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
||||
elif self.MODE == "AGB" and args["save_type"] == 8: # DACS
|
||||
|
|
@ -1817,6 +1844,9 @@ class GbxDevice:
|
|||
_agb_gpio.WriteRTC(buffer[-0x10:], advance=advance)
|
||||
self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)})
|
||||
|
||||
if self.MODE == "DMG":
|
||||
_mbc.EnableRAM(enable=False)
|
||||
|
||||
# Clean up
|
||||
self.INFO["last_action"] = self.INFO["action"]
|
||||
self.INFO["action"] = None
|
||||
|
|
@ -1852,12 +1882,15 @@ class GbxDevice:
|
|||
return False
|
||||
# Special carts
|
||||
# Firmware check L1
|
||||
if "flash_commands_on_bank_1" in cart_type:
|
||||
if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported by FlashGBX. Please try the official GBxCart RW firmware and interface software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> instead.", "abortable":False})
|
||||
return False
|
||||
elif cart_type["type"] == "DMG" and "write_pin" in cart_type and cart_type["write_pin"] == "WR+RESET":
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported by FlashGBX. Please try the official GBxCart RW firmware and interface software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> instead.", "abortable":False})
|
||||
return False
|
||||
elif (self.FW["pcb_ver"] != 5 or self.FW["fw_ver"] < 2) and ("pulse_reset_after_write" in cart_type and cart_type["pulse_reset_after_write"] is True):
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is only supported by FlashGBX using GBxCart RW v1.4 and firmware version R31+L2 or higher. You can also try the official GBxCart RW firmware and interface software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> instead.", "abortable":False})
|
||||
return False
|
||||
# Firmware check L1
|
||||
|
||||
cart_type["_index"] = 0
|
||||
|
|
@ -1885,6 +1918,8 @@ class GbxDevice:
|
|||
else:
|
||||
flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, progress_fncptr=self.SetProgress)
|
||||
|
||||
rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
|
||||
|
||||
# ↓↓↓ Set Voltage
|
||||
if args["override_voltage"] is not False:
|
||||
if args["override_voltage"] == 5:
|
||||
|
|
@ -1909,10 +1944,8 @@ class GbxDevice:
|
|||
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, clk_toggle_fncptr=self._clk_toggle)
|
||||
|
||||
# I need a cart for testing before this has a chance to work ↓
|
||||
#temp = 1 if flashcart.FlashCommandsOnBank1() else 0
|
||||
#self._set_fw_variable("FLASH_COMMANDS_BANK_1", temp)
|
||||
#temp = 1 if flashcart.PulseResetAfterWrite() else 0
|
||||
#self._set_fw_variable("FLASH_PULSE_RESET", temp)
|
||||
#self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1 if flashcart.FlashCommandsOnBank1() else 0)
|
||||
self._set_fw_variable("FLASH_PULSE_RESET", 1 if flashcart.PulseResetAfterWrite() else 0)
|
||||
|
||||
rom_banks = math.ceil(len(data_import) / _mbc.GetROMBankSize())
|
||||
|
||||
|
|
@ -2045,8 +2078,11 @@ class GbxDevice:
|
|||
chip_erase = False
|
||||
else:
|
||||
chip_erase = True
|
||||
#if flashcart.FlashCommandsOnBank1():
|
||||
# _mbc.SelectBankROM(1)
|
||||
if flashcart.ChipErase() is False:
|
||||
return False
|
||||
#_mbc.SelectBankROM(0)
|
||||
elif flashcart.SupportsSectorErase() is False:
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No erase method available.", "abortable":False})
|
||||
return False
|
||||
|
|
@ -2076,6 +2112,12 @@ class GbxDevice:
|
|||
dprint("Resetting the MBC")
|
||||
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
||||
(start_address, bank_size) = _mbc.SelectBankROM(bank)
|
||||
if flashcart.PulseResetAfterWrite():
|
||||
if bank == 0:
|
||||
self._write(self.DEVICE_CMD["OFW_GB_CART_MODE"])
|
||||
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
||||
else:
|
||||
self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
|
||||
self._set_fw_variable("DMG_ROM_BANK", bank)
|
||||
|
||||
buffer_len = min(buffer_len, bank_size)
|
||||
|
|
@ -2086,13 +2128,6 @@ class GbxDevice:
|
|||
flashcart.SelectBankROM(bank)
|
||||
start_address = 0
|
||||
end_address = cart_type["flash_bank_size"]
|
||||
'''
|
||||
elif self.MODE == "AGB":
|
||||
if "agb-ghtj-jpn" in flashcart.CONFIG: # DACS
|
||||
start_address = 0x1F00000
|
||||
end_address = 0x1FFC000
|
||||
buffer_pos = start_address
|
||||
'''
|
||||
# ↑↑↑ Switch ROM bank
|
||||
|
||||
skip_init = False
|
||||
|
|
@ -2113,6 +2148,7 @@ class GbxDevice:
|
|||
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
|
||||
dprint("Erasing sector of size 0x{:X} at position 0x{:X} (0x{:X})".format(sector_size, buffer_pos, pos))
|
||||
current_sector_size = sector_size
|
||||
if flashcart.FlashCommandsOnBank1(): _mbc.SelectBankROM(bank)
|
||||
ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos)
|
||||
if ret is False:
|
||||
return False
|
||||
|
|
@ -2128,9 +2164,9 @@ class GbxDevice:
|
|||
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING))
|
||||
self._cart_write(pos + buffer_len - 1, 0xF0)
|
||||
else:
|
||||
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING))
|
||||
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble)
|
||||
if status is False:
|
||||
self.CANCEL_ARGS = {"info_type":"msgbox_critical", "info_msg":"An error occured while writing to the flash cartridge. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning."}
|
||||
self.CANCEL_ARGS = {"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes to the flash cartridge at position 0x{:X}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(buffer_len, buffer_pos)}
|
||||
self.CANCEL = True
|
||||
self.ERROR = True
|
||||
continue
|
||||
|
|
@ -2184,7 +2220,7 @@ class GbxDevice:
|
|||
if self.CANCEL is True:
|
||||
pass
|
||||
elif (verified_size is not True) and (buffer_pos != verified_size):
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The ROM was flashed completely, but verification of written data failed at address 0x{:X}.".format(verified_size), "abortable":False})
|
||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The ROM was written completely, but verification of written data failed at address 0x{:X}.".format(verified_size), "abortable":False})
|
||||
return False
|
||||
else:
|
||||
verified = True
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ class GbxDevice:
|
|||
if self.FW[1] == 4: # v1.3
|
||||
try:
|
||||
from . import fw_GBxCartRW_v1_3
|
||||
return fw_GBxCartRW_v1_3.FirmwareUpdater
|
||||
return fw_GBxCartRW_v1_3.FirmwareUpdaterWindow
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
|
|
@ -742,13 +742,17 @@ class GbxDevice:
|
|||
else:
|
||||
return buffer[0]
|
||||
|
||||
def CartPowerOff(self):
|
||||
if self.FW["pcb_ver"] == 5:
|
||||
self._write(self.DEVICE_CMD["CART_PWR_OFF"])
|
||||
time.sleep(0.05)
|
||||
|
||||
def CartPowerOn(self):
|
||||
if self.FW[1] == 5:
|
||||
self.write(self.DEVICE_CMD["QUERY_CART_PWR"])
|
||||
ret = self.DEVICE.read(1)
|
||||
if ret == b'\x00':
|
||||
self.set_mode(self.DEVICE_CMD["CART_PWR_ON"])
|
||||
print("POWER ON")
|
||||
time.sleep(0.2)
|
||||
self.DEVICE.reset_input_buffer() # bug workaround
|
||||
|
||||
|
|
|
|||
Binary file not shown.
BIN
FlashGBX/res/fw_GBxCart_RW_v1_4.zip
Normal file
BIN
FlashGBX/res/fw_GBxCart_RW_v1_4.zip
Normal file
Binary file not shown.
12
README.md
12
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# FlashGBX (by Lesserkuma)
|
||||
|
||||
for Windows, Linux, macOS
|
||||
for [Windows](https://github.com/lesserkuma/FlashGBX/releases), [Linux](https://github.com/lesserkuma/FlashGBX#run-using-python-linux-macos-windows), [macOS](https://github.com/lesserkuma/FlashGBX#run-using-python-linux-macos-windows)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/lesserkuma/FlashGBX/master/.github/01.png" alt="FlashGBX on Windows" width="400"><br><img src="https://raw.githubusercontent.com/lesserkuma/FlashGBX/master/.github/02.png" alt="GB Camera Album Viewer" width="400">
|
||||
|
||||
|
|
@ -14,13 +14,13 @@ for Windows, Linux, macOS
|
|||
- Many reproduction cartridges and flash cartridges can be auto-detected
|
||||
- A flash chip query (including Common Flash Interface information) can be performed for flash cartridges
|
||||
- Decode and extract Game Boy Camera photos from save data
|
||||
- Update firmware of insideGadgets GBxCart RW v1.3 devices
|
||||
- Update firmware of insideGadgets GBxCart RW v1.3 and v1.4 devices
|
||||
|
||||
### Confirmed working reader/writer hardware and firmware versions
|
||||
|
||||
- [insideGadgets GBxCart RW Mini, v1.3 and v1.4 (Standard and Pro versions)](https://www.gbxcart.com/)
|
||||
- Official firmware versions R19 to R30 (other hardware revisions and firmware versions may also work, but are untested)
|
||||
- Lesserkuma’s high compatibility firmware version L1
|
||||
- Official firmware versions R19 to R31 (other hardware revisions and firmware versions may also work, but are untested)
|
||||
- Lesserkuma’s high compatibility firmware version L1 and L2
|
||||
|
||||
### Currently supported official cartridge memory mappers
|
||||
|
||||
|
|
@ -84,11 +84,12 @@ for Windows, Linux, macOS
|
|||
- Game Boy
|
||||
|
||||
- DMG-DHCN-20 with MX29LV320ET
|
||||
- DMG-GBRW-20 with 29LV320ETMI-70G
|
||||
- ES29LV160_DRV with 29DL32TF-70
|
||||
- GB-M968 with 29LV160DB
|
||||
- GB-M968 with M29W160EB
|
||||
- GB-M968 with MX29LV320ABTC
|
||||
- S29GL032N90T and ALTERA CPLD configured for MBC5
|
||||
- S29GL032N90T and ALTERA CPLD configured for MBC1 or MBC5
|
||||
- SD007_48BALL_64M with GL032M11BAIR4
|
||||
- SD007_48BALL_64M with M29W640
|
||||
- SD007_48BALL_64M_V2 with GL032M11BAIR4
|
||||
|
|
@ -144,6 +145,7 @@ for Windows, Linux, macOS
|
|||
- AGB-E05-02 with M29W128GH
|
||||
- AGB-E05-06L with 29LV128DBT2C-90Q
|
||||
- AGB-E08-09 with 29LV128DTMC-90Q
|
||||
- AGB-E20-30 with S29GL256N10TFI01
|
||||
- AGB-SD-E05 with MSP55LV128
|
||||
- B104 with MSP55LV128
|
||||
- B11 with 26L6420MC-90
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
|
|||
|
||||
setuptools.setup(
|
||||
name="FlashGBX",
|
||||
version="2.5",
|
||||
version="2.6",
|
||||
author="Lesserkuma",
|
||||
description="Reads and writes Game Boy and Game Boy Advance cartridge data. Currently supports the GBxCart RW hardware device by insideGadgets.",
|
||||
url="https://github.com/lesserkuma/FlashGBX",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user