This commit is contained in:
Lesserkuma 2023-02-09 22:14:23 +01:00
parent a6fe80a56b
commit f556702f1c
21 changed files with 471 additions and 153 deletions

View File

@ -1,4 +1,11 @@
# Release notes
### v3.22 (released 2023-02-09)
- Added support for DRV with AM29LV160DB and ALTERA CPLD *(thanks ccs21)*
- Added support for DIY cart with HY29F800 *(thanks Kaede)*
- Added more information to the dump report with Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN)
- Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums
- Minor bug fixes and improvements
### v3.21 (released 2023-01-16)
- Bundles GBxCart RW v1.4/v1.4a firmware version R39+L8 (adds support for insideGadgets WonderSwan and Game Gear flash carts)
- Added support for SD007_48BALL_SOP28 with M29W320ET *(thanks DevDavisNunez)*
@ -10,7 +17,7 @@
- Allowed for switching between different Write Enable pins during chip erase and sector erase command sequences via a third parameter (“WR” or “AUDIO”) *(thanks ALXCO-Hardware for the suggestion)*
- Added support for the Squareboi 4 MB (2× 2 MB) cart *(thanks ALXCO-Hardware)*
- Added an option to limit the baud rate for GBxCart RW v1.4/v1.4a
- Minor bug fixes and improvements *(thanks gboh, Grender and orangeglo)*
- Minor bug fixes and improvements *(thanks gboh, Grender, orangeglo and marv17)*
### v3.20 (released 2022-11-30)
- Bundles GBxCart RW v1.4/v1.4a firmware version R38+L8 (minor improvements)
@ -83,7 +90,7 @@
- Added support for the Datel Orbit V2 mapper (Action Replay and GameShark) *(thanks Jenetrix)*
- Fixed verification with AA1030_TSOP88BALL with M36W0R603 *(thanks DevDavisNunez)*
- Added support for SD007_TSOP_48BALL_V9 with 32M29EWB *(thanks marv17)*
- Improved support for Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN) *(thanks kyokohunter)*
- Improved support for Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN) *(thanks kyokohunter)*
- Minor bug fixes and improvements
### v3.9 (released 2022-04-29)
@ -107,7 +114,7 @@
### v3.7 (released 2022-03-31)
- Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums (improves support for Classic NES Series, NES Classics and Famicom Mini cartridges)
- When writing new ROMs to Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN), hidden sector data will now be auto-generated if its not provided by the user in form of a .map file
- When writing new ROMs to Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN), hidden sector data will now be auto-generated if its not provided by the user in form of a .map file
- Erasing save data of Game Boy Advance cartridges with the 512K FLASH and 1M FLASH save types is now faster
- Windows Setup Package: Updated the CH340/CH341 driver to the latest version (01/18/2022, 3.7.2022.01)
- Fixed a bug introduced in v3.4 that broke writing ROMs to SD008-6810-V5 with MX29CL256FH cartridges
@ -129,7 +136,7 @@
- Added support for Flash Advance Card 64M with 28F640J3A120 *(thanks manuelcm1)*
- Fixed a bug with writing ROMs to insideGadgets rumble cartridges on the GBxCart RW v1.3 hardware running the L1 firmware *(thanks DevDavisNunez)*
- In GUI mode, flashable cartridges can now completely be wiped of their ROM data by not selecting any file when using the Write ROM feature *(thanks daidianren for the suggestion)*
- Fixed a bug introduced in v3.4 with making a ROM backup of Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN)
- Fixed a bug introduced in v3.4 with making a ROM backup of Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN)
- Bundles GBxCart RW v1.4 firmware version R34+L5 (enables support for some more flash cartridges)
- Added support for DIY cartridges with the AT49F040 flash chip for up to 512 KB of ROM data (requires at least GBxCart RW revision v1.4 and firmware version R34+L5) *(thanks Timville)*
- Added support for the insideGadgets 512 KB flash cartridge (requires at least GBxCart RW revision v1.4 and firmware version R34+L5) *(thanks Timville and marv17)*
@ -227,7 +234,7 @@
### v2.3 (released 2021-06-08)
- Added support for AGB-E05-06L with 29LV128DBT2C-90Q *(thanks marv17)*
- Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN) will now be unlocked properly even if theyre stuck in erase mode *(thanks Grender for testing)*
- Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN) will now be unlocked properly even if theyre stuck in erase mode *(thanks Grender for testing)*
- Confirmed support for SD007_TSOP_48BALL_V10 with GL032M10BFIR3 *(thanks Mr_V)*
- Added support for 2006_TSOP_64BALL_6106 with W29GL128SH9B *(thanks marv17)*
- Fixed support for insideGadgets 1 MB, 128 KB SRAM *(thanks AlexiG)*
@ -265,7 +272,7 @@
- Created a custom high compatibility firmware for GBxCart v1.3 and v1.4 devices, completely written from scratch
- Version L1 for GBxCart RW v1.3
- Works with all officially released Game Boy and Game Boy Advance cartridges including those with rare mappers, except cartridges that use memory cards and other external peripherals
- Supported mappers: MBC1, MBC2, MBC3, MBC30, MBC5, MBC6, MBC7, MBC1M, MMM01, GBD (Game Boy Camera), G-MMC1 (GB Memory), M161, HuC-1, HuC-3, TAMA5, DACS, 3D Memory (GBA Video)
- Supported mappers: MBC1, MBC2, MBC3, MBC30, MBC5, MBC6, MBC7, MBC1M, MMM01, GBD (Game Boy Camera), G-MMC1 (GB-Memory), M161, HuC-1, HuC-3, TAMA5, DACS, 3D Memory (GBA Video)
- Enables support for a few more reproduction and flash cartridges including:
- 4050M0Y0Q0-39VF512 with 4050M0Y0Q0
- Development AGB Cartridge 128M Flash S, E201850
@ -279,7 +286,7 @@
- Same as above but already integrated into the official firmware of GBxCart RW v1.4
- Added support for official cartridges with the MBC6 memory bank controller including its flash memory; tested with “Net de Get: Minigame @ 100” (CGB-BMVJ-JPN) *(thanks to endrifts research at [gbdev](https://gbdev.gg8.se/forums/viewtopic.php?id=544))* (requires GBxCart RW firmware version L1+)
- Previously preliminarily added mapper support including for MBC7 and GBA Video cartridges is now working (requires GBxCart RW firmware version L1+)
- Added support for writing compilation ROMs to Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN); requires a .map file in the same directory as the ROM file; all this can be generated using orangeglos [GBNP ROM builder website](https://orangeglo.github.io/gbnp/index.html)
- Added support for writing compilation ROMs to Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN); requires a .map file in the same directory as the ROM file; all this can be generated using orangeglos [GBNP ROM builder website](https://orangeglo.github.io/gbnp/index.html)
- Confirmed support for GB-M968 with 29LV160DB *(thanks bbsan)*
- Added support for ROM backup as well as save data backup and restore for 8M FLASH DACS cartridges; tested with “Hikaru no Go 3 Joy Carry Cartridge” (AGB-GHTJ-JPN)
- Confirmed support for SD007_TSOP_29LV017D with L017D70VC *(thanks marv17)*
@ -293,7 +300,7 @@
- Fixed minor bugs
### v1.4.1 (released 2021-03-15)
- Added ROM and map backup support for official Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN); save data handling and ROM writing is not supported yet
- Added ROM and map backup support for official Nintendo Power GB-Memory Cartridges (DMG-MMSA-JPN); save data handling and ROM writing is not supported yet
- Added preliminary support for 4050M0Y0Q0-39VF512 with 4050M0Y0Q0 (requires a future firmware update of GBxCart RW)
- Added preliminary support for official cartridges with the MBC7 memory bank controller; tested with “Korokoro Kirby” (CGB-KKKJ-JPN) (requires a future firmware update of GBxCart RW)
- Added support for official cartridges with the M161 memory bank controller; tested with “Mani 4 in 1: Tetris + Alleyway + Yakuman + Tennis” (DMG-601CHN) (requires GBxCart RW firmware R26 or newer)

View File

@ -33,7 +33,7 @@ def LoadConfig(args):
(config_version, fc_files) = ReadConfigFiles(args=args)
if config_version != Util.VERSION:
# Rename old files that have since been replaced/renamed/merged
deprecated_files = [ "fc_AGB_TEST.txt", "fc_DMG_TEST.txt", "fc_AGB_Nintendo_E201850.txt", "fc_AGB_Nintendo_E201868.txt", "config.ini", "fc_DMG_MX29LV320ABTC.txt", "fc_DMG_iG_4MB_MBC3_RTC.txt", "fc_AGB_Flash2Advance.txt", "fc_AGB_MX29LV640_AUDIO.txt", "fc_AGB_M36L0R7050T.txt", "fc_AGB_M36L0R8060B.txt", "fc_AGB_M36L0R8060T.txt", "fc_AGB_iG_32MB_S29GL512N.txt", "fc_DMG_SST39SF010_MBC1_AUDIO.txt", "fc_DMG_SST39SF040_MBC5_AUDIO.txt", "fc_DMG_AM29F010_MBC1_AUDIO.txt", "fc_DMG_AM29F040_MBC1_AUDIO.txt", "fc_DMG_AM29F040_MBC1_WR.txt", "fc_DMG_AM29F080_MBC1_AUDIO.txt", "fc_DMG_AM29F080_MBC1_WR.txt", "fc_DMG_SST39SF040_MBC1_AUDIO.txt", "fc_DMG_SST39SF020_MBC1_AUDIO.txt" ]
deprecated_files = [ "fc_AGB_TEST.txt", "fc_DMG_TEST.txt", "fc_AGB_Nintendo_E201850.txt", "fc_AGB_Nintendo_E201868.txt", "config.ini", "fc_DMG_MX29LV320ABTC.txt", "fc_DMG_iG_4MB_MBC3_RTC.txt", "fc_AGB_Flash2Advance.txt", "fc_AGB_MX29LV640_AUDIO.txt", "fc_AGB_M36L0R7050T.txt", "fc_AGB_M36L0R8060B.txt", "fc_AGB_M36L0R8060T.txt", "fc_AGB_iG_32MB_S29GL512N.txt", "fc_DMG_SST39SF010_MBC1_AUDIO.txt", "fc_DMG_SST39SF040_MBC5_AUDIO.txt", "fc_DMG_AM29F010_MBC1_AUDIO.txt", "fc_DMG_AM29F040_MBC1_AUDIO.txt", "fc_DMG_AM29F040_MBC1_WR.txt", "fc_DMG_AM29F080_MBC1_AUDIO.txt", "fc_DMG_AM29F080_MBC1_WR.txt", "fc_DMG_SST39SF040_MBC1_AUDIO.txt", "fc_DMG_SST39SF020_MBC1_AUDIO.txt", "fc_DMG_29LV016T.txt" ]
for file in deprecated_files:
if os.path.exists(config_path + "/" + file):
os.rename(config_path + "/" + file, config_path + "/" + file + "_" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + ".bak")
@ -175,9 +175,9 @@ def main(portableMode=False):
if app is None:
from . import FlashGBX_CLI
if args["argparsed"].action is None:
if exc is not None: print("{:s}{:s}{:s}".format(Util.ANSI.YELLOW, exc, Util.ANSI.RESET))
parser.print_help()
print("\n\n{:s}NOTE: GUI mode couldnt be launched, but the application can be run in CLI mode.\n Optional command line switches are explained above.{:s}\n".format(Util.ANSI.RED, Util.ANSI.RESET))
if exc is not None: print("{:s}{:s}{:s}".format(Util.ANSI.YELLOW, exc, Util.ANSI.RESET))
print("Now running in CLI mode.\n")
app = FlashGBX_CLI.FlashGBX_CLI(args)

View File

@ -252,7 +252,7 @@ class FlashGBX_CLI():
return
elif args["action"] == "PROGRESS":
# pv style progress status
prog_str = "{:s}/{:s} {:s} [{:s}KB/s] [{:s}] {:s}% ETA {:s} ".format(Util.formatFileSize(size=pos).replace(" ", "").replace("Bytes", "B").replace("Byte", "B").rjust(8), Util.formatFileSize(size=size).replace(" ", "").replace("Bytes", "B"), Util.formatProgressTimeShort(elapsed), "{:.2f}".format(speed).rjust(6), "%PROG_BAR%", "{:d}".format(int(pos/size*100)).rjust(3), Util.formatProgressTimeShort(left))
prog_str = "{:s}/{:s} {:s} [{:s}KiB/s] [{:s}] {:s}% ETA {:s} ".format(Util.formatFileSize(size=pos).replace(" ", "").replace("Bytes", "B").replace("Byte", "B").rjust(8), Util.formatFileSize(size=size).replace(" ", "").replace("Bytes", "B"), Util.formatProgressTimeShort(elapsed), "{:.2f}".format(speed).rjust(6), "%PROG_BAR%", "{:d}".format(int(pos/size*100)).rjust(3), Util.formatProgressTimeShort(left))
prog_width = shutil.get_terminal_size((80, 20))[0] - (len(prog_str) - 10)
progress = min(1, max(0, pos/size))
whole_width = math.floor(progress * prog_width)
@ -274,7 +274,7 @@ class FlashGBX_CLI():
speed = None
if "time_start" in self.PROGRESS.PROGRESS and self.PROGRESS.PROGRESS["time_start"] > 0:
time_elapsed = time.time() - self.PROGRESS.PROGRESS["time_start"]
speed = "{:.2f} KB/s".format((self.CONN.INFO["transferred"] / 1024.0) / time_elapsed)
speed = "{:.2f} KiB/s".format((self.CONN.INFO["transferred"] / 1024.0) / time_elapsed)
self.PROGRESS.PROGRESS["time_start"] = 0
if self.CONN.INFO["last_action"] == 4: # Flash ROM

View File

@ -5,6 +5,7 @@
import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, pkg_resources, struct, math
from .pyside import QtCore, QtWidgets, QtGui, QApplication
from PIL.ImageQt import ImageQt
from serial import SerialException
# from PIL import Image, ImageDraw
from .RomFileDMG import RomFileDMG
from .RomFileAGB import RomFileAGB
@ -653,6 +654,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
dev.SetWriteDelay(enable=str(self.SETTINGS.value("WriteDelay", default="disabled")).lower() == "enabled")
qt_app.processEvents()
self.CONN = dev
self.CONN.SetTimeout(float(self.SETTINGS.value("SerialTimeout", default="0.5")))
self.optDMG.setAutoExclusive(False)
self.optAGB.setAutoExclusive(False)
if "DMG" in self.CONN.GetSupprtedModes():
@ -832,7 +834,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
time_elapsed = time.time() - self.STATUS["time_start"]
msg_te = "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed, asFloat=True))
if "transferred" in self.CONN.INFO:
speed = "{:.2f} KB/s".format((self.CONN.INFO["transferred"] / 1024.0) / time_elapsed)
speed = "{:.2f} KiB/s".format((self.CONN.INFO["transferred"] / 1024.0) / time_elapsed)
self.STATUS["time_start"] = 0
if self.CONN.INFO["last_action"] == 1: # Backup ROM
@ -908,13 +910,35 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.lblHeaderROMChecksumResult.setText("Invalid (0x{:04X}≠0x{:04X})".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["rom_checksum"]))
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
msg = "The ROM was dumped, but the checksum is not correct."
button_gmmc1 = None
if self.CONN.INFO["loop_detected"] is not False:
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
else:
msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
if self.CONN.GetMode() == "DMG" and (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderMapperResult.currentIndex()])[0] in (1, 2, 3): # If MBC1
msg += "\n\nIf this is a NP GB-Memory Cartridge, please use the “Retry as G-MMC1” button."
button_gmmc1 = msgbox.addButton(" Retry as G-MMC1 ", QtWidgets.QMessageBox.ActionRole)
msgbox.setText(msg + msg_te)
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
msgbox.exec()
if msgbox.clickedButton() == button_gmmc1:
if self.CheckDeviceAlive():
mappers = list(Util.DMG_Header_Mapper.keys())
for i in range(0, len(mappers)):
if mappers[i] == 0x105:
self.cmbHeaderMapperResult.setCurrentIndex(i)
cart_type = 0
cart_types = self.CONN.GetSupportedCartridgesDMG()
for i in range(0, len(cart_types[0])):
if "dmg-mmsa-jpn" in cart_types[1][i]:
self.cmbDMGCartridgeTypeResult.setCurrentIndex(i)
cart_type = i
self.STATUS["args"]["mbc"] = 0x105
self.STATUS["args"]["rom_size"] = 1048576
self.STATUS["args"]["cart_type"] = cart_type
self.STATUS["time_start"] = time.time()
QtCore.QTimer.singleShot(1, lambda: [ self.CONN.BackupROM(fncSetProgress=self.PROGRESS.SetProgress, args=self.STATUS["args"]) ])
return
elif self.CONN.GetMode() == "AGB":
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
self.lblAGBHeaderROMChecksumResult.setText("Valid (0x{:06X})".format(Util.AGB_Global_CRC32))
@ -1043,7 +1067,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
else:
self.lblStatus4a.setText("Ready.")
self.CONN.INFO["last_action"] = 0
if dontShowAgain: self.SETTINGS.setValue("SkipFinishMessage", "enabled")
self.SetProgressBars(min=0, max=1, value=1)
@ -1063,13 +1087,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.cmbHeaderMapperResult.setCurrentIndex(i)
def CartridgeTypeChanged(self, index):
self.STATUS["cart_type"] = {}
if index in (-1, 0): return
if self.CONN.GetMode() == "DMG":
cart_types = self.CONN.GetSupportedCartridgesDMG()
if cart_types[1][index] == "RETAIL": # special keyword
pass
else:
if "flash_size" in cart_types[1][index]:
if "flash_size" in cart_types[1][index] and not "dmg-mmsa-jpn" in cart_types[1][index]:
for i in range(0, len(Util.DMG_Header_ROM_Sizes_Flasher_Map)):
if cart_types[1][index]["flash_size"] == (Util.DMG_Header_ROM_Sizes_Flasher_Map[i]):
self.cmbHeaderROMSizeResult.setCurrentIndex(i)
@ -1128,6 +1153,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.grpStatus.setTitle("Transfer Status")
self.STATUS["time_start"] = time.time()
self.STATUS["last_path"] = path
self.STATUS["args"] = args
def FlashROM(self, dpath=""):
if not self.CheckDeviceAlive(): return
@ -1252,7 +1278,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
mbc1 = Util.get_mbc_name(mbc)
mbc2 = Util.get_mbc_name(hdr["mapper_raw"])
compatible_mbc = [ "None", "MBC2", "MBC3", "MBC5", "MBC7", "GBD", "G-MMC1", "HuC-1", "HuC-3" ]
if mbc2 == "None":
if (mbc2 == "None") or (mbc1 == "G-MMC1" and mbc2 == "MBC1") or (mbc2 == "G-MMC1" and mbc1 == "MBC1"):
pass
elif mbc2 != "None" and not (mbc1 in compatible_mbc and mbc2 in compatible_mbc):
if "mbc" in carts[cart_type] and carts[cart_type]["mbc"] == "manual":
@ -1315,6 +1341,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
buffer = None
self.STATUS["time_start"] = time.time()
self.STATUS["last_path"] = path
self.STATUS["args"] = args
def BackupRAM(self):
if not self.CheckDeviceAlive(): return
@ -1383,6 +1410,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.grpStatus.setTitle("Transfer Status")
self.STATUS["time_start"] = time.time()
self.STATUS["last_path"] = path
self.STATUS["args"] = args
def WriteRAM(self, dpath="", erase=False):
if not self.CheckDeviceAlive(): return
@ -1478,6 +1506,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.grpStatus.setTitle("Transfer Status")
self.STATUS["time_start"] = time.time()
self.STATUS["last_path"] = path
self.STATUS["args"] = args
def CheckDeviceAlive(self, setMode=False):
if self.CONN is not None:
@ -1572,7 +1601,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.lblStatus4a.setText("Reading cartridge data...")
self.SetProgressBars(min=0, max=0, value=1)
qt_app.processEvents()
data = self.CONN.ReadInfo(setPinsAsInputs=True)
try:
data = self.CONN.ReadInfo(setPinsAsInputs=True)
except SerialException:
self.DisconnectDevice()
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The connection to the device was lost while trying to read the ROM header. This may happen if the inserted cartridge issues a short circuit or its peak power draw is too high.\n\nAs a potential workaround of the latter, you can try hotswapping the cartridge:\n1. Remove the cartridge from the device\n2. Reconnect the device and select mode\n3. Then insert the cartridge and click “Read Info”", QtWidgets.QMessageBox.Ok)
return False
if resetStatus:
self.btnHeaderRefresh.setEnabled(True)
self.btnDetectCartridge.setEnabled(True)
@ -1597,12 +1633,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.cmbHeaderMapperResult.clear()
self.cmbHeaderMapperResult.addItems(list(Util.DMG_Header_Mapper.values()))
self.cmbHeaderMapperResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
if resetStatus:
self.cmbDMGCartridgeTypeResult.clear()
self.cmbDMGCartridgeTypeResult.addItems(self.CONN.GetSupportedCartridgesDMG()[0])
self.cmbDMGCartridgeTypeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
if "flash_type" in data:
self.cmbDMGCartridgeTypeResult.setCurrentIndex(data["flash_type"])
self.cmbDMGCartridgeTypeResult.clear()
self.cmbDMGCartridgeTypeResult.addItems(self.CONN.GetSupportedCartridgesDMG()[0])
self.cmbDMGCartridgeTypeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
if "flash_type" in data:
self.cmbDMGCartridgeTypeResult.setCurrentIndex(data["flash_type"])
self.cmbHeaderROMSizeResult.clear()
self.cmbHeaderROMSizeResult.addItems(Util.DMG_Header_ROM_Sizes)
self.cmbHeaderROMSizeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
@ -2136,6 +2171,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
def UpdateProgress(self, args):
if args is None: return
if self.CONN is None: return
if "method" in args:
if args["method"] == "ROM_READ":
self.grpStatus.setTitle("Transfer Status (Backup ROM)")
@ -2278,9 +2314,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.btnCancel.setEnabled(args["abortable"])
else:
self.btnCancel.setEnabled(True)
self.lblStatus1aResult.setText(Util.formatFileSize(pos))
self.lblStatus1aResult.setText("{:s}".format(Util.formatFileSize(pos)))
if speed > 0:
self.lblStatus2aResult.setText("{:.2f} KB/s".format(speed))
self.lblStatus2aResult.setText("{:.2f} KiB/s".format(speed))
else:
self.lblStatus2aResult.setText("Pending...")
if left > 0:

View File

@ -42,10 +42,13 @@ class Flashcart:
self.DEFAULT_WE = config["write_pin"]
def CartRead(self, address, length=0):
if length == 0:
if self.CONFIG["type"].upper() == "AGB":
if self.CONFIG["type"].upper() == "AGB":
if length % 2 == 1:
length += 1
if length == 0:
length = 2
else:
else:
if length == 0:
length = 1
return self.CART_READ_FNCPTR(address, length)

View File

@ -10,12 +10,46 @@ class GBMemoryMap:
MAP_DATA = bytearray([0xFF] * 0x80)
IS_MENU = False
def __init__(self, rom=None):
def __init__(self, rom=None, oldmap=None):
if rom is None: return
if rom == bytearray([0xFF] * len(rom)):
self.MAP_DATA = bytearray([0x00] * 0x80)
elif rom is not None:
self.ImportROM(rom)
if oldmap is not None:
self.MAP_DATA[0x70:0x78] = oldmap[0x70:0x78] # keep existing cart id
write_count = struct.unpack("=H", oldmap[0x6E:0x70])[0]
write_count += 1
if write_count > 0xFFFF: write_count = 0xFFFF
self.MAP_DATA[0x6E:0x70] = struct.pack("=H", write_count) # update write count
def ParseMapData(self, buffer_map, buffer_rom=None):
data = {}
try:
keys = ["mapper_params", "f_size", "b_size", "game_code", "title", "timestamp", "kiosk_id", "write_count", "cart_id", "padding", "unknown"]
values = struct.unpack("=24sHH12s44s18s8sH8s6sH", buffer_map)
data = dict(zip(keys, values))
data["mapper_params"] = data["mapper_params"].hex().upper()
data["cart_id"] = data["cart_id"].hex().upper()
rom_header = RomFileDMG(buffer_rom[:0x180]).GetHeader()
if (rom_header["game_title"] in ("NP M-MENU MENU", "DMG MULTI MENU ")):
keys = ["menu_index", "f_offset", "b_offset", "f_size", "b_size", "game_code", "title", "title_gfx", "timestamp", "kiosk_id", "padding", "comment"]
values = struct.unpack("=BBBHH12s44s384s18s8s23s16s", buffer_rom[0x1C000:0x1C200])
data_menu = dict(zip(keys, values))
data["game_code"] = data_menu["game_code"].decode("ASCII", "ignore")
data["title"] = data_menu["title"].decode("SHIFT-JIS", "ignore")
data["timestamp"] = data_menu["timestamp"].decode("ASCII", "ignore")
data["kiosk_id"] = data_menu["kiosk_id"].decode("ASCII", "ignore")
else:
data["game_code"] = data["game_code"].decode("ASCII", "ignore")
data["title"] = data["title"].decode("SHIFT-JIS", "ignore")
data["timestamp"] = data["timestamp"].decode("ASCII", "ignore")
data["kiosk_id"] = data["kiosk_id"].decode("ASCII", "ignore")
except:
pass
return data
def ImportROM(self, data):
info = {"map":{}, "menu":{}}
if len(data) < 0x180:
@ -81,10 +115,11 @@ class GBMemoryMap:
elif info["map"]["sram_type"] == 0b101: # SRAM 128 KB
info["menu"]["metadata"]["b_size"] = 1024
info["menu"]["metadata"]["game_code"] = "{:s} -{:s}- ".format("CGB" if info["rom_header"]["cgb"] == 0xC0 else "DMG", " ").encode("ascii")
game_code = info["rom_header"]["game_code"]
info["menu"]["metadata"]["game_code"] = "{:s} -{:4s}- ".format("CGB" if info["rom_header"]["cgb"] == 0xC0 else "DMG", game_code).encode("ascii")
info["menu"]["metadata"]["title"] = info["rom_header"]["game_title"].encode("ascii").ljust(0x2C)
info["menu"]["metadata"]["timestamp"] = datetime.datetime.now().strftime('%Y/%m/%d%H:%M:%S').encode("ascii")
info["menu"]["metadata"]["kiosk_id"] = "{:s} v{:s}".format(Util.APPNAME, Util.VERSION_PEP440).encode("ascii").ljust(24, b'\xFF')
info["menu"]["metadata"]["timestamp"] = datetime.datetime.now().strftime('%d/%m/%Y%H:%M:%S').encode("ascii")
info["menu"]["metadata"]["kiosk_id"] = "{:s}".format(Util.APPNAME).encode("ascii").ljust(8, b'\xFF')
info["menu"]["raw"] = bytearray(0x56)
data = info
@ -92,7 +127,7 @@ class GBMemoryMap:
values = []
for key in keys:
values.append(data["menu"]["metadata"][key])
buffer = struct.pack("=HH12s44s18s26s", *values)
buffer = struct.pack("=HH12s44s18s8s", *values)
data["menu"]["raw"] = buffer
temp = 0
@ -103,11 +138,13 @@ class GBMemoryMap:
temp |= (data["map"]["ram_start_block"] & 0x7F) << 8
data["map"]["raw"] = temp
self.MAP_DATA[0x00:0x6E] = bytearray([0xFF] * 0x6E)
self.MAP_DATA[0x00:0x7E] = bytearray([0xFF] * 0x7E)
self.MAP_DATA[0x6E:0x70] = bytearray([0x00] * 2)
#self.MAP_DATA[0x70:0x78] = struct.pack("=8s", "{:s}".format(Util.VERSION_PEP440).encode("ascii").ljust(8, b'\xFF'))
self.MAP_DATA[0x7E:0x80] = bytearray([0x00] * 2)
self.MAP_DATA[0:3] = struct.pack(">I", data["map"]["raw"])[:3]
self.MAP_DATA[0x18:0x18+len(data["menu"]["raw"])] = data["menu"]["raw"]
elif info["rom_header"]["game_title"] == "NP M-MENU MENU":
menu_items = []
rom_offset = 0
@ -177,13 +214,15 @@ class GBMemoryMap:
info["map"]["raw"] = temp
menu_items.append(info)
self.MAP_DATA[0x00:0x6E] = bytearray([0xFF] * 0x6E)
self.MAP_DATA[0x00:0x7E] = bytearray([0xFF] * 0x7E)
self.MAP_DATA[0x6E:0x70] = bytearray([0x00] * 2)
#self.MAP_DATA[0x70:0x78] = struct.pack("=8s", "{:s}".format(Util.VERSION_PEP440).encode("ascii").ljust(8, b'\xFF'))
self.MAP_DATA[0x7E:0x80] = bytearray([0x00] * 2)
for i in range(0, len(menu_items)):
pos = i * 3
self.MAP_DATA[pos:pos+3] = struct.pack(">I", menu_items[i]["map"]["raw"])[:3]
self.MAP_DATA[0x54:0x66] = struct.pack("=18s", datetime.datetime.now().strftime('%Y/%m/%d%H:%M:%S').encode("ascii"))
self.MAP_DATA[0x66:0x7E] = struct.pack("=24s", "{:s} v{:s}".format(Util.APPNAME, Util.VERSION_PEP440).encode("ascii").ljust(24, b'\xFF'))
self.MAP_DATA[0x54:0x66] = struct.pack("=18s", datetime.datetime.now().strftime('%d/%m/%Y%H:%M:%S').encode("ascii"))
self.MAP_DATA[0x66:0x6E] = struct.pack("=8s", "{:s}".format(Util.APPNAME).encode("ascii").ljust(8, b'\xFF'))
def MapperToMBCType(self, mbc):
if mbc == 0x00: # ROM only

View File

@ -150,9 +150,11 @@ class RomFileDMG:
# GB Memory
if data["mapper_raw"] == 0x19 and data["game_title"] == "NP M-MENU MENU" and data["header_checksum"] == 0xD3:
data["rom_size_raw"] = 0x05
data["ram_size_raw"] = 0x04
data["mapper_raw"] = 0x105
elif data["mapper_raw"] == 0x01 and data["game_title"] == "DMG MULTI MENU " and data["header_checksum"] == 0x36:
data["rom_size_raw"] = 0x05
data["ram_size_raw"] = 0x04
data["mapper_raw"] = 0x105

View File

@ -7,14 +7,14 @@ from enum import Enum
# Common constants
APPNAME = "FlashGBX"
VERSION_PEP440 = "3.21"
VERSION_PEP440 = "3.22"
VERSION = "v{:s}".format(VERSION_PEP440)
DEBUG = False
DEBUG_LOG = []
AGB_Header_ROM_Sizes = [ "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", "8 MB", "16 MB", "32 MB", "64 MB", "128 MB", "256 MB" ]
AGB_Header_ROM_Sizes = [ "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB", "8 MiB", "16 MiB", "32 MiB", "64 MiB", "128 MiB", "256 MiB" ]
AGB_Header_ROM_Sizes_Map = [ 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000 ]
AGB_Header_Save_Types = [ "None", "4K EEPROM (512 Bytes)", "64K EEPROM (8 KB)", "256K SRAM/FRAM (32 KB)", "512K FLASH (64 KB)", "1M FLASH (128 KB)", "8M DACS (1008 KB)", "Unlicensed 512K SRAM (64 KB)", "Unlicensed 1M SRAM (128 KB)" ]
AGB_Header_Save_Types = [ "None", "4K EEPROM (512 Bytes)", "64K EEPROM (8 KiB)", "256K SRAM/FRAM (32 KiB)", "512K FLASH (64 KiB)", "1M FLASH (128 KiB)", "8M DACS (1008 KiB)", "Unlicensed 512K SRAM (64 KiB)", "Unlicensed 1M SRAM (128 KiB)" ]
AGB_Header_Save_Sizes = [ 0, 512, 8192, 32768, 65536, 131072, 1032192, 65536, 131072 ]
AGB_Global_CRC32 = 0
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS" }
@ -22,10 +22,10 @@ AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper' }
DMG_Mapper_Types = { "None":[ 0x00 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x06 ], "MBC3":[ 0x10, 0x13 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "256M Multi Cart":[ 0x201 ], "Wisdom Tree":[ 0x202 ], "Xploder GB":[ 0x203 ], "Sachen":[ 0x204 ], "Datel Orbit V2":[ 0x205 ] }
DMG_Header_ROM_Sizes = [ "32 KB", "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", "8 MB", "16 MB", "32 MB" ]
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" ]
DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A ]
DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000 ]
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "16K SRAM (2 KB)", "64K SRAM (8 KB)", "256K SRAM (32 KB)", "512K SRAM (64 KB)", "1M SRAM (128 KB)", "MBC6 SRAM+FLASH (1.03 MB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KB)", "Unlicensed 1M EEPROM (128 KB)" ]
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)" ]
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201, 0x203 ]
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x800, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000, 0x20000 ] # RAM size in bytes
DMG_Header_SGB = { 0x00:'No support', 0x03:'Supported' }
@ -50,9 +50,9 @@ class IniSettings():
if not os.path.isdir(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
if os.path.exists(path):
with open(path, "a+") as f: f.close()
with open(path, "a+", encoding="UTF-8") as f: f.close()
else:
with open(path, "w+") as f: f.close()
with open(path, "w+", encoding="UTF-8") as f: f.close()
except:
print("Error accessing the configuration directory or settings file.")
return
@ -77,7 +77,7 @@ class IniSettings():
def Reload(self):
if self.SETTINGS is None: return
if self.FILENAME is not False:
with open(self.FILENAME, "r", encoding="utf-8") as f:
with open(self.FILENAME, "r", encoding="UTF-8") as f:
self.SETTINGS.read_file(f)
if len(self.SETTINGS.sections()) == 0:
self.SETTINGS.add_section(self.MAIN_SECTION)
@ -98,7 +98,7 @@ class IniSettings():
self.SETTINGS[self.MAIN_SECTION][key] = value
dprint("Updating settings:", key, "=", value)
if self.FILENAME is not False:
with open(self.FILENAME, "w", encoding="utf-8") as f:
with open(self.FILENAME, "w", encoding="UTF-8") as f:
self.SETTINGS.write(f)
def clear(self): self.Clear()
@ -106,7 +106,7 @@ class IniSettings():
if self.SETTINGS is None: return None
self.SETTINGS.clear()
if self.FILENAME is not False:
with open(self.FILENAME, "w", encoding="utf-8") as f:
with open(self.FILENAME, "w", encoding="UTF-8") as f:
self.SETTINGS.write(f)
class Progress():
@ -292,16 +292,16 @@ def formatFileSize(size, asInt=False, roundUp=False):
val = size/1024
if roundUp: val = roundup(val)
if asInt:
return "{:d} KB".format(int(val))
return "{:d} KiB".format(int(val))
else:
return "{:.1f} KB".format(val)
return "{:.1f} KiB".format(val)
else:
val = size/1024/1024
if roundUp: val = roundup(val)
if asInt:
return "{:d} MB".format(int(val))
return "{:d} MiB".format(int(val))
else:
return "{:.2f} MB".format(val)
return "{:.2f} MiB".format(val)
def formatProgressTimeShort(sec):
sec = sec % (24 * 3600)
@ -505,7 +505,8 @@ def GetDumpReport(di, device):
"* Dump Time: {timestamp:s}\n" \
"* Time Elapsed: %TIME_ELAPSED% (%TRANSFER_RATE%)\n" \
"* Transfer Buffer: {buffer_size:d} bytes\n" \
.format(hardware=device.GetFullName(), firmware=device.GetFirmwareVersion(), software="{:s} {:s}".format(APPNAME, VERSION), platform=platform.platform(), buffer_size=di["transfer_size"], timestamp=di["timestamp"], baud_rate=device.GetBaudRate())
"* Retries: {retries:d}\n" \
.format(hardware=device.GetFullName(), firmware=device.GetFirmwareVersion(), software="{:s} {:s}".format(APPNAME, VERSION), platform=platform.platform(), buffer_size=di["transfer_size"], timestamp=di["timestamp"], baud_rate=device.GetBaudRate(), retries=device.GetReadErrors())
if mode == "DMG":
s += "" \
@ -587,9 +588,34 @@ def GetDumpReport(di, device):
"* Target Platform: {hdr_target_platform:s}\n" \
.format(hdr_game_title=game_title, hdr_game_code=game_code, hdr_revision=str(di["header"]["version"]), hdr_sgb=di["hdr_sgb"], hdr_cgb=di["hdr_cgb"], hdr_logo=di["hdr_logo"], hdr_header_checksum=di["hdr_header_checksum"], hdr_rom_checksum=di["hdr_rom_checksum"], hdr_rom_size=di["hdr_rom_size"], hdr_save_type=di["hdr_save_type"], hdr_mapper_type=di["hdr_mapper_type"], hdr_target_platform=di["hdr_target_platform"])
if "gbmem" in di and di["gbmem"] is not None:
s += "" \
"* Map Parameters: {gbmem:s}\n" \
.format(gbmem=''.join(format(x, '02X') for x in di["gbmem"][:0x18]))
raw_data = ""
for i in range(0, 4):
raw_data += ''.join(format(x, '02X') for x in di["gbmem"][i*0x20:i*0x20+0x20]) + "\n "
raw_data = raw_data[:-20]
if "gbmem_parsed" in di and len(di["gbmem_parsed"]) > 0:
s += "" \
"\n== GB-Memory Data ==\n" \
"* Game Code: {game_code:s}\n" \
"* Game Title: {title:s}\n" \
"* Write Timestamp: {timestamp:s}\n" \
"* Write Kiosk ID: {kiosk_id:s}\n" \
"* Write Counter: {write_count:d}\n" \
"* Cartridge Value: {cart_id:s}\n" \
"* Raw Map Data: {raw_data:s}\n" \
.format(
game_code=di["gbmem_parsed"]["game_code"],
title=di["gbmem_parsed"]["title"],
timestamp=di["gbmem_parsed"]["timestamp"],
kiosk_id=di["gbmem_parsed"]["kiosk_id"],
cart_id=di["gbmem_parsed"]["cart_id"],
write_count=di["gbmem_parsed"]["write_count"],
raw_data=raw_data
)
else:
s += "" \
"* GB-Memory Data: {:s}\n" \
.format(raw_data)
if mode == "AGB":
di["header"]["game_code_raw"] = di["header"]["game_code_raw"].replace("\0", "")
@ -717,4 +743,4 @@ def dprint(*args, **kwargs):
else:
global DEBUG_LOG
DEBUG_LOG.append(msg)
DEBUG_LOG = DEBUG_LOG[-10000:]
DEBUG_LOG = DEBUG_LOG[-16384:]

View File

@ -13761,6 +13761,13 @@
"st": 1,
"gc": "BGYJ"
},
"9314852f28ab60644e4c9ccf52414f193dbd68e7": {
"rc": 4094564777,
"rs": 16777216,
"gc": "BGYJ",
"ss": 8192,
"st": 2
},
"1470025dae9f864cdaeb904a3837dc89912c3ac3": {
"rc": 1963194139,
"rs": 8388608,
@ -20320,6 +20327,13 @@
"st": 3,
"gc": "V49J"
},
"902bca669e9f33ee1da1865c233cccbd8ac712db": {
"rc": 3284246738,
"rs": 4194304,
"ss": 0,
"st": 0,
"gc": "ZBBJ"
},
"feaba6b8d1ea2f584b7b522286c1c1e83f7355a4": {
"rc": 670544899,
"rs": 1048576,

View File

@ -19,6 +19,7 @@
"write_pin":"AUDIO",
"chip_erase_timeout":90,
"sector_size":0x10000,
"mbc":"manual",
"command_set":"AMD",
"commands":{
"reset":[

View File

@ -15,6 +15,7 @@
"write_pin":"WR",
"chip_erase_timeout":90,
"sector_size":0x10000,
"mbc":"manual",
"command_set":"AMD",
"commands":{
"reset":[

View File

@ -0,0 +1,91 @@
{
"type":"DMG",
"names":[
"DRV with AM29LV160DB and ALTERA CPLD"
],
"flash_ids":[
[ 0x02, 0x02, 0x4A, 0x4A ]
],
"voltage":3.3,
"flash_size":0x200000,
"start_addr":0x4000,
"first_bank":0,
"write_pin":"WR",
"sector_size_from_cfi":true,
"chip_erase_timeout":120,
"mbc":"manual",
"command_set":"AMD",
"commands":{
"reset":[
[ 0x4000, 0xF0 ]
],
"read_identifier":[
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ 0x4AAA, 0x90 ]
],
"read_cfi":[
[ 0x4AAA, 0x98 ]
],
"chip_erase":[
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ 0x4AAA, 0x80 ],
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ 0x4AAA, 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":[
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ 0x4AAA, 0x80 ],
[ 0x4AAA, 0xA9 ],
[ 0x4555, 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 ]
],
"buffer_write":[
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ "SA", 0x26 ],
[ "SA", "BS" ],
[ "PA", "PD" ],
[ "SA", 0x2A ]
],
"buffer_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ "SA", "PD", 0xFFFF ]
],
"single_write":[
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ 0x4AAA, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,51 @@
{
"type":"DMG",
"names":[
"DIY cart with HY29F800 @ AUDIO"
],
"voltage":5,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":120,
"mbc":"manual",
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x90 ]
],
"chip_erase":[
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x80 ],
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x10 ]
],
"chip_erase_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ 0, 0xFF, 0xFF ]
],
"single_write":[
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -1,34 +1,31 @@
{
"type":"DMG",
"names":[
"SD007_T40_64BALL_TSOP28 with 29LV016T"
],
"flash_ids":[
[ 0x04, 0xC7, 0x00, 0x00 ]
"DIY cart with HY29F800 @ WR"
],
"voltage":5,
"flash_size":0x200000,
"start_addr":0,
"first_bank":1,
"write_pin":"WR",
"chip_erase_timeout":60,
"chip_erase_timeout":120,
"mbc":"manual",
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x90 ]
],
"chip_erase":[
[ 0x555, 0xA9 ],
[ 0x2AA, 0x56 ],
[ 0x555, 0x80 ],
[ 0x555, 0xA9 ],
[ 0x2AA, 0x56 ],
[ 0x555, 0x10 ]
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x80 ],
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x10 ]
],
"chip_erase_wait_for":[
[ null, null, null ],
@ -39,9 +36,9 @@
[ 0, 0xFF, 0xFF ]
],
"single_write":[
[ 0x555, 0xA9 ],
[ 0x2AA, 0x56 ],
[ 0x555, 0xA0 ],
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[

View File

@ -3,11 +3,13 @@
"names":[
"SD007_T40_64BALL_TSOP28 with TC58FVB016FT-85",
"SD007_T40_64BALL_S71_TV_TS28 with TC58FVB016FT-85",
"SD007_T40_64BALL_SOJ28 with 29LV016T"
"SD007_T40_64BALL_SOJ28 with 29LV016T",
"SD007_T40_64BALL_TSOP28 with 29LV016T"
],
"flash_ids":[
[ 0x98, 0x45, 0x00, 0x2A ],
[ 0x98, 0x45, 0x00, 0x2A ],
[ 0x04, 0xC7, 0x00, 0x00 ],
[ 0x04, 0xC7, 0x00, 0x00 ]
],
"voltage":5,
@ -21,6 +23,11 @@
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x555, 0xA9 ],
[ 0x2AA, 0x56 ],
[ 0x555, 0x90 ]
],
"chip_erase":[
[ 0x555, 0xA9 ],
[ 0x2AA, 0x56 ],

View File

@ -2,7 +2,7 @@
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
import zipfile, os, serial, struct, time, re, math, platform
import zipfile, os, serial, struct, time, re, math
from .pyside import QtCore, QtWidgets, QtGui, QDesktopWidget
from . import Util
@ -20,6 +20,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
self.APP_PATH = app_path
self.DEVICE = device
self.PCB_VER = device.GetPCBVersion()
self.FW_VER = device.GetFirmwareVersion()
self.PORT = device.GetPort()
self.setWindowTitle("FlashGBX Firmware Updater for GBxCart RW")
@ -178,10 +179,10 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
return False
dev.write(b'0')
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.write(struct.pack(">BIBB", 0x2A, 0x37653565, 0x31, 0))
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
self.APP.QT_APP.processEvents()
time.sleep(0.3 + delay)
dev.reset_input_buffer()
@ -298,7 +299,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
if self.ResetAVR(delay) is False:
fncSetStatus(text="Status: Bootloader error.", enableUI=True)
self.prgStatus.setValue(0)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update failed as the device is not responding correctly. Please ensure you use a <a href=\"https://www.gbxcart.com/\">genuine GBxCart RW</a>, re-connect using a different USB cable and try again.\n\n⚠️ For safety reasons and to avoid potential fire hazards, do not use unauthorized clone hardware that have no electrical fuses, such as the “FLASH&nbsp;BOY” series.".replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX Firmware Updater for GBxCart RW {:s}".format(self.PCB_VER), text="Failed updating your GBxCart RW {:s} ({:s})!\n\nThe firmware update failed as the device is not responding correctly. Please ensure you use a <a href=\"https://www.gbxcart.com/\">genuine GBxCart RW</a>, re-connect using a different USB cable and try again.\n\n⚠️ For safety reasons and to avoid potential fire hazards, do not use this software with unauthorized clone hardware that has no electrical fuses, such as the “FLASH&nbsp;BOY” series.".format(self.PCB_VER, self.FW_VER).replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok)
answer = msgbox.exec()
return 2
@ -312,12 +313,12 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
dev.reset_output_buffer()
dev.write(b"@@@")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
buffer = dev.read(0x11)
if (len(buffer) < 0x11) or (buffer[0:3] != b'TSB'):
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.close()
self.APP.QT_APP.processEvents()
time.sleep(1)
@ -326,13 +327,13 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
fncSetStatus("Status: Waiting for bootloader... (+{:d}ms)".format(math.ceil(delay * 1000)))
if self.ResetAVR(delay) is False:
fncSetStatus(text="Status: Bootloader error.", enableUI=True)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW bootloader is not responding. If it doesnt work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX Firmware Updater for GBxCart RW {:s}".format(self.PCB_VER), text="Failed updating your GBxCart RW {:s} ({:s})!\n\nThe firmware update failed as the device is not responding correctly. Please ensure you use a <a href=\"https://www.gbxcart.com/\">genuine GBxCart RW</a>, re-connect using a different USB cable and try again.\n\n⚠️ For safety reasons and to avoid potential fire hazards, do not use this software with unauthorized clone hardware that has no electrical fuses, such as the “FLASH&nbsp;BOY” series.".format(self.PCB_VER, self.FW_VER).replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok)
answer = msgbox.exec()
return 2
lives -= 1
if lives < 0:
fncSetStatus(text="Status: Bootloader timeout.", enableUI=True)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW bootloader is not responding. If it doesnt work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX Firmware Updater for GBxCart RW {:s}".format(self.PCB_VER), text="Failed updating your GBxCart RW {:s} ({:s})!\n\nThe firmware update was not successful as the GBxCart RW bootloader is not responding. If it doesnt work even after multiple retries, please use the insideGadgets standalone firmware updater instead.".format(self.PCB_VER, self.FW_VER).replace("\n", "<br>"), standardButtons=QtWidgets.QMessageBox.Ok)
answer = msgbox.exec()
return 2
continue
@ -382,7 +383,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
dev.write(b"!")
dev.write(user_data)
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.read(0x41)
# Write firmware
@ -394,21 +395,21 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
lives = 10
dev.write(b"F")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
ret = dev.read(1)
while ret != b"?":
dev.write(b"F")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
ret = dev.read(1)
lives -= 1
if lives == 0:
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.close()
fncSetStatus(text="Status: Protocol Error. Please try again.", enableUI=True)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful (Protocol Error). Do you want to try again?\n\nIf it doesnt work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful (Protocol Error). Do you want to try again?\n\nIf it doesnt work even after multiple retries, please use the insideGadgets standalone firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
answer = msgbox.exec()
if answer == QtWidgets.QMessageBox.Yes:
time.sleep(1)
@ -424,10 +425,10 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
if (ret != b"?"):
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.close()
fncSetStatus(text="Status: Write Error ({:s}). Please try again.".format(str(ret)), enableUI=True)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful (Write Error, {:s}). Do you want to try again?\n\nIf it doesnt work even after multiple retries, you will have to use the official firmware updater to recover the firmware.".format(str(ret)), standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful (Write Error, {:s}). Do you want to try again?\n\nIf it doesnt work even after multiple retries, please use the insideGadgets standalone firmware updater instead.".format(str(ret)), standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
answer = msgbox.exec()
if answer == QtWidgets.QMessageBox.Yes:
time.sleep(1)
@ -435,7 +436,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
return 2
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.read(1)
# verify flash
@ -443,12 +444,12 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
buffer2 = bytearray()
dev.write(b"f")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
for i in range(0, 0x1DC0, 0x40):
self.APP.QT_APP.processEvents()
dev.write(b"!")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
while dev.in_waiting == 0: time.sleep(0.01)
ret = bytearray(dev.read(0x40))
buffer2 += ret
@ -465,9 +466,9 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
fncSetStatus(text="Status: Verification Error.", enableUI=True)
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.close()
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful (Verification Error). Do you want to try again?\n\nIf it doesnt work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful (Verification Error). Do you want to try again?\n\nIf it doesnt work even after multiple retries, please use the insideGadgets standalone firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
answer = msgbox.exec()
if answer == QtWidgets.QMessageBox.Yes:
time.sleep(1)
@ -479,25 +480,25 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
user_data[2] = 42
dev.write(b"C")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
ret = dev.read(1)
while ret != b"?":
dev.write(b"C")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
ret = dev.read(1)
lives -= 1
if lives == 0:
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.close()
fncSetStatus(text="Status: User data update error. Please try again.", enableUI=True)
return 2
dev.write(b"!")
dev.write(user_data)
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.read(0x41)
# Restart
@ -506,7 +507,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
fncSetStatus("Status: Restarting the device...")
dev.write(b"?")
dev.flush()
if platform.system() == "Darwin": time.sleep(0.00125)
time.sleep(0.00125)
dev.close()
self.APP.QT_APP.processEvents()
time.sleep(0.8)

View File

@ -114,9 +114,10 @@ class GbxDevice:
SKIPPING = False
BAUDRATE = 1000000
MAX_BUFFER_LEN = 0x2000
DEVICE_TIMEOUT = 1
DEVICE_TIMEOUT = 0.5
WRITE_DELAY = False
READ_ERRORS = 0
def __init__(self):
pass
@ -181,7 +182,7 @@ class GbxDevice:
#elif self.FW["fw_ver"] < self.DEVICE_MAX_FW:
# conn_msg.append([1, "The GBxCart RW device on port " + ports[i] + " is running an older firmware version. Please consider updating to version L" + str(self.DEVICE_MAX_FW) + " to make use of the latest features.<br><br>Firmware updates are available at <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>."])
elif self.FW["fw_ver"] > self.DEVICE_MAX_FW:
conn_msg.append([0, "NOTE: The GBxCart RW device on port " + ports[i] + " is running a firmware version that is newer than what this version of FlashGBX was developed to work with, so errors may occur."])
conn_msg.append([0, "Note: The GBxCart RW device on port " + ports[i] + " is running a firmware version that is newer than what this version of FlashGBX was developed to work with, so errors may occur."])
if (self.FW["pcb_ver"] not in (4, 5, 6, 101)): # only the v1.3, v1.4, v1.4a, Mini v1.1 PCB revisions are supported
dev.close()
@ -206,7 +207,7 @@ class GbxDevice:
conn_msg.append([3, "A critical error occured while trying to access the GBxCart RW device on port " + ports[i] + ".\n\n" + str(e)])
continue
#conn_msg.append([0, "NOTE: This is a third party tool for GBxCart RW by insideGadgets. Visit https://www.gbxcart.com/ for more information."])
#conn_msg.append([0, "Note: This is a third party tool for GBxCart RW by insideGadgets. Visit https://www.gbxcart.com/ for more information."])
return conn_msg
def LoadFirmwareVersion(self):
@ -403,6 +404,10 @@ class GbxDevice:
dprint("Setting Write Delay to", enable)
self.WRITE_DELAY = enable
def SetTimeout(self, seconds=0.5):
self.DEVICE_TIMEOUT = seconds
self.DEVICE.timeout = self.DEVICE_TIMEOUT
def wait_for_ack(self, values=None):
if values is None: values = [0x01, 0x03]
buffer = self._read(1)
@ -449,6 +454,7 @@ class GbxDevice:
buffer = self.DEVICE.read(count)
if len(buffer) != count:
dprint("Warning: Received {:d} byte(s) instead of the expected {:d} byte(s)".format(len(buffer), count))
self.READ_ERRORS += 1
while self.DEVICE.in_waiting > 0:
self.DEVICE.reset_input_buffer()
time.sleep(0.5)
@ -664,7 +670,7 @@ class GbxDevice:
elif self.MODE == "AGB":
if self.FW["pcb_ver"] in (5, 6, 101) and self.FW["fw_ver"] > 1:
self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=True)
header = self.ReadROM(0, 0x180)
if Util.DEBUG:
with open("debug_header.bin", "wb") as f: f.write(header)
@ -1800,6 +1806,9 @@ class GbxDevice:
def GetDumpReport(self):
return Util.GetDumpReport(self.INFO["dump_info"], self)
def GetReadErrors(self):
return self.READ_ERRORS
#################################################################
@ -2016,20 +2025,25 @@ class GbxDevice:
skip_init = True
if len(temp) != buffer_len:
pos_temp = pos_total
if "verify_write" in args:
pos_temp += args["verify_base_pos"]
self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
else:
self.SetProgress({"action":"UPDATE_POS", "pos":pos_total})
err_text = "Note: Incomplete transfer detected. Resuming from 0x{:X}...".format(pos_temp)
if (max_length >> 1) < 64:
dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}!".format(len(temp), buffer_len, pos_total))
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}.".format(buffer_len, pos_temp))
max_length = 64
elif lives > 18:
dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}!".format(len(temp), buffer_len, pos_total))
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}.".format(buffer_len, pos_temp))
else:
dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}! Decreasing maximum transfer buffer size to 0x{:X}.".format(len(temp), buffer_len, pos_total, max_length >> 1))
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}. Decreasing maximum transfer buffer size to 0x{:X}.".format(buffer_len, pos_temp, max_length >> 1))
max_length >>= 1
self.MAX_BUFFER_LEN = max_length
err_text += "\nBuffer size adjusted to {:d} bytes...".format(max_length)
if ".dev" in Util.VERSION_PEP440 and not Util.DEBUG: print(err_text)
self.INFO["dump_info"]["transfer_size"] = max_length
skip_init = False
@ -2079,15 +2093,19 @@ class GbxDevice:
if "verify_write" in args:
return min(pos_total, len(args["verify_write"]))
# Hidden sector (GB Memory)
if self.MODE == "DMG":
if len(args["path"]) > 0 and _mbc.HasHiddenSector():
file = open(os.path.splitext(args["path"])[0] + ".map", "wb")
temp = _mbc.ReadHiddenSector()
self.INFO["hidden_sector"] = temp
self.INFO["dump_info"]["gbmem"] = temp
file.write(temp)
file.close()
# Hidden sector (GB-Memory)
if self.MODE == "DMG" and len(args["path"]) > 0 and _mbc.HasHiddenSector():
file = open(os.path.splitext(args["path"])[0] + ".map", "wb")
temp = _mbc.ReadHiddenSector()
self.INFO["hidden_sector"] = temp
self.INFO["dump_info"]["gbmem"] = temp
self.INFO["dump_info"]["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=temp, buffer_rom=buffer)
file.write(temp)
file.close()
else:
if "hidden_sector" in self.INFO: del(self.INFO["hidden_sector"])
if "gbmem" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem"])
if "gbmem_parsed" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem_parsed"])
# Calculate Global Checksum
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
@ -2119,9 +2137,14 @@ class GbxDevice:
self.INFO["dump_info"]["agb_savelib"] = temp_ver
self.INFO["dump_info"]["agb_save_flash_id"] = None
if "FLASH" in temp_ver:
agb_save_flash_id = self.ReadFlashSaveID()
if agb_save_flash_id is not False and len(agb_save_flash_id) == 2:
self.INFO["dump_info"]["agb_save_flash_id"] = agb_save_flash_id
try:
agb_save_flash_id = self.ReadFlashSaveID()
if agb_save_flash_id is not False and len(agb_save_flash_id) == 2:
self.INFO["dump_info"]["agb_save_flash_id"] = agb_save_flash_id
except:
print("Error querying the flash save chip.")
self.DEVICE.reset_input_buffer()
self.DEVICE.reset_output_buffer()
self.INFO["rom_checksum_calc"] = chk
@ -2267,7 +2290,7 @@ class GbxDevice:
self._cart_write(0, 0x50)
self._cart_write(0, 0xFF)
if flash_id != bytearray([ 0xB0, 0x00, 0x9F, 0x00 ]):
print("WARNING: Unknown DACS flash chip ID ({:s})".format(' '.join(format(x, '02X') for x in flash_id)))
print("Warning: Unknown DACS flash chip ID ({:s})".format(' '.join(format(x, '02X') for x in flash_id)))
buffer_len = 0x2000
# ↓↓↓ Load commands into firmware
@ -2653,7 +2676,7 @@ class GbxDevice:
# Firmware check L2
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 3) and ("command_set" in cart_type and cart_type["command_set"] == "SHARP") and ("buffer_write" in cart_type["commands"]):
if self.FW["pcb_ver"] in (5, 6):
print("NOTE: Update your GBxCart RW firmware to version L3 or higher for a better transfer rate with this cartridge.")
print("Note: Update your GBxCart RW firmware to version L3 or higher for a better transfer rate with this cartridge.")
del(cart_type["commands"]["buffer_write"])
# Firmware check L2
# Firmware check L5
@ -2662,7 +2685,7 @@ class GbxDevice:
return False
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("double_die" in cart_type and cart_type["double_die"] is True):
if self.FW["pcb_ver"] in (5, 6):
print("NOTE: Update your GBxCart RW firmware to version L5 or higher for a better transfer rate with this cartridge.")
print("Note: Update your GBxCart RW firmware to version L5 or higher for a better transfer rate with this cartridge.")
del(cart_type["commands"]["buffer_write"])
# Firmware check L5
# Firmware check L8
@ -2681,28 +2704,6 @@ class GbxDevice:
if cart_type["command_set"] == "GBMEMORY":
flashcart = Flashcart_DMG_MMSA(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, progress_fncptr=self.SetProgress)
if "buffer_map" not in args:
if os.path.exists(os.path.splitext(args["path"])[0] + ".map"):
with open(os.path.splitext(args["path"])[0] + ".map", "rb") as file: args["buffer_map"] = file.read()
else:
temp = data_import
if len(temp) == 0: temp = bytearray([0xFF] * 0x180)
try:
gbmem = GBMemoryMap(rom=temp)
args["buffer_map"] = gbmem.GetMapData()
except:
print("{:s}An error occured while trying to generate the hidden sector data for the NP GB Memory cartridge.{:s}".format(ANSI.RED, ANSI.RESET))
if args["buffer_map"] is False:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The NP GB Memory Cartridge requires extra hidden sector data. As it couldnt be auto-generated, please provide your own at the following path: {:s}".format(os.path.splitext(args["path"])[0] + ".map"), "abortable":False})
return False
else:
dprint("Generated hidden sector data:", args["buffer_map"])
if Util.DEBUG:
with open("debug_mmsa_map.bin", "wb") as f: f.write(args["buffer_map"])
data_map_import = copy.copy(args["buffer_map"])
data_map_import = bytearray(data_map_import)
dprint("Hidden sector data loaded")
else:
flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress, set_we_pin_wr=self._set_we_pin_wr, set_we_pin_audio=self._set_we_pin_audio)
@ -2789,6 +2790,33 @@ class GbxDevice:
dprint("Pullups disabled")
# ↑↑↑ Flashcart configuration
# ↓↓↓ DMG-MMSA-JPN hidden sector
if self.MODE == "DMG" and _mbc.GetName() == "G-MMC1":
if "buffer_map" not in args:
if os.path.exists(os.path.splitext(args["path"])[0] + ".map"):
with open(os.path.splitext(args["path"])[0] + ".map", "rb") as file: args["buffer_map"] = file.read()
else:
temp = data_import
if len(temp) == 0: temp = bytearray([0xFF] * 0x180)
try:
gbmem = GBMemoryMap(rom=temp, oldmap=_mbc.ReadHiddenSector())
args["buffer_map"] = gbmem.GetMapData()
except Exception as e:
print("{:s}An error occured while trying to generate the hidden sector data for the NP GB-Memory cartridge.{:s}\n{:s}".format(ANSI.RED, ANSI.RESET, str(e)))
args["buffer_map"] = False
if args["buffer_map"] is False:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The NP GB-Memory Cartridge requires extra hidden sector data. As it couldnt be auto-generated, please provide your own at the following path: {:s}".format(os.path.splitext(args["path"])[0] + ".map"), "abortable":False})
return False
else:
dprint("Generated hidden sector data:", args["buffer_map"])
if Util.DEBUG:
with open("debug_mmsa_map.bin", "wb") as f: f.write(args["buffer_map"])
data_map_import = copy.copy(args["buffer_map"])
data_map_import = bytearray(data_map_import)
dprint("Hidden sector data loaded")
# ↑↑↑ DMG-MMSA-JPN hidden sector
# ↓↓↓ Load commands into firmware
flash_cmds = []
command_set_type = flashcart.GetCommandSetType()
@ -2810,7 +2838,7 @@ class GbxDevice:
dprint("Using Sharp/Intel command set")
elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
temp = 0x00
dprint("Using GB Memory command set")
dprint("Using GB-Memory command set")
elif command_set_type in ("BLAZE_XPLODER", "DATEL_ORBITV2"):
temp = 0x00
else:
@ -2824,7 +2852,7 @@ class GbxDevice:
if command_set_type == "GBMEMORY" and self.FW["pcb_ver"] not in (5, 6, 101):
self._set_fw_variable("FLASH_WE_PIN", 0x01)
dprint("Using GB Memory mode on GBxCart RW v1.3")
dprint("Using GB-Memory mode on GBxCart RW v1.3")
elif command_set_type == "DMG-MBC5-32M-FLASH":
self._set_fw_variable("FLASH_WE_PIN", 0x02)
else:
@ -2843,7 +2871,7 @@ class GbxDevice:
dprint("Using Flash2Advance mode with a buffer of {:d} bytes".format(flash_buffer_size))
elif command_set_type == "GBMEMORY" and self.FW["pcb_ver"] in (5, 6, 101):
self._write(0x03) # FLASH_METHOD_DMG_MMSA
dprint("Using GB Memory mode on GBxCart RW v1.4")
dprint("Using GB-Memory mode on GBxCart RW v1.4")
elif flashcart.SupportsBufferWrite() and flash_buffer_size > 0:
self._write(0x02) # FLASH_METHOD_BUFFERED
flash_cmds = flashcart.GetCommands("buffer_write")
@ -2926,7 +2954,7 @@ class GbxDevice:
if "flash_ids" in cart_type:
(verified, flash_id) = flashcart.VerifyFlashID()
if not verified:
print("NOTE: This cartridges Flash ID ({:s}) doesnt match the cartridge type selection.".format(' '.join(format(x, '02X') for x in flash_id)))
print("Note: This cartridges Flash ID ({:s}) doesnt match the cartridge type selection.".format(' '.join(format(x, '02X') for x in flash_id)))
# ↑↑↑ Read Flash ID
# ↓↓↓ Read Sector Map
@ -3247,6 +3275,7 @@ class GbxDevice:
if sector[0] >= len(data_import): break
verify_args = copy.copy(args)
verify_args.update({"verify_write":data_import[sector[0]:sector[0]+sector[1]], "rom_size":len(data_import), "verify_from":sector[0], "path":"", "rtc_area":flashcart.HasRTC(), "verify_mbc":_mbc})
verify_args["verify_base_pos"] = sector[0]
verify_args["verify_len"] = len(verify_args["verify_write"])
verify_args["rom_size"] = len(verify_args["verify_write"])
self.SetProgress({"action":"UPDATE_POS", "pos":sector[0]})
@ -3298,6 +3327,7 @@ class GbxDevice:
self.ERROR = False
self.CANCEL = False
self.CANCEL_ARGS = {}
self.READ_ERRORS = 0
if self.IsConnected():
if self.FW["pcb_ver"] in (5, 6, 101):
self._write(self.DEVICE_CMD["OFW_CART_MODE"])

View File

@ -140,6 +140,7 @@ class GbxDevice:
FAST_READ = False
BAUDRATE = 1000000
WRITE_DELAY = False
READ_ERRORS = 0
def __init__(self):
pass
@ -223,7 +224,7 @@ class GbxDevice:
conn_msg.append([0, "{:s}Now running in Legacy Mode. You can install the optimized firmware version L1 in GUI mode from the “Tools” menu.{:s}".format(ANSI.YELLOW, ANSI.RESET)])
self.PORT = ports[i]
self.DEVICE.timeout = 1
self.DEVICE.timeout = 0.5
# Load Flash Cartridge Handlers
self.UpdateFlashCarts(flashcarts)
@ -414,6 +415,9 @@ class GbxDevice:
def SetWriteDelay(self, enable=True):
self.WRITE_DELAY = enable
def SetTimeout(self, seconds=0.5):
self.DEVICE.timeout = seconds
def wait_for_ack(self):
buffer = self.read(1)
if buffer == False:
@ -452,6 +456,7 @@ class GbxDevice:
if self.DEVICE.in_waiting > 1000: dprint("Recv buffer used: {:d} bytes".format(self.DEVICE.in_waiting))
buffer = self.DEVICE.read(readlen)
if len(buffer) != readlen:
self.READ_ERRORS += 1
dprint("Received {:d} byte(s) instead of the expected {:d} bytes during iteration {:d}.".format(len(buffer), readlen, i))
self.write('0') # end
time.sleep(0.5)
@ -1214,7 +1219,10 @@ class GbxDevice:
def GetDumpReport(self):
return Util.GetDumpReport(self.INFO["dump_info"], self)
def GetReadErrors(self):
return self.READ_ERRORS
#################################################################
def BackupROM(self, fncSetProgress=None, args=None):
@ -1284,7 +1292,7 @@ class GbxDevice:
# Firmware check R26+
# Firmware check CFW
if ("agb_rom_size" in args and args["agb_rom_size"] > 32 * 1024 * 1024) or (self.MODE == "DMG" and "mbc" in args and not self.IsSupportedMbc(args["mbc"])) or (self.MODE == "AGB" and "dacs_8m" in self.INFO and self.INFO["dacs_8m"] is True): # 3D Memory
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge is currently not supported by {:s} using the current firmware version of the {:s} device. Please try a newer firmware version.".format(APPNAME, self.GetFullName()), "abortable":False})
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge is currently not supported by {:s} using the current firmware version of the {:s} device. Please try the dedicated insideGadgets software programs, try updating the firmware or upgrade to a new hardware revision.".format(APPNAME, self.GetFullName()), "abortable":False})
return False
# Firmware check CFW

Binary file not shown.

View File

@ -105,6 +105,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- DIY cart with AM29F040
- DIY cart with AM29F080
- DIY cart with AT49F040
- DIY cart with HY29F800
- DIY cart with M29F032D
- DIY cart with MBM29F033C
- DIY cart with MX29LV640
@ -155,6 +156,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- 36VF3204 and ALTERA CPLD (no PCB text)
- DMG-DHCN-20 with MX29LV320ET
- DMG-GBRW-20 with 29LV320ETMI-70G
- DRV with AM29LV160DB and ALTERA CPLD
- ES29LV160_DRV with 29DL32TF-70
- GB-M968 with 29LV160DB
- GB-M968 with M29W160EB
@ -304,6 +306,7 @@ The author would like to thank the following very kind people for their help and
- antPL (flash chip info)
- bbsan (flash chip info)
- BennVenn (unlicensed mapper reverse engineering)
- ccs21 (flash chip info)
- ClassicOldSong (bug reports)
- crizzlycruz (flash chip info)
- Därk (flash chip info)
@ -341,6 +344,7 @@ The author would like to thank the following very kind people for their help and
- joyrider3774 (flash chip info)
- JS7457 (flash chip info)
- julgr (macOS help, testing)
- Kaede (flash chip info)
- kscheel (bug reports)
- kyokohunter (bug reports)
- litlemoran (flash chip info)

View File

@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
setuptools.setup(
name="FlashGBX",
version="3.21",
version="3.22",
author="Lesserkuma",
description="Reads and writes Game Boy and Game Boy Advance cartridge data. Supported hardware: GBxCart RW v1.3 and v1.4 by insideGadgets.",
url="https://github.com/lesserkuma/FlashGBX",