This commit is contained in:
Lesserkuma 2022-03-30 21:27:13 +02:00
parent b4d6a40077
commit 0deedd5956
29 changed files with 21049 additions and 20189 deletions

View File

@ -1,4 +1,19 @@
# Release notes
### v3.7 (released 2022-03-30)
- 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
- 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
- Added support for AGB-E05-01 with S29GL064 *(thanks DevDavisNunez)*
- Added support for DIY carts with MBC1 and AM29F010 @ AUDIO *(thanks JS7457)*
- Added support for DIY carts with MBC1 and AM29F040 @ AUDIO *(thanks JS7457)*
- Added support for DIY carts with MBC1 and AM29F080 @ AUDIO *(thanks Timville)*
- Added support for DIY carts with MBC1 and AT49F040 @ AUDIO *(thanks Timville)*
- Added support for DIY carts with MBC1 and SST39SF040 @ AUDIO *(thanks Timville)*
- Added support for B100 with MX29LV640ET *(thanks Mr_V)*
- Added support for B54 with MX29LV320ET *(thanks Mr_V)*
### v3.6 (released 2022-03-09)
- When opening Game Boy Camera Album Viewer manually, the save data will now automatically be loaded if a Game Boy Camera or Pocket Camera cartridge is connected
- Fixed a bug with the insideGadgets 4 MB (S29GL032M) cartridge *(thanks frarees)*
@ -6,7 +21,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
- 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)*
@ -38,7 +53,7 @@
- Extracted Game Boy Camera pictures can now have a frame around them; this frame can be customized by placing a file called `pc_frame.png` (must be at least 160×144 pixels) into the configuration directory
### v3.2 (released 2021-12-18)
- Fixed the configuration files for DIY cart with AM29F016/AM29F016B *(thanks dyf2007)*
- Fixed the configuration files for DIY carts with AM29F016/AM29F016B *(thanks dyf2007)*
- Added support for SD008-6810-V5 with MX29CL256FH (multigame cartridge with 32 MB of ROM and 512 KB of save data)
- Added support for SD008-6810-V4 with MX29GL256EL *(thanks LucentW)*
- Added support for the [HDR Game Boy Camera Flashcart](https://github.com/HDR/Gameboy-Camera-Flashcart) (select manually if auto-detected option doesnt work)
@ -104,7 +119,7 @@
### v2.3 (released 2021-06-08)
- Added support for AGB-E05-06L with 29LV128DBT2C-90Q *(thanks marv17)*
- Nintendo Power GB Memory Cartridges 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)*
@ -124,11 +139,11 @@
- Added support for SD007_TSOP_48BALL_V10 with M29W320DT *(thanks Jayro)*
- Fixed a problem of reading from a certain type of cartridge that uses the GL256S flash chip *(thanks marv17)*
- Added support for B11 with 26L6420MC-90 *(thanks dyf2007)*
- Added support for a DIY cart with MBC3 and MX29LV640 *(thanks eveningmoose)*
- Added support for DIY carts with MBC3 and MX29LV640 *(thanks eveningmoose)*
### v2.1 (released 2021-05-05)
- Fixed support for SD007_TSOP_29LV017D with L017D70VC *(thanks marv17 and 90sFlav)*
- Added support for a DIY cart with MBC1 and AM29F080 *(thanks skite2001)*
- Added support for DIY carts with MBC1 and AM29F080 *(thanks skite2001)*
- Added support for SD007_TSOP_48BALL_V8 with 29LV320CTTC *(thanks Jayro)*
- Added the MBC5+SRAM mapper type which is officially unused, but is actually used by GB Studio *(thanks Jayro)*
- The GBxCart RW v1.3 firmware updater should now also work if the device is below firmware version R19
@ -170,7 +185,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)
@ -276,7 +291,7 @@
- Renamed config.ini to settings.ini to avoid confusion with the term “config file”
### v0.8β (released 2020-10-03)
- Added support for the DIY cart with AM29F016/AM29F016B with AUDIO as WE *(thanks AndehX)*
- Added support for DIY carts with AM29F016/AM29F016B with AUDIO as WE *(thanks AndehX)*
- Renamed `VIN` to `AUDIO` in config files and the command line switch `--resetconfig` to `--reset`
- Added experimental support for GBxCart RW revisions other than v1.3 and fixed a crash when connecting to unknown revisions of the GBxCart RW
- The app is now available as a package and can be installed directly through *pip* *(thanks JFox)*

View File

@ -128,7 +128,7 @@ def main(portableMode=False):
ap_cli2.add_argument("--dmg-mbc", choices=["auto", "1", "2", "3", "5", "6", "7"], type=str.lower, default="auto", help="set memory bank controller type of Game Boy cartridge")
ap_cli2.add_argument("--dmg-savesize", choices=["auto", "4k", "16k", "64k", "256k", "512k", "1m", "eeprom2k", "eeprom4k", "tama5", "4m"], type=str.lower, default="auto", help="set size of Game Boy cartridge save data")
ap_cli2.add_argument("--agb-romsize", choices=["auto", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb"], type=str.lower, default="auto", help="set size of Game Boy Advance cartridge ROM data")
ap_cli2.add_argument("--agb-savetype", choices=["auto", "eeprom4k", "eeprom64k", "sram256k", "sram512k", "sram1m", "flash512k", "flash1m", "dacs8m"], type=str.lower, default="auto", help="set type of Game Boy cartridge save data")
ap_cli2.add_argument("--agb-savetype", choices=["auto", "eeprom4k", "eeprom64k", "sram256k", "flash512k", "flash1m", "dacs8m", "sram512k", "sram1m"], type=str.lower, default="auto", help="set type of Game Boy Advance cartridge save data")
ap_cli2.add_argument("--store-rtc", action="store_true", help="store RTC register values if supported")
ap_cli2.add_argument("--ignore-bad-header", action="store_true", help="dont stop if invalid data found in cartridge header data")
#ap_cli2.add_argument("--fast-read-mode", action="store_true", help="enable experimental fast read mode for GBxCart RW v1.3")

View File

@ -87,7 +87,7 @@ class FlashGBX_CLI():
dev = self.DEVICE[1]
builddate = dev.GetFWBuildDate()
if builddate != "":
print("\nConnected to {:s} (dated {:s})".format(dev.GetFullNameExtended(), builddate))
print("\nConnected to {:s}".format(dev.GetFullNameExtended(more=True)))
else:
print("\nConnected to {:s}".format(dev.GetFullNameExtended()))
@ -482,7 +482,7 @@ class FlashGBX_CLI():
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["features_raw"]):
print("{:s}\nWARNING: This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates.{:s}".format(ANSI.YELLOW, APPNAME, self.CONN.GetFullName(), ANSI.RESET))
if data['logo_correct'] and data['game_title'] == "NP M-MENU MENU" and self.ARGS["argparsed"].flashcart_type == "autodetect":
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU ") and self.ARGS["argparsed"].flashcart_type == "autodetect":
cart_types = self.CONN.GetSupportedCartridgesDMG()
for i in range(0, len(cart_types[0])):
if "DMG-MMSA-JPN" in cart_types[0][i]:
@ -577,8 +577,8 @@ class FlashGBX_CLI():
data["save_type"] = db_agb_entry['st']
if data["dacs_8m"] is True:
stok = True
s += "Save Type: {:s}\n".format(Util.AGB_Header_Save_Types[8])
data["save_type"] = 8
s += "Save Type: {:s}\n".format(Util.AGB_Header_Save_Types[6])
data["save_type"] = 6
if stok is False:
s += "Save Type: Not detected\n"
@ -620,7 +620,6 @@ class FlashGBX_CLI():
if len(cart_types) > 0:
cart_type = cart_type_id
#self.STATUS["cart_type"] = supp_cart_types[1][cart_type]
for i in range(0, len(cart_types)):
if cart_types[i] == cart_type_id:
msg_cart_type += "- {:s}*\n".format(supp_cart_types[0][cart_types[i]])
@ -671,6 +670,8 @@ class FlashGBX_CLI():
elif ("[WR / AAA/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/AAA/A9)” a try."
elif ("[WR / 555/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/555/AA)” a try."
elif ("[WR / 555/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/555/A9)” a try."
elif ("[AUDIO/ AAA/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AUDIO/AAA/AA)” a try."
elif ("[AUDIO/ 555/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AUDIO/555/AA)” a try."
msg_cart_type_s += "\n"
else:
msg_cart_type_s = "Cartridge Type: Generic ROM Cartridge (not rewritable or not auto-detectable)\n"
@ -691,7 +692,6 @@ class FlashGBX_CLI():
def BackupROM(self, args, header):
mbc = 1
rom_size = 0
#fast_read_mode = args.fast_read_mode is True
if self.CONN.GetMode() == "DMG":
if args.dmg_mbc == "auto":
@ -711,11 +711,9 @@ class FlashGBX_CLI():
if args.dmg_romsize == "auto":
try:
#rom_banks = Util.DMG_Header_ROM_Sizes_Flasher_Map[header["rom_size_raw"]]
rom_size = Util.DMG_Header_ROM_Sizes_Flasher_Map[header["rom_size_raw"]]
except:
print("{:s}Couldnt determine ROM size, will use 8 MB. It can also be manually set with the “--dmg-romsize” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
#rom_banks = 512
rom_size = 8 * 1024 * 1024
else:
sizes = [ "auto", "32kb", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb" ]
@ -766,9 +764,12 @@ class FlashGBX_CLI():
print("{:s}Couldnt access “{:s}”.{:s}".format(ANSI.RED, path, ANSI.RESET))
return
#if fast_read_mode: print("Fast Read Mode enabled.")
s_mbc = ""
if self.CONN.GetMode() == "DMG": s_mbc = " using Mapper Type 0x{:X}".format(mbc)
if self.CONN.GetMode() == "DMG":
if mbc in Util.DMG_Header_Mapper:
s_mbc = " using Mapper Type “{:s}".format(Util.DMG_Header_Mapper[mbc])
else:
s_mbc = " using Mapper Type 0x{:X}".format(mbc)
if self.CONN.GetMode() == "DMG":
print("The ROM will now be read{:s} and saved to “{:s}”.".format(s_mbc, os.path.abspath(path)))
else:
@ -788,7 +789,6 @@ class FlashGBX_CLI():
if carts[i]["type"] != self.CONN.GetMode(): continue
if args.flashcart_type in carts[i]["names"]:
print("Selected flash cartridge type: {:s}".format(args.flashcart_type))
#rom_banks = int(carts[i]["flash_size"] / 0x4000)
rom_size = carts[i]["flash_size"]
cart_type = i
break
@ -815,7 +815,6 @@ class FlashGBX_CLI():
break
if cart_type <= 0 and args.flashcart_type == "autodetect":
#cart_type = self.DetectCartridge(limitVoltage=not args.force_5v)
cart_type = self.DetectCartridge()
if cart_type is None: cart_type = 0
if cart_type == 0:
@ -875,7 +874,6 @@ class FlashGBX_CLI():
if not prefer_chip_erase and 'chip_erase' in carts[cart_type]['commands'] and 'sector_erase' in carts[cart_type]['commands']:
print("This flash cartridge supports both Sector Erase and Full Chip Erase methods. You can use the “--prefer-chip-erase” command line switch if necessary.")
#fast_read_mode = args.fast_read_mode is True
verify_write = args.no_verify_write is False
fix_header = False
@ -898,7 +896,6 @@ class FlashGBX_CLI():
return
print("")
#if fast_read_mode: print("Fast Read Mode enabled for flash verification.")
v = carts[cart_type]["voltage"]
if override_voltage: v = override_voltage
print("The following ROM file will now be written to the flash cartridge at {:s}V:\n{:s}".format(str(v), os.path.abspath(path)))
@ -958,7 +955,7 @@ class FlashGBX_CLI():
if args.agb_savetype == "auto":
save_type = header["save_type"]
else:
sizes = [ "auto", "eeprom4k", "eeprom64k", "sram256k", "sram512k", "sram1m", "flash512k", "flash1m", "dacs8m" ]
sizes = [ "auto", "eeprom4k", "eeprom64k", "sram256k", "flash512k", "flash1m", "dacs8m", "sram512k", "sram1m" ]
save_type = sizes.index(args.agb_savetype)
path = header["game_title"].strip().encode('ascii', 'ignore').decode('ascii')
@ -987,7 +984,11 @@ class FlashGBX_CLI():
if (path == ""): return
s_mbc = ""
if self.CONN.GetMode() == "DMG": s_mbc = " using Mapper Type 0x{:X}".format(mbc)
if self.CONN.GetMode() == "DMG":
if mbc in Util.DMG_Header_Mapper:
s_mbc = " using Mapper Type “{:s}".format(Util.DMG_Header_Mapper[mbc])
else:
s_mbc = " using Mapper Type 0x{:X}".format(mbc)
if args.action == "backup-save":
if not args.overwrite and os.path.exists(os.path.abspath(path)):
answer = input("The target file “{:s}” already exists.\nDo you want to overwrite it? [y/N]: ".format(os.path.abspath(path))).strip().lower()
@ -1040,8 +1041,6 @@ class FlashGBX_CLI():
self.CONN.TransferData(args={ 'mode':3, 'path':path, 'mbc':mbc, 'save_type':save_type, 'erase':True, 'rtc':rtc }, signal=self.PROGRESS.SetProgress)
elif args.action == "debug-test-save": # debug
self.ARGS["debug"] = True
#self.CONN.DetectCartridge()
#return
print("Making a backup of the original save data.")
ret = self.CONN.TransferData(args={ 'mode':2, 'path':self.CONFIG_PATH + "/test1.bin", 'mbc':mbc, 'save_type':save_type }, signal=self.PROGRESS.SetProgress)
@ -1056,10 +1055,12 @@ 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()
if self.CONN.CanPowerCycleCart():
print("\nPower cycling.")
self.CONN.CartPowerOff()
time.sleep(1)
self.CONN.CartPowerOn()
self.CONN.ReadInfo(checkRtc=False)
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)
@ -1069,6 +1070,11 @@ class FlashGBX_CLI():
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 mbc == 6:
for i in range(0, len(test2)):
test2[i] &= 0x0F
test3[i] &= 0x0F
if test2 != test4:
diffcount = 0
for i in range(0, len(test2)):
@ -1080,11 +1086,6 @@ class FlashGBX_CLI():
if test3[i] != test4[i]: diffcount += 1
print("\n{:s}Differences found between two consecutive readbacks: {:d}{:s}".format(ANSI.RED, diffcount, ANSI.RESET))
if mbc == 6:
for i in range(0, len(test2)):
test2[i] &= 0x0F
test3[i] &= 0x0F
found_offset = test2.find(test3[0:512])
if found_offset < 0:
if self.CONN.GetMode() == "AGB":
@ -1150,9 +1151,8 @@ class FlashGBX_CLI():
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("Available firmware version:\n{:s}\n".format("{:s} ({: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 {:s} device.\n2. On the circuit board of your GBxCart RW {:s}, 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.".format(device_name, device_name))
if len(input("").strip()) != 0:
print("Canceled.")

View File

@ -189,14 +189,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if platform.system() == "Darwin": btnWidth += 12
self.btnConfig.setMaximumWidth(btnWidth)
#self.mnuDevice = QtWidgets.QMenu("&Device options")
self.mnuConfig = QtWidgets.QMenu()
self.mnuConfig.addAction("Check for &updates at application startup", lambda: [ self.SETTINGS.setValue("UpdateCheck", str(self.mnuConfig.actions()[0].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")), self.UpdateCheck() ])
self.mnuConfig.addAction("&Append date && time to filename of save data backups", lambda: self.SETTINGS.setValue("SaveFileNameAddDateTime", str(self.mnuConfig.actions()[1].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("Prefer full &chip erase over sector erase when both available", lambda: self.SETTINGS.setValue("PreferChipErase", str(self.mnuConfig.actions()[2].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("&Verify data after writing", lambda: self.SETTINGS.setValue("VerifyWrittenData", str(self.mnuConfig.actions()[3].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
#self.mnuConfig.addAction("Use &fast read mode", lambda: self.SETTINGS.setValue("FastReadMode", str(self.mnuConfig.actions()[4].isChecked()).lower().replace("true", "enabled").replace("false", "disabled"))) # GBxCart RW
self.mnuConfig.addAction("&Limit voltage to 3.3V when detecting Game Boy flash cartridges", lambda: self.SETTINGS.setValue("AutoDetectLimitVoltage", str(self.mnuConfig.actions()[4].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addSeparator()
self.mnuConfig.addAction("Re-&enable suppressed messages", self.ReEnableMessages)
@ -206,13 +203,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.mnuConfig.actions()[1].setCheckable(True)
self.mnuConfig.actions()[2].setCheckable(True)
self.mnuConfig.actions()[3].setCheckable(True)
#self.mnuConfig.actions()[4].setCheckable(True) # GBxCart RW
self.mnuConfig.actions()[4].setCheckable(True)
self.mnuConfig.actions()[0].setChecked(self.SETTINGS.value("UpdateCheck") == "enabled")
self.mnuConfig.actions()[1].setChecked(self.SETTINGS.value("SaveFileNameAddDateTime", default="disabled") == "enabled")
self.mnuConfig.actions()[2].setChecked(self.SETTINGS.value("PreferChipErase", default="disabled") == "enabled")
self.mnuConfig.actions()[3].setChecked(self.SETTINGS.value("VerifyWrittenData", default="enabled") == "enabled")
#self.mnuConfig.actions()[4].setChecked(self.SETTINGS.value("FastReadMode", default="disabled") == "enabled") # GBxCart RW
self.mnuConfig.actions()[4].setChecked(self.SETTINGS.value("AutoDetectLimitVoltage", default="disabled") == "enabled")
self.btnConfig.setMenu(self.mnuConfig)
@ -280,14 +275,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
rowHeaderRevision.addWidget(self.lblHeaderRevisionResult)
group_layout.addLayout(rowHeaderRevision)
#rowHeaderGB = QtWidgets.QHBoxLayout()
#lblHeaderGB = QtWidgets.QLabel("Target Platform:")
#lblHeaderGB.setContentsMargins(0, 1, 0, 1)
#rowHeaderGB.addWidget(lblHeaderGB)
#self.lblHeaderGBResult = QtWidgets.QLabel("")
#rowHeaderGB.addWidget(self.lblHeaderGBResult)
#group_layout.addLayout(rowHeaderGB)
rowHeaderRtc = QtWidgets.QHBoxLayout()
lblHeaderRtc = QtWidgets.QLabel("Real Time Clock:")
lblHeaderRtc.setContentsMargins(0, 1, 0, 1)
@ -393,14 +380,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
rowAGBHeaderRevision.addWidget(self.lblAGBHeaderRevisionResult)
group_layout.addLayout(rowAGBHeaderRevision)
#rowAGBHeader96h = QtWidgets.QHBoxLayout()
#lblAGBHeader96h = QtWidgets.QLabel("Cartridge Identifier:")
#lblAGBHeader96h.setContentsMargins(0, 1, 0, 1)
#rowAGBHeader96h.addWidget(lblAGBHeader96h)
self.lblAGBHeader96hResult = QtWidgets.QLabel("")
#rowAGBHeader96h.addWidget(self.lblAGBHeader96hResult)
#group_layout.addLayout(rowAGBHeader96h)
rowAGBGpioRtc = QtWidgets.QHBoxLayout()
lblAGBGpioRtc = QtWidgets.QLabel("Real Time Clock:")
lblAGBGpioRtc.setContentsMargins(0, 1, 0, 1)
@ -557,7 +536,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.btnRestoreRAM.setEnabled(False)
self.btnConnect.setText("&Connect")
self.lblDevice.setText("Disconnected.")
#self.mnuTools.actions()[2].setEnabled(False)
def ReEnableMessages(self):
self.SETTINGS.setValue("AutoReconnect", "disabled")
@ -756,13 +734,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.btnConnect.setEnabled(True)
if len(self.DEVICES) == 0: return False
return True
def AbortOperation(self):
self.CONN.CANCEL = True
self.CONN.ERROR = False
self.lblStatus4a.setText("Stopping... Please wait.")
self.lblStatus4aResult.setText("")
def FinishOperation(self):
if self.lblStatus2aResult.text() == "Pending...": self.lblStatus2aResult.setText("")
self.lblStatus4aResult.setText("")
@ -1013,9 +992,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
def FlashROM(self, dpath=""):
if not self.CheckDeviceAlive(): return
#if "cart_type" in self.STATUS and "dmg-mmsa-jpn" in self.STATUS["cart_type"]:
# self.ShowGBMemoryWindow()
# return
just_erase = False
path = ""
@ -1081,11 +1057,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
msg += " You can still give it a try, but its possible that its too large which may cause the ROM writing to fail."
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
if answer == QtWidgets.QMessageBox.Cancel: return
override_voltage = False
if 'voltage_variants' in carts[cart_type] and carts[cart_type]['voltage'] == 3.3:
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="The selected flash cartridge type usually flashes fine with 3.3V, however sometimes it may require 5V. Which mode should be used?")
button_3_3v = msgbox.addButton(" Use &3.3V (safer) ", QtWidgets.QMessageBox.ActionRole)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Some cartridges of this type can be flashed at 3.3V, others may require 5V. Which mode should be used?")
button_3_3v = msgbox.addButton("Use &3.3V", QtWidgets.QMessageBox.ActionRole)
button_5v = msgbox.addButton("Use &5V", QtWidgets.QMessageBox.ActionRole)
button_cancel = msgbox.addButton("&Cancel", QtWidgets.QMessageBox.RejectRole)
msgbox.setDefaultButton(button_3_3v)
@ -1114,12 +1090,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
else:
prefer_chip_erase = False
#fast_read_mode = self.SETTINGS.value("FastReadMode", default="disabled")
#if fast_read_mode and fast_read_mode.lower() == "enabled":
# fast_read_mode = True
#else:
# fast_read_mode = False
verify_write = self.SETTINGS.value("VerifyWrittenData", default="enabled")
if verify_write and verify_write.lower() == "enabled":
verify_write = True
@ -1439,7 +1409,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.btnRestoreRAM.setEnabled(True)
self.btnHeaderRefresh.setFocus()
self.SetProgressBars(min=0, max=100, value=0)
#if "has_rtc" in data and data["has_rtc"] is True: print("Real Time Clock cartridge detected.")
self.lblStatus4a.setText("Ready.")
qt_app.processEvents()
@ -1556,7 +1525,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.lblHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["features_raw"]) and resetStatus:
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates in the Tools menu or the makers website.".format(APPNAME, self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
if data['logo_correct'] and data['game_title'] == "NP M-MENU MENU":
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU "):
cart_types = self.CONN.GetSupportedCartridgesDMG()
for i in range(0, len(cart_types[0])):
if "dmg-mmsa-jpn" in cart_types[1][i]:
@ -1582,13 +1551,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.lblAGBHeaderLogoValidResult.setText("Invalid")
self.lblAGBHeaderLogoValidResult.setStyleSheet("QLabel { color: red; }")
if data['96h_correct']:
self.lblAGBHeader96hResult.setText("OK")
self.lblAGBHeader96hResult.setStyleSheet(self.lblAGBHeaderCodeResult.styleSheet())
else:
self.lblAGBHeader96hResult.setText("Invalid")
self.lblAGBHeader96hResult.setStyleSheet("QLabel { color: red; }")
if data['has_rtc']:
if 'rtc_buffer' in data:
try:
@ -1657,7 +1619,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if db_agb_entry['st'] < len(Util.AGB_Header_Save_Types):
self.cmbAGBSaveTypeResult.setCurrentIndex(db_agb_entry['st'])
if data["dacs_8m"] is True:
self.cmbAGBSaveTypeResult.setCurrentIndex(8)
self.cmbAGBSaveTypeResult.setCurrentIndex(6)
if data['empty'] == True: # defaults
if data['empty_nocart'] == True:
@ -1704,7 +1666,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.lblStatus4a.setText("Detecting Cartridge...")
self.SetProgressBars(min=0, max=0, value=1)
qt_app.processEvents()
#self.ReadCartridge(resetStatus=False)
limitVoltage = str(self.SETTINGS.value("AutoDetectLimitVoltage", default="disabled")).lower() == "enabled"
ret = self.CONN.DetectCartridge(limitVoltage=limitVoltage, checkSaveType=not canSkipMessage)
@ -1733,7 +1694,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
if len(cart_types) > 0:
cart_type = cart_type_id
#if (cart_type == 1): cart_type = 0
if self.CONN.GetMode() == "DMG":
self.cmbDMGCartridgeTypeResult.setCurrentIndex(cart_type)
elif self.CONN.GetMode() == "AGB":
@ -1746,7 +1706,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
else:
msg_cart_type += "- {:s}<br>".format(supp_cart_types[0][cart_types[i]])
msg_cart_type = msg_cart_type[:-4]
#if msg_cart_type == "": msg_cart_type = "{:s}<br>".format(supp_cart_types[0][0])
except:
pass
@ -1779,11 +1738,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
msg_flash_size_s = ""
msg_flash_id_s = ""
msg_cfi_s = ""
try_this = None
found_supported = False
is_generic = False
if cart_type is not None:
#(flash_id, cfi_s, _) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage, cart_type=supp_cart_types[1][cart_type])
#msg_cart_type_s = "<b>Cartridge Type:</b> Supported flash cartridge type (will be auto-selected)<br>"
if len(cart_types) > 1:
msg_cart_type_s = "<b>Cartridge Type:</b> {:s} (or compatible)<br>".format(msg_cart_type_used)
else:
@ -1796,16 +1754,28 @@ class FlashGBX_GUI(QtWidgets.QWidget):
msg_flash_size_s = "<b>ROM Size:</b> {:s}<br>".format(Util.formatFileSize(size, asInt=True))
else:
#(flash_id, cfi_s, _) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage)
if (len(flash_id.split("\n")) > 2) and ((self.CONN.GetMode() == "DMG") or ("dacs_8m" in header and header["dacs_8m"] is not True)):
msg_cart_type_s = "<b>Cartridge Type:</b> Unknown flash cartridge Please submit the displayed information along with a picture of the cartridges circuit board."
if ("[ 0/90]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (0/90)” a try."
elif ("[ AAA/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AAA/AA)” a try."
elif ("[ AAA/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AAA/A9)” a try."
elif ("[WR / AAA/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/AAA/AA)” a try."
elif ("[WR / AAA/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/AAA/A9)” a try."
elif ("[WR / 555/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/555/AA)” a try."
elif ("[WR / 555/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/555/A9)” a try."
if ("[ 0/90]" in flash_id):
try_this = "Generic Flash Cartridge (0/90)"
elif ("[ AAA/AA]" in flash_id):
try_this = "Generic Flash Cartridge (AAA/AA)"
elif ("[ AAA/A9]" in flash_id):
try_this = "Generic Flash Cartridge (AAA/A9)"
elif ("[WR / AAA/AA]" in flash_id):
try_this = "Generic Flash Cartridge (WR/AAA/AA)"
elif ("[WR / AAA/A9]" in flash_id):
try_this = "Generic Flash Cartridge (WR/AAA/A9)"
elif ("[WR / 555/AA]" in flash_id):
try_this = "Generic Flash Cartridge (WR/555/AA)"
elif ("[WR / 555/A9]" in flash_id):
try_this = "Generic Flash Cartridge (WR/555/A9)"
elif ("[AUDIO/ AAA/AA]" in flash_id):
try_this = "Generic Flash Cartridge (AUDIO/AAA/AA)"
elif ("[AUDIO/ 555/AA]" in flash_id):
try_this = "Generic Flash Cartridge (AUDIO/555/AA)"
if try_this is not None:
msg_cart_type_s += " For ROM writing, you can give the option called “{:s}” a try at your own risk.".format(try_this)
msg_cart_type_s += "<br>"
else:
msg_cart_type_s = "<b>Cartridge Type:</b> Generic ROM Cartridge (not rewritable or not auto-detectable)<br>"
@ -1869,12 +1839,19 @@ class FlashGBX_GUI(QtWidgets.QWidget):
button_ok = msgbox.addButton("&OK", QtWidgets.QMessageBox.ActionRole)
msgbox.setDefaultButton(button_ok)
msgbox.setEscapeButton(button_ok)
if try_this is not None:
button_try = msgbox.addButton(" &Try Generic Type ", QtWidgets.QMessageBox.ActionRole)
button_try.setToolTip("{:s}".format(try_this))
else:
button_try = None
if not is_generic:
msg_fw = "<br><span style=\"font-size: 8pt;\"><i>{:s} {:s} | {:s}</i></span><br>".format(APPNAME, VERSION, self.CONN.GetFullNameExtended())
button_clipboard = msgbox.addButton(" &Copy to Clipboard ", QtWidgets.QMessageBox.ActionRole)
else:
msg_fw = ""
button_clipboard = None
temp = "{:s}{:s}{:s}{:s}{:s}{:s}{:s}{:s}".format(msg, msg_header_s, msg_flash_size_s, msg_save_type_s, msg_flash_id_s, msg_cfi_s, msg_cart_type_s_detail, msg_fw)
temp = temp[:-4]
msgbox.setText(temp)
@ -1886,6 +1863,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
doc.setHtml(temp)
temp = doc.toPlainText()
clipboard.setText(temp)
elif msgbox.clickedButton() == button_try:
if try_this in supp_cart_types[0]:
cart_type = supp_cart_types[0].index(try_this)
if self.CONN.GetMode() == "DMG":
self.cmbDMGCartridgeTypeResult.setCurrentIndex(cart_type)
elif self.CONN.GetMode() == "AGB":
self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
self.btnHeaderRefresh.setEnabled(True)
self.btnDetectCartridge.setEnabled(True)

View File

@ -61,12 +61,6 @@ class Flashcart:
def GetMBC(self):
if (self.CONFIG["type"].upper() == "AGB") or ("mbc" not in self.CONFIG): return False
mbc = self.CONFIG["mbc"]
#if mbc == 1: mbc = 0x03
#elif mbc == 2: mbc = 0x06
#elif mbc == 3: mbc = 0x13
#elif mbc == 5: mbc = 0x1B
#elif mbc == 6: mbc = 0x20
#elif mbc == 7: mbc = 0x22
return mbc
def FlashCommandsOnBank1(self):
@ -86,7 +80,6 @@ class Flashcart:
if buffer_size is False:
return False
else:
#return True
return ("buffer_write" in self.CONFIG["commands"])
def SupportsSingleWrite(self):
@ -133,6 +126,9 @@ class Flashcart:
cfi = self.ReadCFI()
if cfi is False:
print("CFI ERROR: Couldnt retrieve buffer size from the cartridge.")
if "single_write" in self.CONFIG["commands"]:
del(self.CONFIG["commands"]["buffer_write"])
print("Buffered write disabled.")
return False
if not "buffer_size" in cfi: return False
buffer_size = cfi["buffer_size"]
@ -197,11 +193,9 @@ class Flashcart:
self.CONFIG["commands"]["read_cfi"] = [ [ 0xAA, 0x98 ] ]
if "read_cfi" in self.CONFIG["commands"]:
#print(self.CONFIG["commands"]["read_cfi"])
self.CartWrite(self.CONFIG["commands"]["read_cfi"])
time.sleep(0.1)
buffer = self.CartRead(0, 0x400)
#print(buffer)
self.Reset()
cfi = CFI().Parse(buffer)
if cfi is not False:
@ -232,6 +226,9 @@ class Flashcart:
cfi = self.ReadCFI()
if cfi is False:
print("CFI ERROR: Couldnt retrieve sector size map from the cartridge.")
if "chip_erase" in self.CONFIG["commands"]:
del(self.CONFIG["commands"]["sector_erase"])
print("Sector erase mode disabled.")
return False
sector_size = cfi["erase_sector_blocks"]
if cfi["tb_boot_sector_raw"] == 0x03: sector_size.reverse()
@ -276,8 +273,6 @@ class Flashcart:
def SectorErase(self, pos=0, buffer_pos=0):
self.Reset(full_reset=False)
#time_start = time.time()
#if progress_fnc is not None: progress_fnc({"action":"ERASE", "time_start":time_start, "abortable":False})
if "sector_erase" not in self.CONFIG["commands"]: return False
if "sector_size" not in self.CONFIG: return False
for i in range(0, len(self.CONFIG["commands"]["sector_erase"])):
@ -330,7 +325,6 @@ class Flashcart:
except:
dprint("Warning: Sector map is smaller than expected.")
self.SECTOR_POS -= 1
#self.CONFIG["sector_size"][self.SECTOR_POS][0]
return sector_size
else:
return self.CONFIG["sector_size"]
@ -478,9 +472,6 @@ class CFI:
return info
class Flashcart_DMG_MMSA(Flashcart):
#def __init__(self, config={}, cart_write_fncptr=None, cart_read_fncptr=None):
# super().__init__(config=config, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr)
def ReadCFI(self):
return False
@ -494,7 +485,6 @@ class Flashcart_DMG_MMSA(Flashcart):
return True
def EraseHiddenSector(self, buffer):
#time_start = time.time()
if self.PROGRESS_FNCPTR is not None: self.PROGRESS_FNCPTR({"action":"SECTOR_ERASE", "sector_pos":0, "time_start":time.time(), "abortable":False})
self.UnlockForWriting()

221
FlashGBX/GBMemory.py Normal file
View File

@ -0,0 +1,221 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
import datetime, struct, copy
from . import Util
from .RomFileDMG import RomFileDMG
class GBMemoryMap:
MAP_DATA = bytearray([0xFF] * 0x80)
IS_MENU = False
def __init__(self, rom=None):
if rom is not None:
self.ImportROM(rom)
def ImportROM(self, data):
info = {"map":{}, "menu":{}}
if len(data) < 0x180:
return False
info["rom_header"] = RomFileDMG(data[:0x180]).GetHeader()
self.IS_MENU = (info["rom_header"]["game_title"] in ("NP M-MENU MENU", "DMG MULTI MENU "))
data = copy.deepcopy(data)
if len(data) < 0x20000:
data = bytearray(data) + bytearray([0xFF] * (0x20000 - len(data)))
if not self.IS_MENU:
mbc_type = self.MapperToMBCType(info["rom_header"]["features_raw"])
if mbc_type is False: return
if len(data) <= 0x20000:
rom_size = 0b010
elif len(data) <= 0x40000:
rom_size = 0b011
elif len(data) <= 0x80000:
rom_size = 0b100
else:
rom_size = 0b101
if info["rom_header"]["ram_size_raw"] not in Util.DMG_Header_RAM_Sizes_Map:
sram_size = 0
sram_type = 0b000
else:
sram_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(info["rom_header"]["ram_size_raw"])]
if sram_size == 0:
sram_type = 0b000
elif sram_size == 0x2000:
sram_type = 0b010
elif sram_size == 0x8000:
sram_type = 0b011
elif sram_size == 0x10000:
sram_type = 0b100
elif sram_size == 0x20000:
sram_type = 0b101
else:
sram_type = 0b000
info["map"] = {
"mbc_type":mbc_type,
"rom_size":rom_size,
"sram_type":sram_type,
"rom_start_block":0,
"ram_start_block":0,
"raw":bytearray()
}
info["menu"]["metadata"] = {}
info["menu"]["metadata"]["f_size"] = int(len(data) / (128 * 1024))
if info["map"]["sram_type"] == 0b000: # None
info["menu"]["metadata"]["b_size"] = 0
elif info["map"]["sram_type"] == 0b001: # SRAM MBC2 512 Byte
info["menu"]["metadata"]["b_size"] = 64 #4
elif info["map"]["sram_type"] == 0b010: # SRAM 8 KB
info["menu"]["metadata"]["b_size"] = 64
elif info["map"]["sram_type"] == 0b011: # SRAM 32 KB
info["menu"]["metadata"]["b_size"] = 256
elif info["map"]["sram_type"] == 0b100: # SRAM 64 KB
info["menu"]["metadata"]["b_size"] = 512
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")
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"]["raw"] = bytearray(0x56)
data = info
keys = ["f_size", "b_size", "game_code", "title", "timestamp", "kiosk_id"]
values = []
for key in keys:
values.append(data["menu"]["metadata"][key])
buffer = struct.pack("=HH12s44s18s26s", *values)
data["menu"]["raw"] = buffer
temp = 0
temp |= (data["map"]["mbc_type"] & 0x7) << 29
temp |= (data["map"]["rom_size"] & 0x7) << 26
temp |= (data["map"]["sram_type"] & 0x7) << 23
temp |= (data["map"]["rom_start_block"] & 0x7F) << 16
temp |= (data["map"]["ram_start_block"] & 0x7F) << 8
data["map"]["raw"] = temp
self.MAP_DATA[0x00:0x6E] = bytearray([0xFF] * 0x6E)
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
ram_offset = 0
for i in range(0, 8):
pos = 0x1C000 + (i * 0x200)
menu_item = data[pos:pos+0x200]
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", menu_item)
info = dict(zip(keys, values))
if info["menu_index"] == 0xFF: continue
info["rom_data_offset"] = info["f_offset"] * (128 * 1024)
info["rom_data_size"] = info["f_size"] * (128 * 1024)
info["ram_data_offset"] = info["b_offset"] * (8 * 1024)
info["ram_data_size"] = self.GetBlockSizeBackup(info["b_size"]) * (8 * 1024)
del(info["title_gfx"])
info["rom_start_block"] = int(rom_offset / 0x8000)
rom_offset += info["rom_data_size"]
info["ram_start_block"] = int(ram_offset / 0x800)
ram_offset += info["ram_data_size"]
info["rom_header"] = RomFileDMG(data[info["rom_data_offset"]:info["rom_data_offset"]+0x180]).GetHeader()
mbc_type = self.MapperToMBCType(info["rom_header"]["features_raw"])
if mbc_type is False: return
if info["rom_data_size"] <= 0x20000:
rom_size = 0b010
elif info["rom_data_size"] <= 0x40000:
rom_size = 0b011
elif info["rom_data_size"] <= 0x80000:
rom_size = 0b100
else:
rom_size = 0b101
if info["rom_header"]["game_title"] == "NP M-MENU MENU" or info["rom_header"]["ram_size_raw"] not in Util.DMG_Header_RAM_Sizes_Map:
sram_size = 0
sram_type = 0b000
else:
sram_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(info["rom_header"]["ram_size_raw"])]
if sram_size == 0:
sram_type = 0b000
elif sram_size == 0x2000:
sram_type = 0b010
elif sram_size == 0x8000:
sram_type = 0b011
elif sram_size == 0x10000:
sram_type = 0b100
elif sram_size == 0x20000:
sram_type = 0b101
else:
sram_type = 0b000
info["map"] = {
"mbc_type":mbc_type,
"rom_size":rom_size,
"sram_type":sram_type,
"rom_start_block":info["rom_start_block"],
"ram_start_block":info["ram_start_block"],
"raw":bytearray()
}
temp = 0
temp |= (info["map"]["mbc_type"] & 0x7) << 29
temp |= (info["map"]["rom_size"] & 0x7) << 26
temp |= (info["map"]["sram_type"] & 0x7) << 23
temp |= (info["map"]["rom_start_block"] & 0x7F) << 16
temp |= (info["map"]["ram_start_block"] & 0x7F) << 8
info["map"]["raw"] = temp
menu_items.append(info)
self.MAP_DATA[0x00:0x6E] = bytearray([0xFF] * 0x6E)
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'))
def MapperToMBCType(self, mbc):
if mbc == 0x00: # ROM only
mbc_type = 0
elif mbc in (0x01, 0x02, 0x03): # MBC1
mbc_type = 1
elif mbc == 0x06: # MBC2
mbc_type = 2
elif mbc in (0x10, 0x13): # MBC3
mbc_type = 3
elif mbc in (0x19, 0x1A, 0x1B, 0x1C, 0x1E): # MBC5
mbc_type = 5
else:
mbc_type = False
return mbc_type
def GetBlockSizeBackup(self, b_size=None):
if b_size == 0:
b_size = 0
elif b_size == 1:
b_size = 1
elif b_size == 64:
b_size = 1
elif b_size == 256:
b_size = 4
elif b_size == 1024:
b_size = 16
else:
b_size = 4
return b_size
def IsMenu(self):
return self.IS_MENU
def GetMapData(self):
if self.MAP_DATA == bytearray([0xFF] * 0x80): return False
return self.MAP_DATA

View File

@ -708,9 +708,16 @@ class DMG_GMMC1(DMG_MBC5):
def CalcChecksum(self, buffer):
header = RomFileDMG(buffer[:0x180]).GetHeader()
target_chk_value = 0
if header["game_title"] == "NP M-MENU MENU":
target_sha1_value = "15f5d445c0b2fdf4221cf2a986a4a5cb8dfda131"
target_chk_value = 0x19E8
if hashlib.sha1(buffer[0:0x18000]).hexdigest() != "15f5d445c0b2fdf4221cf2a986a4a5cb8dfda131":
elif header["game_title"] == "DMG MULTI MENU ":
target_sha1_value = "b8949fb9c4343b2c04ad59064e9d1dd78a131366"
target_chk_value = 0xC297
if target_chk_value != 0:
if hashlib.sha1(buffer[0:0x18000]).hexdigest() != target_sha1_value:
return 0
elif buffer[0:0x180] == buffer[0x20000:0x20180]:
return 1

View File

@ -33,16 +33,18 @@ class RomFileDMG:
return checksum
def CalcChecksumGlobal(self, fix=False):
checksum = 0
for i in range(0, len(self.ROMFILE), 2):
if i != 0x14E:
checksum = checksum + self.ROMFILE[i + 1]
checksum = checksum + self.ROMFILE[i]
temp1 = self.ROMFILE[0x14E]
temp2 = self.ROMFILE[0x14F]
self.ROMFILE[0x14E] = 0
self.ROMFILE[0x14F] = 0
checksum = sum(self.ROMFILE) & 0xFFFF
if fix:
self.ROMFILE[0x14E] = checksum >> 8
self.ROMFILE[0x14F] = checksum & 0xFF
return checksum & 0xFFFF
else:
self.ROMFILE[0x14E] = temp1
self.ROMFILE[0x14F] = temp2
return checksum
def FixHeader(self):
self.CalcChecksumHeader(True)
@ -78,13 +80,13 @@ class RomFileDMG:
data["features"] = "?"
data["rom_size_raw"] = int(buffer[0x148])
data["rom_size"] = "?"
if buffer[0x148] in Util.DMG_Header_ROM_Sizes: data["rom_size"] = Util.DMG_Header_ROM_Sizes[buffer[0x148]]
if buffer[0x148] < len(Util.DMG_Header_ROM_Sizes): data["rom_size"] = Util.DMG_Header_ROM_Sizes[buffer[0x148]]
data["ram_size_raw"] = int(buffer[0x149])
if data["features_raw"] == 0x05 or data["features_raw"] == 0x06:
data["ram_size"] = 0x200
else:
data["ram_size"] = "?"
if buffer[0x149] in Util.DMG_Header_RAM_Sizes:
if buffer[0x149] < len(Util.DMG_Header_RAM_Sizes):
data["ram_size"] = Util.DMG_Header_RAM_Sizes[buffer[0x149]]
data["version"] = int(buffer[0x14C])
data["header_checksum"] = int(buffer[0x14D])
@ -107,6 +109,9 @@ class RomFileDMG:
if data["features_raw"] == 0x19 and data["game_title"] == "NP M-MENU MENU" and data["header_checksum"] == 0xD3:
data["features_raw"] = 0x105
data["ram_size_raw"] = 0x04
elif data["features_raw"] == 0x01 and data["game_title"] == "DMG MULTI MENU " and data["header_checksum"] == 0x36:
data["features_raw"] = 0x105
data["ram_size_raw"] = 0x04
# M161 (Mani 4 in 1)
elif data["features_raw"] == 0x10 and data["game_title"] == "TETRIS SET" and data["header_checksum"] == 0x3F:
@ -118,12 +123,16 @@ class RomFileDMG:
data["features_raw"] == 0x11 and data["game_title"] == "GANBARUGA SET" and data["header_checksum"] == 0x90 or \
data["features_raw"] == 0x11 and data["game_title"] == "RTYPE 2 SET" and data["header_checksum"] == 0x32:
data["features_raw"] = 0x0B
# Unlicensed 256M Mapper
elif data["game_title"].upper() == "GB HICOL" and data["header_checksum"] in (0x4A, 0x49, 0xE9):
elif (data["game_title"].upper() == "GB HICOL" and data["header_checksum"] in (0x4A, 0x49, 0xE9)) or \
(data["game_title"] == "BennVenn" and data["header_checksum"] == 0x48):
data["features_raw"] = 0x201
data["rom_size_raw"] = 0x0A
data["ram_size_raw"] = 0x201
elif buffer[0x150:0x160].decode("ascii", "replace") == "256M ROM Builder":
data["features_raw"] = 0x201
data["ram_size_raw"] = 0x201
# Unlicensed Wisdom Tree Mapper
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xF5, 0xD2, 0x91, 0x7D, 0x5E, 0x5B, 0xAB, 0xD8, 0x5F, 0x0A, 0xC7, 0xBA, 0x56, 0xEB, 0x49, 0x8A, 0xBA, 0x12, 0x49, 0x13 ]): # Exodus / Joshua

View File

@ -7,22 +7,21 @@ from enum import Enum
# Common constants
APPNAME = "FlashGBX"
VERSION_PEP440 = "3.6"
VERSION_PEP440 = "3.7"
VERSION = "v{:s}".format(VERSION_PEP440)
DEBUG = False
AGB_Header_ROM_Sizes = [ "1 MB", "2 MB", "4 MB", "8 MB", "16 MB", "32 MB", "64 MB", "128 MB", "256 MB" ]
AGB_Header_ROM_Sizes_Map = [ 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000 ]
AGB_Header_Save_Types = [ "None", "4K EEPROM (512 Bytes)", "64K EEPROM (8 KB)", "256K SRAM (32 KB)", "512K SRAM (64 KB)", "1M SRAM (128 KB)", "512K FLASH (64 KB)", "1M FLASH (128 KB)", "8M DACS (1008 KB)" ]
AGB_Header_Save_Sizes = [ 0, 512, 8192, 32768, 65536, 131072, 65536, 131072, 1032192 ]
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_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" }
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000 ]
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' }
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_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x10, 0x20 ]
#DMG_Header_ROM_Sizes_Flasher_Map = [ 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 ] # Number of ROM banks
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)" ]
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201 ]
@ -66,7 +65,6 @@ class IniSettings():
path = ""
if path == "":
#buf = io.StringIO(ini)
self.FILENAME = False
self.SETTINGS = configparser.RawConfigParser()
self.SETTINGS.read_string(ini)
@ -258,7 +256,6 @@ def roundup(x):
return math.ceil(x * d) / d
def formatFileSize(size, asInt=False, roundUp=False):
#size = size / 1024
if size == 1:
return "{:d} Byte".format(size)
elif size < 1024:
@ -393,8 +390,8 @@ def ParseCFI(buffer):
info["tb_boot_sector"] = "{:s} (0x{:02X})".format(temp[buffer[pri_address + 0x1E]], buffer[pri_address + 0x1E])
except:
info["tb_boot_sector"] = "0x{:02X}".format(buffer[pri_address + 0x1E])
elif "{:s}{:s}{:s}".format(chr(buffer[0x214]), chr(buffer[0x216]), chr(buffer[0x218])) == "PRI":
pass # TODO
#elif "{:s}{:s}{:s}".format(chr(buffer[0x214]), chr(buffer[0x216]), chr(buffer[0x218])) == "PRI":
# pass
info["device_size"] = int(math.pow(2, buffer[0x4E]))
info["buffer_size"] = buffer[0x56] << 8 | buffer[0x54]

File diff suppressed because it is too large Load Diff

View File

@ -55,6 +55,22 @@
[ null, null, null ],
[ "SA", 0xFFFF, 0xFFFF ]
],
"chip_erase":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0x80 ],
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0x10 ]
],
"chip_erase_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ 0, 0xFFFF, 0xFFFF ]
],
"buffer_write":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],

View File

@ -0,0 +1,56 @@
{
"type":"AGB",
"names":[
"B54 with MX29LV320ET",
"B100 with MX29LV640ET"
],
"flash_ids":[
[ 0xC2, 0x00, 0xA7, 0x22 ],
[ 0xC2, 0x00, 0xC9, 0x22 ]
],
"voltage":3.3,
"flash_size":0x400000,
"chip_erase_timeout":200,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x90 ]
],
"read_cfi":[
[ 0xAA, 0x98 ]
],
"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, 0xFFFF, 0xFFFF ]
],
"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

@ -0,0 +1,71 @@
{
"type":"AGB",
"names":[
"AGB-E05-01 with S29GL064"
],
"flash_ids":[
[ 0x02, 0x00, 0x7D, 0x22 ]
],
"voltage":3.3,
"flash_size":0x800000,
"sector_size_from_cfi":true,
"chip_erase_timeout":120,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0x90 ]
],
"read_cfi":[
[ 0xAA, 0x98 ]
],
"sector_erase":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0x80 ],
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ "SA", 0x30 ]
],
"sector_erase_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ "SA", 0xFFFF, 0xFFFF ]
],
"buffer_write":[
[ 0xAAA, 0xA9 ],
[ 0x555, 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":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,58 @@
{
"type":"DMG",
"names":[
"DIY cart with MBC1 and AM29F010 @ AUDIO"
],
"flash_ids":[
[ 0x01, 0x20 ]
],
"voltage":5,
"flash_size":0x20000,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":60,
"mbc":0x03,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x90 ]
],
"read_cfi":[
[ 0x555, 0x98 ]
],
"chip_erase":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x80 ],
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 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":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,58 @@
{
"type":"DMG",
"names":[
"DIY cart with MBC1 and AM29F040 @ AUDIO"
],
"flash_ids":[
[ 0x01, 0xA4 ]
],
"voltage":5,
"flash_size":0x80000,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":60,
"mbc":0x03,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x90 ]
],
"read_cfi":[
[ 0x555, 0x98 ]
],
"chip_erase":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x80 ],
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 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":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,58 @@
{
"type":"DMG",
"names":[
"DIY cart with MBC1 and AM29F040 @ WR"
],
"flash_ids":[
[ 0x01, 0xA4 ]
],
"voltage":5,
"flash_size":0x80000,
"start_addr":0,
"first_bank":1,
"mbc":0x03,
"write_pin":"WR",
"chip_erase_timeout":15,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x90 ]
],
"read_cfi":[
[ 0x555, 0x98 ]
],
"chip_erase":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x80 ],
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 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":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,59 @@
{
"type":"DMG",
"names":[
"DIY cart with MBC1 and AM29F080 @ AUDIO"
],
"flash_ids":[
[ 0x20, 0xF1 ],
[ 0x01, 0xD5 ]
],
"voltage":5,
"flash_size":0x100000,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":60,
"mbc":0x03,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x90 ]
],
"read_cfi":[
[ 0x555, 0x98 ]
],
"chip_erase":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x80 ],
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 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":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,54 @@
{
"type":"DMG",
"names":[
"Generic Flash Cartridge (AUDIO/555/AA)"
],
"voltage":3.3,
"voltage_variants":true,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":120,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x90 ]
],
"read_cfi":[
[ 0x555, 0x98 ]
],
"chip_erase":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0x80 ],
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 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":[
[ 0x555, 0xAA ],
[ 0x2AA, 0x55 ],
[ 0x555, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -0,0 +1,54 @@
{
"type":"DMG",
"names":[
"Generic Flash Cartridge (AUDIO/AAA/AA)"
],
"voltage":3.3,
"voltage_variants":true,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":120,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0xAAA, 0xAA ],
[ 0x555, 0x55 ],
[ 0xAAA, 0x90 ]
],
"read_cfi":[
[ 0xAAA, 0x98 ]
],
"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

@ -15,6 +15,7 @@
[ 0x04, 0x04, 0x7D, 0x7D ]
],
"voltage":3.3,
"voltage_variants":true,
"flash_size":0x400000,
"start_addr":0x4000,
"first_bank":0,

View File

@ -62,8 +62,8 @@
[ "SA", 0xFF, 0xFF ]
],
"buffer_write":[
[ 0x4AAA, 0xA9 ],
[ 0x4555, 0x56 ],
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ "SA", 0x26 ],
[ "SA", "BS" ],
[ "PA", "PD" ],

View File

@ -9,10 +9,6 @@
"start_addr":0x4000,
"first_bank":1,
"write_pin":"WR",
"sector_size":[
[0x2000, 8],
[0x10000, 63]
],
"chip_erase_timeout":40,
"mbc":0x03,
"pulse_reset_after_write":true,

View File

@ -0,0 +1,55 @@
{
"type":"DMG",
"names":[
"DIY cart with MBC1 and SST39SF040 @ AUDIO"
],
"flash_ids":[
[ 0xBF, 0xB7, 0x01, 0xFF ]
],
"voltage":5,
"flash_size":0x80000,
"start_addr":0,
"first_bank":1,
"flash_commands_on_bank_1":true,
"write_pin":"AUDIO",
"chip_erase_timeout":30,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0x5555, 0xAA ],
[ 0x2AAA, 0x55 ],
[ 0x5555, 0x90 ]
],
"chip_erase":[
[ 0x5555, 0xAA ],
[ 0x2AAA, 0x55 ],
[ 0x5555, 0x80 ],
[ 0x5555, 0xAA ],
[ 0x2AAA, 0x55 ],
[ 0x5555, 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":[
[ 0x5555, 0xAA ],
[ 0x2AAA, 0x55 ],
[ 0x5555, 0xA0 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -145,13 +145,11 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
rowDeviceInfo2 = QtWidgets.QHBoxLayout()
self.lblDevicePCBVer = QtWidgets.QLabel("PCB version:")
self.lblDevicePCBVer.setMinimumWidth(120)
#self.lblDevicePCBVerResult = QtWidgets.QLabel("v1.4")
self.optDevicePCBVer14 = QtWidgets.QRadioButton("v1.4")
self.connect(self.optDevicePCBVer14, QtCore.SIGNAL("clicked()"), self.SetPCBVersion)
self.optDevicePCBVer14a = QtWidgets.QRadioButton("v1.4a")
self.connect(self.optDevicePCBVer14a, QtCore.SIGNAL("clicked()"), self.SetPCBVersion)
rowDeviceInfo2.addWidget(self.lblDevicePCBVer)
#rowDeviceInfo2.addWidget(self.lblDevicePCBVerResult)
rowDeviceInfo2.addWidget(self.optDevicePCBVer14)
rowDeviceInfo2.addWidget(self.optDevicePCBVer14a)
rowDeviceInfo2.addStretch(1)
@ -216,7 +214,6 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
self.lblDeviceNameResult.setText(self.DEV_NAME)
self.lblDeviceFWVerResult.setText(self.FW_VER)
#self.lblDevicePCBVerResult.setText(self.PCB_VER)
if self.PCB_VER == "v1.4":
self.optDevicePCBVer14.setChecked(True)
elif self.PCB_VER == "v1.4a":
@ -239,7 +236,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
self.OFW_BUILDTS = self.INI.GetValue("fw_buildts")
self.OFW_TEXT = self.INI.GetValue("fw_text")
self.lblDeviceFWVer2Result.setText("{:s} (dated {:s})".format(self.OFW_VER, datetime.datetime.fromtimestamp(int(self.OFW_BUILDTS)).astimezone().replace(microsecond=0).isoformat()))
self.lblDeviceFWVer2Result.setText("{:s} ({:s})".format(self.OFW_VER, datetime.datetime.fromtimestamp(int(self.OFW_BUILDTS)).astimezone().replace(microsecond=0).isoformat()))
def run(self):
try:

View File

@ -10,6 +10,7 @@ from .RomFileAGB import RomFileAGB
from .Mapper import DMG_MBC, AGB_GPIO
from .Flashcart import Flashcart, Flashcart_DMG_MMSA
from .Util import ANSI, dprint, bitswap, ParseCFI
from .GBMemory import GBMemoryMap
from . import Util
class GbxDevice:
@ -93,6 +94,7 @@ class GbxDevice:
WORKER = None
INFO = { "action":None, "last_action":None }
ERROR = False
ERROR_ARGS = {}
CANCEL = False
CANCEL_ARGS = {}
SIGNAL = None
@ -119,7 +121,6 @@ class GbxDevice:
for i in range(0, len(comports)):
if comports[i].vid == 0x1A86 and comports[i].pid == 0x7523:
ports.append(comports[i].device)
#break
if len(ports) == 0: return False
for i in range(0, len(ports)):
@ -301,7 +302,7 @@ class GbxDevice:
else:
s = "{:s}{:d}".format(self.FW["cfw_id"], self.FW["fw_ver"])
if more:
s += " (dated {:s})".format(self.FW["fw_dt"])
s += " ({:s})".format(self.FW["fw_dt"])
return s
def GetPCBVersion(self):
@ -316,7 +317,7 @@ class GbxDevice:
def GetFullNameExtended(self, more=False):
if more:
return "{:s} Firmware {:s} ({:s}) (dated {:s}) at {:.1f}M baud".format(self.GetFullName(), self.GetFirmwareVersion(), self.PORT, self.FW["fw_dt"], self.BAUDRATE/1000/1000)
return "{:s} Firmware {:s} ({:s}) on {:s} at {:.1f}M baud".format(self.GetFullName(), self.GetFirmwareVersion(), self.FW["fw_dt"], self.PORT, self.BAUDRATE/1000/1000)
else:
return "{:s} Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.PORT)
@ -356,10 +357,14 @@ class GbxDevice:
tb_stack = traceback.extract_stack()
stack = tb_stack[len(tb_stack)-2] # caller only
if stack.name == "_write": stack = tb_stack[len(tb_stack)-3]
print("{:s}Waiting for confirmation from the device has failed. (Called from {:s}(), line {:d}){:s}\n".format(ANSI.RED, stack.name, stack.lineno, ANSI.RESET))
self.CANCEL = True
self.CANCEL_ARGS = {"info_type":"msgbox_critical", "info_msg":"An error occured while waiting for confirmation from the device. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning."}
if buffer is False:
dprint("Timeout error ({:s}(), line {:d})".format(stack.name, stack.lineno))
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A timeout error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
else:
dprint("Communication error ({:s}(), line {:d})".format(stack.name, stack.lineno))
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A communication error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
self.ERROR = True
self.CANCEL = True
return False
return buffer
@ -666,18 +671,16 @@ class GbxDevice:
if mbc == 0x20: # MBC6
save_size = 1081344
save_type = 7
(cart_types, cart_type_id, flash_id, cfi_s, cfi) = self.AutoDetectFlash(limitVoltage=limitVoltage)
return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
elif mbc == 0x22: # MBC7
max_size = 512
elif mbc == 0xFD: # TAMA5
save_size = 32
save_type = 10
(cart_types, cart_type_id, flash_id, cfi_s, cfi) = self.AutoDetectFlash(limitVoltage=limitVoltage)
return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':max_size, 'rtc':False }
elif self.MODE == "AGB":
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':5, 'rtc':False }
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':8, 'rtc':False }
ret = self._BackupRestoreRAM(args=args)
@ -712,18 +715,25 @@ class GbxDevice:
save_size = Util.AGB_Flash_Save_Chips_Sizes[list(Util.AGB_Flash_Save_Chips).index(flash_save_id)]
save_chip = Util.AGB_Flash_Save_Chips[flash_save_id]
if save_size == 131072:
save_type = 7
save_type = 5
elif save_size == 65536:
save_type = 6
save_type = 4
except:
pass
if save_type is None:
if info["dacs_8m"] is True:
save_size = 1032192
save_type = 8
elif save_size > 256:
save_type = Util.AGB_Header_Save_Sizes.index(save_size)
save_type = 6
elif save_size > 256: # SRAM
if save_size == 131072:
save_type = 8
elif save_size == 65536:
save_type = 7
elif save_size == 32768:
save_type = 3
else:
save_type = Util.AGB_Header_Save_Sizes.index(save_size)
else:
dprint("Testing EEPROM")
# Check for 4K EEPROM
@ -745,7 +755,6 @@ class GbxDevice:
save_size = 512
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
#if self.CanPowerCycleCart(): self.CartPowerCycle()
self.INFO["last_action"] = 0
self.INFO["action"] = None
@ -880,7 +889,6 @@ class GbxDevice:
if isinstance(temp, int): temp = bytearray([temp])
if temp is False or len(temp) != length: return bytearray()
buffer += temp
#if self.INFO["action"] == self.ACTIONS["SAVE_READ"] and not self.NO_PROG_UPDATE:
if not self.NO_PROG_UPDATE:
self.SetProgress({"action":"READ", "bytes_added":len(temp)})
@ -899,7 +907,6 @@ class GbxDevice:
temp = self._read(length)
if isinstance(temp, int): temp = bytearray([temp])
buffer += temp
#if self.INFO["action"] == self.ACTIONS["SAVE_READ"] and not self.NO_PROG_UPDATE:
if not self.NO_PROG_UPDATE:
self.SetProgress({"action":"READ", "bytes_added":len(temp)})
@ -1000,7 +1007,6 @@ class GbxDevice:
self._cart_write(address + length - 1, 0x00)
while True:
sr = self._cart_read(address + length - 1)
#print("sr=0x{:X}".format(sr))
if sr == 0x80: break
time.sleep(0.001)
@ -1092,7 +1098,8 @@ class GbxDevice:
ret = self._write(data, wait=True)
if ret not in (0x01, 0x03):
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))
dprint("Flash error at 0x{:X} in iteration {:d} of {:d} while trying to write a total of 0x{:X} bytes (response = {:s})".format(address, i, num, len(buffer), str(ret)))
self.ERROR_ARGS = { "iteration":i }
self.SKIPPING = False
return False
pos += len(data)
@ -1190,7 +1197,6 @@ class GbxDevice:
self._cart_write(address + length - 1, 0xFF)
while True:
sr = self._cart_read(address + length - 1)
#print("sr=0x{:X}".format(sr))
if sr == 0x80: break
time.sleep(0.001)
@ -1203,10 +1209,6 @@ class GbxDevice:
def WriteROM_DMG_MBC5_32M_FLASH(self, address, buffer, bank):
length = len(buffer)
#if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5):
# max_length = 128
#else:
# max_length = 256
max_length = 128
num = math.ceil(length / max_length)
if length > max_length: length = max_length
@ -1290,7 +1292,6 @@ class GbxDevice:
dprint("*** Now checking: {:s}\n".format(flashcart_meta["names"][0]))
if self.MODE == "DMG":
#self._set_fw_variable("FLASH_COMMANDS_BANK_1", "flash_commands_on_bank_1" in flashcart_meta)
if flashcart_meta["write_pin"] == "WR":
we = 0x01 # FLASH_WE_PIN_WR
elif flashcart_meta["write_pin"] in ("AUDIO", "VIN"):
@ -1396,7 +1397,6 @@ class GbxDevice:
if "method" in cfi: break
for method in flash_commands:
if self.MODE == "DMG":
#self._set_fw_variable("FLASH_COMMANDS_BANK_1", "flash_commands_on_bank_1" in flashcart_meta)
if we == "WR":
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
elif we in ("AUDIO", "VIN"):
@ -1555,7 +1555,7 @@ class GbxDevice:
if not line_exists: flash_id_lines.append([method_string, flash_id])
for i in range(0, len(method['reset'])):
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
if self.MODE == "DMG":
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
time.sleep(0.25)
@ -1572,7 +1572,7 @@ class GbxDevice:
flash_id += "\n"
flash_id = rom_string + flash_id
self._set_fw_variable("FLASH_WE_PIN", 0x02) # Set AUDIO back to high
#self._set_fw_variable("FLASH_WE_PIN", 0x02) # Set AUDIO back to high
return (flash_id, cfi_info, cfi)
#################################################################
@ -1612,7 +1612,7 @@ class GbxDevice:
if len(args["path"]) > 0:
file = open(args["path"], "wb")
self.FAST_READ = True #args["fast_read_mode"]
self.FAST_READ = True
flashcart = False
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
@ -1643,9 +1643,7 @@ class GbxDevice:
else:
_mbc.EnableMapper()
#rom_banks = args['rom_banks']
rom_size = args["rom_size"]
#if _mbc.GetName() in ("MBC6", "M161", "Wisdom Tree"): rom_banks = _mbc.GetROMBanks(args['rom_banks'] * 0x4000)
rom_banks = _mbc.GetROMBanks(rom_size)
size = _mbc.GetROMSize()
@ -1659,7 +1657,6 @@ class GbxDevice:
if "verify_write" in args:
rom_banks = math.ceil(len(args["verify_write"]) / cart_type["flash_bank_size"])
else:
#rom_banks = int(min(size, cart_type["flash_size"]) / cart_type["flash_bank_size"])
rom_banks = math.ceil(size / cart_type["flash_bank_size"])
else:
rom_banks = 1
@ -1706,6 +1703,7 @@ class GbxDevice:
if file is not None: file.close()
except:
pass
if self.CanPowerCycleCart(): self.CartPowerCycle()
return
if (self.MODE == "AGB" and self.INFO["3d_memory"]):
@ -1802,7 +1800,7 @@ class GbxDevice:
# Prepare some stuff
command = None
empty_data_byte = 0xFF
empty_data_byte = 0x00
extra_size = 0
audio_low = False
if self.MODE == "DMG":
@ -1818,11 +1816,11 @@ class GbxDevice:
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
_mbc.EnableMapper()
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
empty_data_byte = 0x00
buffer_len = 0x20
elif _mbc.GetName() == "MBC7":
buffer_len = save_size
elif _mbc.GetName() == "MBC6":
empty_data_byte = 0xFF
audio_low = True
self._set_fw_variable("FLASH_METHOD", 0x04) # FLASH_METHOD_DMG_MBC6
self._set_fw_variable("FLASH_WE_PIN", 0x01) # WR
@ -1860,13 +1858,15 @@ class GbxDevice:
buffer_len = 0x40
else:
buffer_len = 0x100
elif args["save_type"] in (6, 7): # FLASH
elif args["save_type"] in (4, 5): # FLASH
empty_data_byte = 0xFF
ret = self.ReadFlashSaveID()
if ret is False:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldnt detect the save data flash chip.", "abortable":False})
return False
(buffer_len, agb_flash_chip, _) = ret
elif args["save_type"] == 8: # DACS
elif args["save_type"] == 6: # DACS
empty_data_byte = 0xFF
# Read Chip ID
ram_banks = 1
self._cart_write(0, 0x90)
@ -1906,11 +1906,11 @@ class GbxDevice:
[ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 1]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 1]) ], # 4K EEPROM
[ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 2]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 2]) ], # 64K EEPROM
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 256K SRAM
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 2 if agb_flash_chip == 0x1F3D else 1]) ], # 512K FLASH
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 1]) ], # 1M FLASH
[ False, False ], # 8M DACS
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 512K SRAM
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 1M SRAM
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 2 if agb_flash_chip == 0x1F3D else 1]) ], # 512K FLASH
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 2 if agb_flash_chip == 0x1F3D else 1]) ], # 1M FLASH
[ False, False ], # 8M DACS
]
command = commands[args["save_type"]][args["mode"] - 2]
if args["rtc"] is True:
@ -1960,14 +1960,14 @@ class GbxDevice:
elif self.MODE == "AGB":
start_address = 0
bank_size = 0x10000
if args["save_type"] == 8: # DACS
if args["save_type"] == 6: # DACS
bank_size = 0xFC000
end_address = 0xFC000
else:
end_address = min(save_size, bank_size)
if save_size > bank_size:
if args["save_type"] == 7: # FLASH 1M
if args["save_type"] == 5: # FLASH 1M
dprint("Switching to FLASH bank {:d}".format(bank))
cmds = [
[ 0x5555, 0xAA ],
@ -1976,14 +1976,13 @@ class GbxDevice:
[ 0, bank ]
]
self._cart_write_flash(cmds)
elif args["save_type"] == 5: # SRAM 1M
elif args["save_type"] == 8: # SRAM 1M
dprint("Switching to SRAM bank {:d}".format(bank))
self._cart_write(0x1000000, bank)
else:
dprint("Unknown bank switching method")
time.sleep(0.1)
time.sleep(0.05)
#buffer_offset = bank * bank_size
max_length = 64
dprint("start_address=0x{:X}, end_address=0x{:X}, buffer_len=0x{:X}, buffer_offset=0x{:X}".format(start_address, end_address, buffer_len, buffer_offset))
pos = start_address
@ -1993,6 +1992,7 @@ class GbxDevice:
cancel_args.update(self.CANCEL_ARGS)
self.CANCEL_ARGS = {}
self.SetProgress(cancel_args)
if self.CanPowerCycleCart(): self.CartPowerCycle()
return
if args["mode"] == 2: # Backup
@ -2004,7 +2004,7 @@ class GbxDevice:
temp = self.ReadRAM_TAMA5()
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
temp = self.ReadRAM(address=int(pos/8), length=buffer_len, command=command, max_length=max_length)
elif self.MODE == "AGB" and args["save_type"] == 8: # DACS
elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
temp = self.ReadROM(address=0x1F00000+pos, length=buffer_len, skip_init=False, max_length=max_length)
else:
temp = self.ReadRAM(address=pos, length=buffer_len, command=command, max_length=max_length)
@ -2036,7 +2036,7 @@ class GbxDevice:
self.WriteRAM_TAMA5(buffer=buffer[buffer_offset:buffer_offset+buffer_len])
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
self.WriteRAM(address=int(pos/8), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
elif self.MODE == "AGB" and args["save_type"] in (6, 7): # FLASH
elif self.MODE == "AGB" and args["save_type"] in (4, 5): # FLASH
sector_address = int(pos / buffer_len)
if agb_flash_chip == 0x1F3D: # Atmel AT29LV512
self.WriteRAM(address=int(pos/128), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
@ -2062,8 +2062,9 @@ class GbxDevice:
if lives == 0:
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
if buffer[buffer_offset:buffer_offset+buffer_len] != bytearray([0xFF] * buffer_len):
self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
if (pos+0x1F00000) in (0x1F00000, 0x1F10000, 0x1F20000, 0x1F30000, 0x1F40000, 0x1F50000, 0x1F60000, 0x1F70000, 0x1F80000, 0x1F90000, 0x1FA0000, 0x1FB0000, 0x1FC0000, 0x1FD0000, 0x1FE0000, 0x1FF0000, 0x1FF2000, 0x1FF4000, 0x1FF6000, 0x1FF8000, 0x1FFA000):
sector_address = pos+0x1F00000
dprint("sector_address:", hex(sector_address))
@ -2179,7 +2180,7 @@ class GbxDevice:
return True
def _FlashROM(self, args):
self.FAST_READ = True #args["fast_read_mode"]
self.FAST_READ = True
if "buffer" in args:
data_import = args["buffer"]
@ -2254,14 +2255,24 @@ class GbxDevice:
if cart_type["command_set"] == "GBMEMORY":
flashcart = Flashcart_DMG_MMSA(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, progress_fncptr=self.SetProgress)
if "buffer_map" not in args:
try:
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()
except:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The GB Memory Cartridge requires a hidden sector file, but it wasnt found.\nExpected path: {:s}".format(os.path.splitext(args["path"])[0] + ".map"), "abortable":False})
return False
#with open(args["path"], "rb") as f: rom_data = bytearray(f.read())
#gbmem = GBMemory(None, rom_data, None)
#args["buffer_map"] = gbmem.GetMapData()
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")
@ -2305,8 +2316,6 @@ class GbxDevice:
args["mbc"] = 0x1B # MBC5+SRAM+BATTERY
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
# I need a cart for testing before this has a chance to work ↓
#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())
@ -2433,7 +2442,6 @@ class GbxDevice:
(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)))
#return False
# ↑↑↑ Read Flash ID
# ↓↓↓ Read Sector Map
@ -2456,15 +2464,12 @@ class GbxDevice:
# ↓↓↓ Chip erase
chip_erase = False
if flashcart.SupportsChipErase():
if flashcart.SupportsSectorErase() and args["prefer_chip_erase"] is False and flashcart.GetSectorMap() is not False:
if flashcart.SupportsSectorErase() and args["prefer_chip_erase"] is False and sector_map is not False:
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
@ -2524,7 +2529,8 @@ class GbxDevice:
cancel_args.update(self.CANCEL_ARGS)
self.CANCEL_ARGS = {}
self.SetProgress(cancel_args)
return False
if self.CanPowerCycleCart(): self.CartPowerCycle()
return
if buffer_pos >= len(data_import): break
@ -2554,7 +2560,12 @@ class GbxDevice:
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), rumble_stop=rumble)
if status is False:
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)}
if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Unstable connection detected while writing 0x{:X} bytes in iteration {:d} at position 0x{:X} ({:s}).\n\nPlease clean the cartridge contacts very thoroughly, then re-connect the device and try again.".format(buffer_len, self.ERROR_ARGS["iteration"], buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False))})
elif buffer_pos > 0:
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X}.\n\nPlease ensure that your cartridge supports ROMs larger than {:s} and that the cartridge contacts are clean, then re-connect the device and try again.".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False))})
else:
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}).\n\nPlease make sure that the correct cart type was selected and that the cartridge contacts are clean, then re-connect the device and try again from the beginning.".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=True))})
self.CANCEL = True
self.ERROR = True
continue
@ -2562,9 +2573,8 @@ class GbxDevice:
skip_init = True
buffer_pos += buffer_len
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
pos += buffer_len
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
# Hidden Sector
if command_set_type == "GBMEMORY":
@ -2586,10 +2596,6 @@ class GbxDevice:
self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE_VERIFY", "size":buffer_pos})
verify_args = copy.copy(args)
#if self.MODE == "DMG":
# rom_banks = math.ceil(len(data_import)/_mbc.GetROMBankSize())
#else:
# rom_banks = 1
start_address = 0
end_address = buffer_pos
@ -2599,9 +2605,8 @@ class GbxDevice:
verified_size = self._BackupROM(verify_args)
if self.CANCEL is True:
pass
#elif (verified_size is not True) and (buffer_pos != verified_size):
elif (verified_size is not True) and (len(data_import) != verified_size):
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})
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}.\n\nPlease make sure that the correct cart type was selected, that your cartridge actually does support ROMs that are {:s} in size and that the cartridge contacts are clean, then re-connect the device and try again from the beginning.".format(verified_size, Util.formatFileSize(len(data_import))), "abortable":False})
return False
else:
verified = True

View File

@ -119,7 +119,7 @@ class GbxDevice:
"AUDIO_HIGH":'8',
"AUDIO_LOW":'9',
}
PCB_VERSIONS = {1:'v1.0', 2:'v1.1', 4:'v1.3', 90:'XMAS', 100:'Mini v1.0', 101:'Mini v1.0d'}
PCB_VERSIONS = {1:'v1.0', 2:'v1.1/v1.2', 4:'v1.3', 90:'XMAS', 100:'Mini v1.0', 101:'Mini v1.0d'}
SUPPORTED_CARTS = {}
FW = []
@ -212,11 +212,11 @@ class GbxDevice:
self.DEVICE = None
continue
if (self.FW[1] not in (4, 5)):
conn_msg.append([0, "NOTE: This version of FlashGBX was developed to be used with GBxCart RW v1.3 and v1.4. Other revisions are untested and may not be fully compatible."])
elif self.FW[1] == 4:
conn_msg.append([0, "NOTE: FlashGBX is now optimized for the custom high compatibility firmware by Lesserkuma. You can install it in GUI mode from the Tools menu."])
conn_msg.append([0, "For help please visit the insideGadgets Discord: https://gbxcart.com/discord"])
if (self.FW[1] not in (4, 5)):
conn_msg.append([0, "\nNow running in Legacy Mode.\nThis version of FlashGBX was developed to be used with GBxCart RW v1.3 and v1.4.\nOther revisions are untested and may not be fully compatible.\n"])
elif self.FW[1] == 4:
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
@ -1120,7 +1120,7 @@ class GbxDevice:
return False
return True
def ReadInfo(self, setPinsAsInputs=False):
def ReadInfo(self, setPinsAsInputs=False, checkRtc=False):
if not self.IsConnected(): raise Exception("Couldnt access the the device.")
data = {}
self.POS = 0
@ -1244,7 +1244,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 update to the custom high compatibility firmware by Lesserkuma and try again.".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 update to the high compatibility firmware and try again.".format(APPNAME, self.GetFullName()), "abortable":False})
return False
# Firmware check CFW
@ -1539,19 +1539,19 @@ class GbxDevice:
save_size = 32 * 1024
read_command = 'GBA_READ_SRAM'
write_command = 'GBA_WRITE_SRAM'
elif save_type == 4: # SRAM 64 KB
elif save_type == 7: # SRAM 64 KB
save_size = 64 * 1024
read_command = 'GBA_READ_SRAM'
write_command = 'GBA_WRITE_SRAM'
elif save_type == 5: # SRAM 128 KB
elif save_type == 8: # SRAM 128 KB
save_size = 128 * 1024
read_command = 'GBA_READ_SRAM'
write_command = 'GBA_WRITE_SRAM'
bank_count = 2
elif save_type == 6: # FLASH 64 KB
elif save_type == 4: # FLASH 64 KB
save_size = 64 * 1024
read_command = 'GBA_READ_SRAM'
elif save_type == 7: # FLASH 128 KB
elif save_type == 5: # FLASH 128 KB
save_size = 128 * 1024
read_command = 'GBA_READ_SRAM'
bank_count = 2
@ -1560,7 +1560,7 @@ class GbxDevice:
# Get Save Flash Manufacturer
maker_id = None
if save_type == 6 or save_type == 7:
if save_type == 4 or save_type == 5:
maker_id = self.ReadFlashSaveMakerID()
if maker_id == "ATMEL" and mode == 3:
transfer_size = 128
@ -1611,9 +1611,9 @@ class GbxDevice:
if endAddr > (startAddr + save_size): endAddr = startAddr + save_size
if save_type == 1 or save_type == 2: # EEPROM
self.set_number(eeprom_size, self.DEVICE_CMD["GBA_SET_EEPROM_SIZE"])
elif save_type == 5: # 1M SRAM
elif save_type == 8: # 1M SRAM
self.gbx_flash_write_address_byte(0x1000000, bank)
elif (save_type == 6 or save_type == 7) and bank > 0: # FLASH
elif (save_type == 4 or save_type == 5) and bank > 0: # FLASH
self.set_number(bank, self.DEVICE_CMD["GBA_FLASH_SET_BANK"])
self.set_number(startAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
@ -1668,7 +1668,7 @@ class GbxDevice:
self.wait_for_ack()
elif self.MODE == "AGB":
if save_type == 6 or save_type == 7: # FLASH
if save_type == 4 or save_type == 5: # FLASH
if maker_id == "ATMEL":
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_ATMEL"], data)
self.wait_for_ack()
@ -1809,9 +1809,9 @@ class GbxDevice:
elif self.MODE == "AGB":
if bank > 0:
if (save_type == 5) and bank > 0: # 1M SRAM
if (save_type == 8) and bank > 0: # 1M SRAM
self.gbx_flash_write_address_byte(0x1000000, 0)
elif (save_type == 6 or save_type == 7) and bank > 0: # FLASH
elif (save_type == 4 or save_type == 5) and bank > 0: # FLASH
self.set_number(0, self.DEVICE_CMD["GBA_FLASH_SET_BANK"])
if mode == 2:
@ -1918,20 +1918,20 @@ class GbxDevice:
# Firmware check R23+
# Firmware check R25+
if self.MODE == "AGB" and "single_write" in flashcart_meta["commands"] and flashcart_meta["commands"]["single_write"] == [['PA', 0x70], ['PA', 0x10], ['PA', 'PD']] and (([ 0xB0, 0x00, 0xE2, 0x00 ] in flashcart_meta["flash_ids"]) or ([ 0xB0, 0x00, 0xB0, 0x00 ] in flashcart_meta["flash_ids"])): # Dev
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 check for firmware updates in the Tools menu or the makers website.".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 check for firmware updates in the Tools menu or the makers website.".format(APPNAME, self.GetFullName()), "abortable":False})
return False
# Firmware check R25+
# Firmware check R28+
if (int(self.FW[0]) >= 28) and self.MODE == "AGB" and ("flash_ids" in flashcart_meta and ([ 0x89, 0x00, 0x89, 0x00, 0x18, 0x00, 0x18, 0x00 ] in flashcart_meta["flash_ids"])): # Flash2Advance
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 check for firmware updates in the Tools menu or the makers website.".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 check for firmware updates in the Tools menu or the makers website.".format(APPNAME, self.GetFullName()), "abortable":False})
return False
# Firmware check R25+
# Firmware check
if self.MODE == "AGB" and "buffer_write" in flashcart_meta["commands"] and flashcart_meta["commands"]["buffer_write"] == [['SA', 0xEA], ['SA', 'BS'], ['PA', 'PD'], ['SA', 0xD0], ['SA', 0xFF]]:
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 check for firmware updates in the Tools menu or the makers website.".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 check for firmware updates in the Tools menu or the makers website.".format(APPNAME, self.GetFullName()), "abortable":False})
return False
if self.MODE == "AGB" and "buffer_size" in flashcart_meta and flashcart_meta["buffer_size"] > 256:
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 check for firmware updates in the Tools menu or the makers website.".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 check for firmware updates in the Tools menu or the makers website.".format(APPNAME, self.GetFullName()), "abortable":False})
return False
if self.MODE == "DMG" and "single_write" in flashcart_meta["commands"] and "buffer_write" in flashcart_meta["commands"] and flashcart_meta["commands"]["buffer_write"] != [['SA', 0xE8], ['SA', 'BS'], ['PA', 'PD'], ['SA', 0xD0], ['SA', 0xFF]]:
print("NOTE: Update your GBxCart RW firmware to version L1 or higher for a better transfer rate with this cartridge.")
@ -1941,7 +1941,7 @@ class GbxDevice:
del flashcart_meta["commands"]["buffer_write"]
#if "dmg-mmsa-jpn" in flashcart_meta:
# self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Flashing GB Memory cartridges is currently only supported via the high compatibility firmware which you can install from the Tools menu.", "abortable":False})
# self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Flashing GB Memory cartridges is currently only supported via the high compatibility firmware which you can install from the Tools menu.", "abortable":False})
# return False
# Firmware check
#dprint(flashcart_meta)
@ -2024,7 +2024,7 @@ class GbxDevice:
# Check if write command exists and quit if not
if "single_write" not in flashcart_meta["commands"] and "buffer_write" not in flashcart_meta["commands"]:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM flashing, however you can try the high compatibility firmware which is available through the firmware updater in the Tools menu.", "abortable":False})
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM writing using the current firmware version. However, it may be supported via the high compatibility firmware which is available in GUI mode from the “Tools” menu.", "abortable":False})
return False
# Chip Erase
@ -2252,7 +2252,7 @@ class GbxDevice:
pos += 32
else:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Buffer writing for this flash chip is not supported with your devices firmware version. You can try the high compatibility firmware which is available through the firmware updater in the Tools menu.\n\n{:s}".format(str(flashcart_meta["commands"]["buffer_write"])), "abortable":False})
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Buffer writing for this flash chip is not supported with your devices firmware version. You can try the high compatibility firmware which is available through the firmware updater in the Tools menu.\n\n{:s}".format(str(flashcart_meta["commands"]["buffer_write"])), "abortable":False})
return False
elif self.MODE == "AGB":
@ -2359,7 +2359,7 @@ class GbxDevice:
pos += 256
else:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Buffer writing for this flash chip is not supported with your devices firmware version. You can try the high compatibility firmware which is available through the firmware updater in the Tools menu.\n\n{:s}".format(str(flashcart_meta["commands"]["buffer_write"])), "abortable":False})
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Buffer writing for this flash chip is not supported with your devices firmware version. You can try the high compatibility firmware which is available through the firmware updater in the Tools menu.\n\n{:s}".format(str(flashcart_meta["commands"]["buffer_write"])), "abortable":False})
return False
elif "single_write" in flashcart_meta["commands"]:

Binary file not shown.

View File

@ -83,11 +83,14 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- 29LV Series Flash BOY with 29LV160DB
- BUNG Doctor GB Card 64M
- DIY cart with AM29F010
- DIY cart with AM29F016
- DIY cart with AM29F032
- DIY cart with AM29F040
- DIY cart with AM29F080
- DIY cart with AT49F040
- DIY cart with MBC1 and AM29F080
- DIY cart with MBC3 and MX29LV640
- DIY cart with MX29LV640
- DIY cart with SST39SF040
- DMG-MBC5-32M-FLASH Development Cartridge, E201264
- Ferrante Crafts cart with SST39SF010A
- GB-CART32K-A with SST39SF020A
@ -196,6 +199,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- AGB-E05-01 with GL128S
- AGB-E05-01 with MSP55LV128M
- AGB-E05-01 with MX29GL128FHT2I-90G
- AGB-E05-01 with S29GL064
- AGB-E05-02 with JS28F128
- AGB-E05-02 with M29W128FH
- AGB-E05-02 with M29W128GH
@ -204,8 +208,10 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- AGB-E20-30 with M29W128GH
- AGB-E20-30 with S29GL256N10TFI01
- AGB-SD-E05 with MSP55LV128
- B100 with MX29LV640ET
- B104 with MSP55LV128
- B11 with 26L6420MC-90
- B54 with MX29LV320ET
- BX2006_0106_NEW with S29GL128N10TFI01
- BX2006_TSOP_64BALL with GL128S
- BX2006_TSOP_64BALL with GL256S
@ -263,6 +269,7 @@ The author would like to thank the following very kind people for their help and
- dyf2007 (flash chip info)
- easthighNerd (feature suggestions)
- EchelonPrime (flash chip info)
- EmperorOfTigers (bug reports)
- endrift (research, mGBA emulator)
- eveningmoose (flash chip info)
- frarees (bug reports)
@ -276,6 +283,7 @@ The author would like to thank the following very kind people for their help and
- Jayro (flash chip info)
- JFox (help with properly packaging the app for pip, Linux help)
- joyrider3774 (flash chip info)
- JS7457 (flash chip info)
- julgr (macOS help, testing)
- litlemoran (flash chip info)
- LovelyA72 (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.6",
version="3.7",
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",