diff --git a/CHANGES.md b/CHANGES.md
index 7ffa597..c51a6e5 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,11 @@
# Release notes
+### v3.37 (released 2024-03-01)
+- Bundles GBxCart RW v1.4/v1.4a firmware version R42+L11 (improves flash cart compatibility)
+- Added support for F0095_4G_V1 with F0095H0 *(thanks Ausar)*
+- Improved support for 256M29EWH (no PCB text)
+- Confirmed and improved support for 512M29EWH (no PCB text)
+- Minor bug fixes and improvements
+
### v3.36 (released 2024-01-15)
- Added support for DIY cart with 28F016S5 *(thanks alexbc2999)*
- Fixed a problem with reading Sachen cartridges *(thanks xukkorz)*
diff --git a/FlashGBX/FlashGBX.py b/FlashGBX/FlashGBX.py
index 47e3c62..6f74fae 100644
--- a/FlashGBX/FlashGBX.py
+++ b/FlashGBX/FlashGBX.py
@@ -129,7 +129,7 @@ def main(portableMode=False):
ap_cli2.add_argument("--dmg-romsize", choices=["auto", "32kb", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb"], type=str.lower, default="auto", help="set size of Game Boy cartridge ROM data")
ap_cli2.add_argument("--dmg-mbc", 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", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "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-romsize", choices=["auto", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb", "512mb"], 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", "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="don’t stop if invalid data found in cartridge header data")
diff --git a/FlashGBX/FlashGBX_CLI.py b/FlashGBX/FlashGBX_CLI.py
index d01772f..f8cc45b 100644
--- a/FlashGBX/FlashGBX_CLI.py
+++ b/FlashGBX/FlashGBX_CLI.py
@@ -593,7 +593,7 @@ def DetectCartridge(self, limitVoltage=False):
header = self.CONN.ReadInfo()
self.ReadCartridge(header)
ret = self.CONN.DetectCartridge(limitVoltage=limitVoltage, checkSaveType=True)
- (header, _, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, _, flash_id) = ret
+ (header, _, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, _, flash_id, detected_size) = ret
# Save Type
if save_type is None:
@@ -645,7 +645,10 @@ def DetectCartridge(self, limitVoltage=False):
(flash_id, cfi_s, _) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage, cart_type=supp_cart_types[1][cart_type])
msg_cart_type_s = "Cartridge Type: Supported flash cartridge – compatible with:\n{:s}\n".format(msg_cart_type)
- if "flash_size" in supp_cart_types[1][cart_type_id]:
+ if detected_size > 0:
+ size = detected_size
+ msg_flash_size_s = "ROM Size: {:s}\n".format(Util.formatFileSize(size=size, asInt=True))
+ elif "flash_size" in supp_cart_types[1][cart_type_id]:
size = supp_cart_types[1][cart_type_id]["flash_size"]
msg_flash_size_s = "ROM Size: {:s}\n".format(Util.formatFileSize(size=size, asInt=True))
@@ -741,7 +744,7 @@ def BackupROM(self, args, header):
if args.agb_romsize == "auto":
rom_size = header["rom_size"]
else:
- sizes = [ "auto", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb" ]
+ sizes = [ "auto", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb", "512mb" ]
rom_size = Util.AGB_Header_ROM_Sizes_Map[sizes.index(args.agb_romsize) - 1]
if args.path != "auto":
@@ -855,8 +858,8 @@ def FlashROM(self, args, header):
path = args.path
try:
- if os.path.getsize(path) > 0x10000000: # reject too large files to avoid exploding RAM
- print("{:s}ROM files bigger than 256 MiB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
+ if os.path.getsize(path) > 0x20000000: # reject too large files to avoid exploding RAM
+ print("{:s}ROM files bigger than 512 MiB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
return
elif os.path.getsize(path) < 0x400:
print("{:s}ROM files smaller than 1 KiB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py
index 55bb7fe..a6616df 100644
--- a/FlashGBX/FlashGBX_GUI.py
+++ b/FlashGBX/FlashGBX_GUI.py
@@ -690,7 +690,7 @@ def ConnectDevice(self):
dev.SetWriteDelay(enable=str(self.SETTINGS.value("WriteDelay", default="disabled")).lower() == "enabled")
qt_app.processEvents()
self.CONN = dev
- self.CONN.SetTimeout(float(self.SETTINGS.value("SerialTimeout", default="0.5")))
+ self.CONN.SetTimeout(float(self.SETTINGS.value("SerialTimeout", default="1")))
self.optDMG.setAutoExclusive(False)
self.optAGB.setAutoExclusive(False)
if "DMG" in self.CONN.GetSupprtedModes():
@@ -1258,8 +1258,8 @@ def FlashROM(self, dpath=""):
if os.path.getsize(path) == 0:
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The selected ROM file is empty.", QtWidgets.QMessageBox.Ok)
return
- if os.path.getsize(path) > 0x10000000: # reject too large files to avoid exploding RAM
- QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "ROM files bigger than 256 MiB are not supported.", QtWidgets.QMessageBox.Ok)
+ if os.path.getsize(path) > 0x20000000: # reject too large files to avoid exploding RAM
+ QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "ROM files bigger than 512 MiB are not supported.", QtWidgets.QMessageBox.Ok)
return
with open(path, "rb") as file:
@@ -2339,7 +2339,7 @@ def DetectCartridge(self, canSkipMessage=False):
self.DisconnectDevice()
cart_type = None
else:
- (header, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, _, flash_id) = ret
+ (header, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, _, flash_id, detected_size) = ret
# Save Type
if not canSkipMessage:
@@ -2436,7 +2436,10 @@ def DetectCartridge(self, canSkipMessage=False):
msg_cart_type_s_detail = "Compatible Cartridge Types:
{:s}
".format(msg_cart_type)
found_supported = True
- if "flash_size" in supp_cart_types[1][cart_type_id]:
+ if detected_size > 0:
+ size = detected_size
+ msg_flash_size_s = "ROM Size: {:s}
".format(Util.formatFileSize(size=size, asInt=True))
+ elif "flash_size" in supp_cart_types[1][cart_type_id]:
size = supp_cart_types[1][cart_type_id]["flash_size"]
msg_flash_size_s = "ROM Size: {:s}
".format(Util.formatFileSize(size=size, asInt=True))
diff --git a/FlashGBX/Flashcart.py b/FlashGBX/Flashcart.py
index d1ed87a..8f8be1c 100644
--- a/FlashGBX/Flashcart.py
+++ b/FlashGBX/Flashcart.py
@@ -133,6 +133,10 @@ def GetFlashSize(self, default=False):
if "flash_size" not in self.CONFIG: return default
return self.CONFIG["flash_size"]
+ def SetFlashSize(self, size):
+ if "flash_size" not in self.CONFIG: return
+ self.CONFIG["flash_size"] = size
+
def GetBufferSize(self):
if "buffer_size" in self.CONFIG:
return self.CONFIG["buffer_size"]
@@ -450,11 +454,17 @@ def SectorErase(self, pos=0, buffer_pos=0):
else:
return self.CONFIG["sector_size"]
+ def HasBanks(self):
+ return "flash_bank_select_type" in self.CONFIG
+
def SelectBankROM(self, index):
if "flash_bank_select_type" not in self.CONFIG: return False
+ dprint(f"Setting flash bank to {index:d}")
if self.CONFIG["flash_bank_select_type"] == 1:
- dprint(self.GetName(), "|", index)
+ index = index & 0xF
self.CartWrite([[2, index << 4]], sram=True)
+ self.CartWrite([[3, 0x40]], sram=True)
+ self.CartWrite([[4, 0x00]], sram=True)
return True
elif self.CONFIG["flash_bank_select_type"] == 2: # Flash2Advance Ultra
bank1 = 0 if index < 4 else 0x10
diff --git a/FlashGBX/Util.py b/FlashGBX/Util.py
index 1242926..da3590e 100644
--- a/FlashGBX/Util.py
+++ b/FlashGBX/Util.py
@@ -7,19 +7,19 @@
# Common constants
APPNAME = "FlashGBX"
-VERSION_PEP440 = "3.36"
+VERSION_PEP440 = "3.37"
VERSION = "v{:s}".format(VERSION_PEP440)
-VERSION_TIMESTAMP = 1705328830
+VERSION_TIMESTAMP = 1709318129
DEBUG = False
DEBUG_LOG = []
APP_PATH = ""
CONFIG_PATH = ""
-AGB_Header_ROM_Sizes = [ "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB", "8 MiB", "16 MiB", "32 MiB", "64 MiB", "128 MiB", "256 MiB" ]
-AGB_Header_ROM_Sizes_Map = [ 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000 ]
+AGB_Header_ROM_Sizes = [ "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB", "8 MiB", "16 MiB", "32 MiB", "64 MiB", "128 MiB", "256 MiB", "512 MiB" ]
+AGB_Header_ROM_Sizes_Map = [ 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000 ]
AGB_Header_Save_Types = [ "None", "4K EEPROM (512 Bytes)", "64K EEPROM (8 KiB)", "256K SRAM/FRAM (32 KiB)", "512K FLASH (64 KiB)", "1M FLASH (128 KiB)", "8M DACS (1 MiB)", "Unlicensed 512K SRAM (64 KiB)", "Unlicensed 1M SRAM (128 KiB)", "Unlicensed Batteryless SRAM" ]
AGB_Header_Save_Sizes = [ 0, 512, 8192, 32768, 65536, 131072, 1048576, 65536, 131072, 0 ]
-AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS", 0xBF5B:"Unlicensed SST SST49LF080A", 0xFFFF:"Unlicensed 0xFFFF" }
+AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS", 0xBF5B:"Unlicensed SST49LF080A", 0xFFFF:"Unlicensed 0xFFFF" }
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000 ]
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x0F:'MBC3+RTC+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x110:'MBC30+RTC+SRAM+BATTERY', 0x12:'MBC3+SRAM', 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:'MAC-GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper' }
@@ -314,30 +314,27 @@ def formatProgressTimeShort(sec):
sec %= 60
return "{:02d}:{:02d}:{:02d}".format(int(hr), int(min), int(sec))
-def formatProgressTime(sec, asFloat=False):
- if int(sec) == 1:
- return "{:d} second".format(int(sec))
- elif sec < 60:
- if sec < 1 and asFloat:
- return "{:.2f} seconds".format(sec)
- else:
- return "{:d} seconds".format(int(sec))
- elif int(sec) == 60:
- return "1 minute"
- else:
- min = int(sec / 60)
- sec = int(sec % 60)
- s = str(min) + " "
- if min == 1:
- s = s + "minute"
- else:
- s = s + "minutes"
- s = s + ", " + str(sec) + " "
- if sec == 1:
- s = s + "second"
- else:
- s = s + "seconds"
- return s
+def formatProgressTime(seconds, asFloat=False):
+ hr = int(seconds // 3600)
+ min = int((seconds // 60) - (60 * hr))
+ sec = seconds % 60
+
+ if seconds < 1 and asFloat:
+ return "{:.2f} seconds".format(seconds)
+ s = ""
+ if hr > 0:
+ s += "{:d} hour".format(hr)
+ if hr != 1: s += "s"
+ s += ", "
+ if min > 0:
+ s += "{:d} minute".format(min)
+ if min != 1: s += "s"
+ s += ", "
+ if sec >= 1 or seconds < 60:
+ s += "{:d} second".format(int(sec))
+ if sec != 1: s += "s"
+ s += ", "
+ return s[:-2]
def formatPathOS(path, end_sep=False):
if platform.system() == "Windows":
diff --git a/FlashGBX/config/db_AGB.json b/FlashGBX/config/db_AGB.json
index 9778580..6f531f2 100644
--- a/FlashGBX/config/db_AGB.json
+++ b/FlashGBX/config/db_AGB.json
@@ -489,8 +489,8 @@
"gc": "A2YE",
"rc": 3754462526,
"rs": 4194304,
- "st": 4,
- "ss": 65536,
+ "st": 0,
+ "ss": 0,
"lg": "En,Fr,De,Es,It",
"rg": "USA"
},
@@ -21226,11 +21226,11 @@
"lg": "Ja",
"rg": "Japan"
},
- "f71e3df4bf5f4c9000b9e3c931696bf237e6cac6": {
+ "414992293e84502d31cbc612234fbb15427e9a81": {
"gn": "Fear Factor - Unleashed",
"ne": "(USA)",
"gc": "BFUE",
- "rc": 310993820,
+ "rc": 3214000765,
"rs": 8388608,
"st": 1,
"ss": 512,
diff --git a/FlashGBX/config/db_DMG.json b/FlashGBX/config/db_DMG.json
index c25fa57..4fa6199 100644
--- a/FlashGBX/config/db_DMG.json
+++ b/FlashGBX/config/db_DMG.json
@@ -207,6 +207,15 @@
"lg": "En,Fr,De,Es,It",
"rg": "Europe"
},
+ "70d6c1b7b0dab7f6dec9dbc4eae454a19a37d6cd": {
+ "gn": "Trip World DX",
+ "ne": "(USA) (Limited Run Games)",
+ "gc": "",
+ "rc": 2937566187,
+ "rs": 524288,
+ "lg": "En",
+ "rg": "USA"
+ },
"bdf78e28adb73b3bec1c232bb5102cd9cb6128c5": {
"gn": "Tutty",
"ne": "(Europe) (Demo)",
diff --git a/FlashGBX/config/fc_AGB_F0095H0.txt b/FlashGBX/config/fc_AGB_F0095H0.txt
new file mode 100644
index 0000000..893ae7a
--- /dev/null
+++ b/FlashGBX/config/fc_AGB_F0095H0.txt
@@ -0,0 +1,52 @@
+{
+ "type":"AGB",
+ "names":[
+ "F0095_4G_V1 with F0095H0"
+ ],
+ "flash_ids":[
+ [ 0x8A, 0x00, 0xB0, 0x88 ]
+ ],
+ "voltage":3.3,
+ "flash_size":0x20000000,
+ "flash_bank_size":0x2000000,
+ "flash_bank_select_type":1,
+ "sector_size":0x40000,
+ "reset_every":0x400000,
+ "buffer_size":1024,
+ "command_set":"INTEL",
+ "commands":{
+ "reset":[
+ [ 0, 0x50 ],
+ [ 0, 0xFF ]
+ ],
+ "read_identifier":[
+ [ 0, 0x90 ]
+ ],
+ "sector_erase":[
+ [ "SA", 0x60 ],
+ [ "SA", 0xD0 ],
+ [ "SA", 0x20 ],
+ [ "SA", 0xD0 ]
+ ],
+ "sector_erase_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ "SA", 0x80, 0x80 ]
+ ],
+ "buffer_write":[
+ [ "SA", 0xEA ],
+ [ "SA", "BS" ],
+ [ "PA", "PD" ],
+ [ "SA", 0xD0 ],
+ [ "SA", 0xFF ]
+ ],
+ "buffer_write_wait_for":[
+ [ "SA", 0x80, 0x80 ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ "SA", 0x80, 0x80 ],
+ [ null, null, null ]
+ ]
+ }
+}
diff --git a/FlashGBX/config/fc_DMG_256M29EWH.txt b/FlashGBX/config/fc_DMG_256M29EWH.txt
index 1de6ab9..b6c6f48 100644
--- a/FlashGBX/config/fc_DMG_256M29EWH.txt
+++ b/FlashGBX/config/fc_DMG_256M29EWH.txt
@@ -1,9 +1,11 @@
{
"type":"DMG",
"names":[
- "256M29EWH (no PCB text)"
+ "256M29EWH (no PCB text)",
+ "512M29EWH (no PCB text)"
],
"flash_ids":[
+ [ 0x8A, 0x8A, 0x7D, 0x7D ],
[ 0x8A, 0x8A, 0x7D, 0x7D ]
],
"voltage":3.3,
@@ -13,6 +15,7 @@
"first_bank":1,
"chip_erase_timeout":120,
"sector_size_from_cfi":true,
+ "buffer_size":256,
"reset_every":0x800000,
"mbc":0x201,
"write_pin":"WR",
@@ -59,7 +62,23 @@
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
- [ "SA", 0xFF, 0xFF ]
+ [ "SA", 0x80, 0x80 ]
+ ],
+ "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", 0xFF ]
],
"single_write":[
[ 0xAAA, 0xA9 ],
diff --git a/FlashGBX/config/fc_DMG_28F016S5.txt b/FlashGBX/config/fc_DMG_28F016S5.txt
new file mode 100644
index 0000000..1ace364
--- /dev/null
+++ b/FlashGBX/config/fc_DMG_28F016S5.txt
@@ -0,0 +1,43 @@
+{
+ "type":"DMG",
+ "names":[
+ "DIY cart with 28F016S5 @ AUDIO"
+ ],
+ "flash_ids":[
+ [ 0x89, 0xAA ]
+ ],
+ "voltage":5,
+ "flash_size":0x200000,
+ "sector_size":0x10000,
+ "start_addr":0,
+ "first_bank":1,
+ "write_pin":"AUDIO",
+ "mbc":"manual",
+ "command_set":"INTEL",
+ "commands":{
+ "reset":[
+ [ 0, 0xFF ]
+ ],
+ "read_identifier":[
+ [ 0, 0x90 ]
+ ],
+ "sector_erase":[
+ [ 0x4000, 0x20 ],
+ [ 0x4000, 0xD0 ]
+ ],
+ "sector_erase_wait_for":[
+ [ null, null, null ],
+ [ 0x4000, 0x80, 0x80 ]
+ ],
+ "single_write":[
+ [ "PA", 0x70 ],
+ [ "PA", 0x40 ],
+ [ "PA", "PD" ]
+ ],
+ "single_write_wait_for":[
+ [ 0, 0x80, 0x80 ],
+ [ null, null, null ],
+ [ null, null, null ]
+ ]
+ }
+}
diff --git a/FlashGBX/config/fc_DMG_Generic_AUDIO_555_A9.txt b/FlashGBX/config/fc_DMG_Generic_AUDIO_555_A9.txt
new file mode 100644
index 0000000..badb241
--- /dev/null
+++ b/FlashGBX/config/fc_DMG_Generic_AUDIO_555_A9.txt
@@ -0,0 +1,54 @@
+{
+ "type":"DMG",
+ "names":[
+ "Generic Flash Cartridge (AUDIO/555/A9)"
+ ],
+ "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, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 0x555, 0x90 ]
+ ],
+ "read_cfi":[
+ [ 0x555, 0x98 ]
+ ],
+ "chip_erase":[
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 0x555, 0x80 ],
+ [ 0x555, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 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, 0xA9 ],
+ [ 0x2AA, 0x56 ],
+ [ 0x555, 0xA0 ],
+ [ "PA", "PD" ]
+ ],
+ "single_write_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ]
+ ]
+ }
+}
diff --git a/FlashGBX/config/fc_DMG_Generic_AUDIO_AAA_A9.txt b/FlashGBX/config/fc_DMG_Generic_AUDIO_AAA_A9.txt
new file mode 100644
index 0000000..2058292
--- /dev/null
+++ b/FlashGBX/config/fc_DMG_Generic_AUDIO_AAA_A9.txt
@@ -0,0 +1,54 @@
+{
+ "type":"DMG",
+ "names":[
+ "Generic Flash Cartridge (AUDIO/AAA/A9)"
+ ],
+ "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, 0xA9 ],
+ [ 0x555, 0x56 ],
+ [ 0xAAA, 0x90 ]
+ ],
+ "read_cfi":[
+ [ 0xAAA, 0x98 ]
+ ],
+ "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, 0xFF, 0xFF ]
+ ],
+ "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 ]
+ ]
+ }
+}
diff --git a/FlashGBX/config/fc_DMG_Sillyhatday_4MB_2x29F016.txt b/FlashGBX/config/fc_DMG_Sillyhatday_4MB_2x29F016.txt
index 6468656..a00d363 100644
--- a/FlashGBX/config/fc_DMG_Sillyhatday_4MB_2x29F016.txt
+++ b/FlashGBX/config/fc_DMG_Sillyhatday_4MB_2x29F016.txt
@@ -1,7 +1,7 @@
{
"type":"DMG",
"names":[
- "Sillyhatday MBC5-DUAL-FLASH-4/8MB"
+ "Sillyhatday MBC5-DUAL-FLASH-4MB"
],
"voltage":5,
"flash_size":0x800000,
diff --git a/FlashGBX/config/fc_DMG_Sillyhatday_8MB_4x29F016.txt b/FlashGBX/config/fc_DMG_Sillyhatday_8MB_4x29F016.txt
new file mode 100644
index 0000000..3034dbc
--- /dev/null
+++ b/FlashGBX/config/fc_DMG_Sillyhatday_8MB_4x29F016.txt
@@ -0,0 +1,111 @@
+{
+ "type":"DMG",
+ "names":[
+ "Sillyhatday MBC5-QUAD-FLASH-8MB"
+ ],
+ "voltage":5,
+ "flash_size":0x800000,
+ "start_addr":0x4000,
+ "first_bank":0,
+ "write_pin":"AUDIO",
+ "chip_erase_timeout":120,
+ "mbc":0x1B,
+ "command_set":"AMD",
+ "commands":{
+ "reset":[
+ [ 0x4000, 0xF0 ]
+ ],
+ "read_identifier":[
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x90 ]
+ ],
+ "read_cfi":[
+ [ 0x4AAA, 0x98 ]
+ ],
+ "chip_erase":[
+ [ 0x3000, 0x00, "WR" ],
+ [ 0x2100, 0x00, "WR" ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x80 ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x10 ],
+ [ 0x3000, 0x00, "WR" ],
+ [ 0x2100, 0x80, "WR" ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x80 ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x10 ],
+ [ 0x3000, 0x01, "WR" ],
+ [ 0x2100, 0x00, "WR" ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x80 ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x10 ],
+ [ 0x3000, 0x01, "WR" ],
+ [ 0x2100, 0x80, "WR" ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x80 ],
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0x10 ],
+ [ 0x3000, 0x00, "WR" ],
+ [ 0x2100, 0x00, "WR" ]
+ ],
+ "chip_erase_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ 0x4000, 0xFF, 0xFF ],
+ [ null, null, null ],
+ [ 0x4000, 0xFF, 0xFF ]
+ ],
+ "single_write":[
+ [ 0x4555, 0xAA ],
+ [ 0x42AA, 0x55 ],
+ [ 0x4555, 0xA0 ],
+ [ "PA", "PD" ]
+ ],
+ "single_write_wait_for":[
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ],
+ [ null, null, null ]
+ ]
+ }
+}
diff --git a/FlashGBX/config/fc_DMG_iG_8MB.txt b/FlashGBX/config/fc_DMG_iG_8MB.txt
index 1f53f00..1743b23 100644
--- a/FlashGBX/config/fc_DMG_iG_8MB.txt
+++ b/FlashGBX/config/fc_DMG_iG_8MB.txt
@@ -2,7 +2,7 @@
"type":"DMG",
"names":[
"insideGadgets 8 MiB",
- "FunnyPlaying MidnightTrace 4 MiB Flash Cart"
+ "FunnyPlaying MidnightTrace Flash Cart"
],
"flash_ids":[
[ 0x01, 0x01, 0x7E, 0x7E ],
diff --git a/FlashGBX/hw_GBxCartRW.py b/FlashGBX/hw_GBxCartRW.py
index e1e22d8..5cfebcc 100644
--- a/FlashGBX/hw_GBxCartRW.py
+++ b/FlashGBX/hw_GBxCartRW.py
@@ -17,7 +17,7 @@ class GbxDevice:
DEVICE_NAME = "GBxCart RW"
DEVICE_MIN_FW = 1
DEVICE_MAX_FW = 10
- DEVICE_LATEST_FW_TS = { 4:1686057604, 5:1681900614, 6:1681900614 }
+ DEVICE_LATEST_FW_TS = { 4:1709317610, 5:1707258786, 6:1707258786 }
DEVICE_CMD = {
"NULL":0x30,
@@ -47,6 +47,7 @@ class GbxDevice:
"CLK_LOW":0xAA,
"ENABLE_PULLUPS":0xAB,
"DISABLE_PULLUPS":0xAC,
+ "GET_VARIABLE":0xAD,
"DMG_CART_READ":0xB1,
"DMG_CART_WRITE":0xB2,
"DMG_CART_WRITE_SRAM":0xB3,
@@ -116,7 +117,7 @@ class GbxDevice:
BAUDRATE = 1000000
MAX_BUFFER_READ = 0x2000
MAX_BUFFER_WRITE = 0x400
- DEVICE_TIMEOUT = 0.75
+ DEVICE_TIMEOUT = 1
WRITE_DELAY = False
READ_ERRORS = 0
@@ -405,7 +406,8 @@ def SetWriteDelay(self, enable=True):
dprint("Setting Write Delay to", enable)
self.WRITE_DELAY = enable
- def SetTimeout(self, seconds=0.5):
+ def SetTimeout(self, seconds=1):
+ if seconds < 1: seconds = 1
self.DEVICE_TIMEOUT = seconds
self.DEVICE.timeout = self.DEVICE_TIMEOUT
@@ -420,11 +422,10 @@ def wait_for_ack(self, values=None):
if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
return False
elif buffer is False:
- print("{:s}Error: The USB connection timed out.{:s}".format(ANSI.RED, ANSI.RESET))
- dprint("Timeout error ({:s}(), line {:d})".format(stack.name, stack.lineno))
+ dprint("Timeout error ({:s}(), line {:d}): {:f}".format(stack.name, stack.lineno, self.DEVICE.timeout))
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))
+ dprint("Communication error ({:s}(), line {:d}): {:s}".format(stack.name, stack.lineno, str(buffer)))
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
@@ -839,13 +840,13 @@ def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True):
ret = self.AutoDetectFlash(limitVoltage=limitVoltage)
if ret is False: return False
- (cart_types, cart_type_id, flash_id, cfi_s, cfi) = ret
+ (cart_types, cart_type_id, flash_id, cfi_s, cfi, detected_size) = ret
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
cart_type = supported_carts[cart_type_id]
if self.MODE == "DMG" and "command_set" in cart_type and cart_type["command_set"] == "DMG-MBC5-32M-FLASH":
checkSaveType = False
elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
- save_size == 65536
+ save_size = 65536
save_type = 7
checkSaveType = False
elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 1:
@@ -875,14 +876,14 @@ def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True):
if mbc == 0x20: # MBC6
save_size = 1081344
save_type = 0x104
- return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
+ return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
elif mbc == 0x22: # MBC7
save_type = 0x102
save_size = 512
elif mbc == 0xFD: # TAMA5
save_size = 32
save_type = 0x103
- return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
+ return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':save_type, 'rtc':False }
elif self.MODE == "AGB":
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':8, 'rtc':False }
@@ -997,7 +998,7 @@ def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True):
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
self.INFO["last_action"] = 0
self.INFO["action"] = None
- return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
+ return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
def CheckBatterylessSRAM(self):
bl_size = None
@@ -1713,6 +1714,7 @@ def AutoDetectFlash(self, limitVoltage=False):
# Check flash size
flash_type_id = 0
+ detected_size = 0
cfi_s = ""
cfi = None
if len(flash_types) > 0:
@@ -1732,7 +1734,37 @@ def AutoDetectFlash(self, limitVoltage=False):
size_undetected = True
if size_undetected:
- if isinstance(cfi, dict) and "device_size" in cfi:
+ if "flash_bank_select_type" in supp_flash_types[1][flash_types[0]] and supp_flash_types[1][flash_types[0]]["flash_bank_select_type"] == 1:
+ # Check where the ROM data repeats (by bank switching)
+ fc_fncptr = {
+ "cart_write_fncptr":self._cart_write,
+ "cart_write_fast_fncptr":self._cart_write_flash,
+ "cart_read_fncptr":self.ReadROM,
+ "cart_powercycle_fncptr":self.CartPowerCycle,
+ "progress_fncptr":self.SetProgress,
+ "set_we_pin_wr":self._set_we_pin_wr,
+ "set_we_pin_audio":self._set_we_pin_audio,
+ }
+ flashcart = Flashcart(config=supp_flash_types[1][flash_types[0]], fncptr=fc_fncptr)
+ flashcart.SelectBankROM(0)
+ size_check = self.ReadROM(0, 0x1000) + self.ReadROM(0x1FFF000, 0x1000)
+ num_banks = 1
+ while num_banks < (flashcart.GetFlashSize() // 0x2000000) + 1:
+ dprint("Checking bank {:d}".format(num_banks))
+ flashcart.SelectBankROM(num_banks)
+ buffer = self.ReadROM(0, 0x1000) + self.ReadROM(0x1FFF000, 0x1000)
+ if buffer == size_check: break
+ num_banks <<= 1
+ detected_size = 0x2000000 * num_banks
+ for i in range(0, len(flash_types)):
+ if detected_size == supp_flash_types[1][flash_types[i]]["flash_size"]:
+ dprint("Detected {:d} flash banks".format(num_banks))
+ flash_type_id = flash_types[i]
+ size_undetected = False
+ break
+ flashcart.SelectBankROM(0)
+
+ elif isinstance(cfi, dict) and "device_size" in cfi:
for i in range(0, len(flash_types)):
if "flash_size" in supp_flash_types[1][flash_types[i]] and cfi['device_size'] == supp_flash_types[1][flash_types[i]]["flash_size"]:
flash_type_id = flash_types[i]
@@ -1762,8 +1794,8 @@ def AutoDetectFlash(self, limitVoltage=False):
if self.MODE == "DMG" and not flash_id_found:
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
time.sleep(0.1)
-
- return (flash_types, flash_type_id, flash_id, cfi_s, cfi)
+
+ return (flash_types, flash_type_id, flash_id, cfi_s, cfi, detected_size)
def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most horribly written function
if cart_type is not None:
@@ -2160,7 +2192,7 @@ def _BackupROM(self, args):
else:
rom_banks = 1
rom_bank_size = 0x2000000
-
+
is_3dmemory = (self.MODE == "AGB" and "command_set" in cart_type and cart_type["command_set"] == "3DMEMORY")
if "verify_write" in args:
@@ -2306,7 +2338,7 @@ def _BackupROM(self, args):
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}. Decreasing maximum transfer buffer size to 0x{:X}.".format(buffer_len, pos_temp, max_length >> 1))
max_length >>= 1
self.MAX_BUFFER_READ = max_length
- err_text += "\nBuffer size adjusted to {:d} bytes...".format(max_length)
+ err_text += "\nBuffer size adjusted to {:d} bytes.".format(max_length)
if ".dev" in Util.VERSION_PEP440 and not Util.DEBUG: print(err_text)
self.INFO["dump_info"]["transfer_size"] = max_length
@@ -2804,7 +2836,7 @@ def _BackupRestoreRAM(self, args):
if agb_flash_chip == 0x1F3D: # Atmel AT29LV512
self.WriteRAM(address=int(pos/128), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
else:
- dprint("pos=0x{:X}, sector_address={:d}".format(pos, sector_address))
+ dprint("Erasing flash save sector; pos=0x{:X}, sector_address=0x{:X}".format(pos, sector_address))
cmds = [
[ 0x5555, 0xAA ],
[ 0x2AAA, 0x55 ],
@@ -2823,8 +2855,10 @@ def _BackupRestoreRAM(self, args):
if sr == 0xFFFF: break
lives -= 1
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
+ errmsg = "Warning: Save data flash sector at 0x{:X} didn’t erase successfully (SR={:04X}).".format(bank*0x10000 + pos, sr)
+ print(errmsg)
+ dprint(errmsg)
+ break
if buffer[buffer_offset:buffer_offset+buffer_len] != bytearray([0xFF] * buffer_len):
if ("ereader" in self.INFO and self.INFO["ereader"] is True and sector_address == 0xF000):
self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+0xF80], command=command, max_length=0x80)
@@ -3393,8 +3427,9 @@ def _FlashROM(self, args):
if len(sector_offsets) > 0:
flash_capacity = sector_offsets[-1][0] + sector_offsets[-1][1]
if flash_capacity < len(data_import) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]):
- self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"There are not enough flash sectors available to write this ROM. The maximum capacity is {:s}.".format(Util.formatFileSize(size=flash_capacity, asInt=False)), "abortable":False})
- return False
+ #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"There are not enough flash sectors available to write this ROM. The maximum capacity is {:s}.".format(Util.formatFileSize(size=flash_capacity, asInt=False)), "abortable":False})
+ #return False
+ sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size)
sector_offsets_hash = base64.urlsafe_b64encode(hashlib.sha1(str(sector_offsets).encode("UTF-8")).digest()).decode("ASCII", "ignore")[:4]
@@ -3495,7 +3530,7 @@ def _FlashROM(self, args):
start_bank = 0
start_address = 0
buffer_pos = 0
- retry_hp = 100
+ retry_hp = 0
end_address = len(data_import)
dprint("ROM banks:", end_bank)
@@ -3503,10 +3538,18 @@ def _FlashROM(self, args):
write_sectors = [[ 0, len(data_import) ]]
elif write_sectors is None or len(write_sectors) == 0:
write_sectors = sector_offsets
+
+ if len(write_sectors) == 0:
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Coulnd’t start writing ROM because the flash cart couldn’t be detected properly.", "abortable":False})
+ return False
for sector in write_sectors:
if chip_erase is False:
- retry_hp = 100
+ if retry_hp == 0:
+ retry_hp = 15 # First sector
+ else:
+ retry_hp = 100 # Other sectors
+
if self.MODE == "AGB":
dprint("Writing sector:", hex(sector[0]), hex(sector[1]))
buffer_pos = sector[0]
@@ -3576,7 +3619,6 @@ def _FlashROM(self, args):
flashcart.SelectBankROM(bank)
temp = end_address - start_address
start_address %= cart_type["flash_bank_size"]
- #end_address = start_address + temp
end_address = min(cart_type["flash_bank_size"], start_address + temp)
# ↑↑↑ Switch ROM bank
@@ -3637,22 +3679,42 @@ def _FlashROM(self, args):
if status is False or se_ret is False:
self.CANCEL = True
self.ERROR = True
+ sr = "Unknown"
+ if (self.FW["pcb_ver"] in (5, 6) and self.FW["fw_ver"] >= 11):
+ lives = 3
+ while lives > 0:
+ dprint("Retrieving last status register value...")
+ self.DEVICE.reset_input_buffer()
+ self.DEVICE.reset_output_buffer()
+ self._write(self.DEVICE_CMD["GET_VARIABLE"])
+ self._write(2)
+ self._write(3)
+ sr = self._read(2)
+ if sr not in (False, None) and len(sr) == 2:
+ sr = "0x{:X}".format(struct.unpack(">H", sr)[0])
+ break
+ dprint("Erroneous response:", sr)
+ lives -= 1
+ if lives == 0:
+ sr = "Timeout"
+ dprint("Last status register value:", sr)
+
if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
break
- elif buffer_pos == 0 or not self.DEVICE.is_open or self.DEVICE is None:
- self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), errmsg_mbc_selection)})
+ elif not self.DEVICE.is_open or self.DEVICE is None:
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}\n\nStatus Register: {:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), errmsg_mbc_selection, sr)})
break
else:
if chip_erase: retry_hp = 0
if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
retry_hp -= 5
if retry_hp <= 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}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables".format(buffer_len, self.ERROR_ARGS["iteration"], buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False))})
+ 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}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n\nStatus Register: {:s}".format(buffer_len, self.ERROR_ARGS["iteration"], buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), sr)})
continue
else:
retry_hp -= 10
if retry_hp <= 0:
- self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(size=len(data_import), asInt=False), errmsg_mbc_selection), "abortable":False})
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}\n\nStatus Register: {:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(size=len(data_import), asInt=False), errmsg_mbc_selection, sr), "abortable":False})
continue
rev_buffer_pos = sector_offsets[sector_pos - 1][0]
@@ -3665,7 +3727,7 @@ def _FlashROM(self, args):
pos = end_address
status = False
- self.SetProgress({"action":"ERROR", "abortable":True, "pos":buffer_pos, "text":"Write error! Retrying from 0x{:X}...".format(rev_buffer_pos)})
+ self.SetProgress({"action":"ERROR", "abortable":True, "pos":buffer_pos, "text":err_text})
delay = 0.5 + (100-retry_hp)/50
if self.CanPowerCycleCart():
self.CartPowerOff()
diff --git a/FlashGBX/res/config.zip b/FlashGBX/res/config.zip
index ca46f93..0f038ab 100644
Binary files a/FlashGBX/res/config.zip and b/FlashGBX/res/config.zip differ
diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_3.zip b/FlashGBX/res/fw_GBxCart_RW_v1_3.zip
index 018bcf0..22eeef3 100644
Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_3.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_3.zip differ
diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_4.zip b/FlashGBX/res/fw_GBxCart_RW_v1_4.zip
index 38631b0..d9d12d8 100644
Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_4.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_4.zip differ
diff --git a/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip b/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip
index bbf6fb4..1a1bce9 100644
Binary files a/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip and b/FlashGBX/res/fw_GBxCart_RW_v1_4a.zip differ
diff --git a/README.md b/README.md
index 978f9d1..d521ff1 100644
--- a/README.md
+++ b/README.md
@@ -165,6 +165,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- 2006_TSOP_64BALL_QFP48 with AL016J55FFAR2
- 256M29EWH (no PCB text)
- 36VF3204 and ALTERA CPLD (no PCB text)
+ - 512M29EWH (no PCB text)
- DMG-DHCN-20 with MX29LV320ET
- DMG-GBRW-20 with 29LV320ETMI-70G
- DRV with 29LV320DB and ALTERA CPLD
@@ -278,6 +279,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- BX2006_TSOPBGA_6108 with M29W640
- DV15 with MSP55LV100G
- F864-3 with M36L0R7050B
+ - F0095_4G_V1 with F0095H0
- GA-07 with unlabeled flash chip
- GE28F128W30 with 128W30B0
- M36XXX_T32_32D_16D with M36L0R806
@@ -323,7 +325,7 @@ Many different reproduction cartridges share their flash chip command set, so ev
The author would like to thank the following very kind people for their help, contributions or documentation (in alphabetical order):
-2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr
+2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, Ausar, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, Jayro, Jenetrix, JFox, joyrider3774, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, Sithdown, skite2001, Smelly-Ghost, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr
## DISCLAIMER
diff --git a/setup.py b/setup.py
index b7fdf75..e5fde94 100644
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@
setuptools.setup(
name="FlashGBX",
- version="3.36",
+ version="3.37",
author="Lesserkuma",
description="Reads and writes Game Boy and Game Boy Advance cartridge data using the GBxCart RW by insideGadgets",
url="https://github.com/lesserkuma/FlashGBX",