Skip to content

Commit

Permalink
3.29
Browse files Browse the repository at this point in the history
  • Loading branch information
lesserkuma committed May 14, 2023
1 parent c94e76d commit 97d4a3a
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 32 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Release notes
### v3.29 (released 2023-05-14)
- Fixed an issue with writing to some flash cartridges using the old GBxCart RW v1.3 hardware revision *(thanks yosoo)*
- Fixed an issue with Batteryless SRAM save data restore *(thanks antPL)*
- Confirmed support for SD007_TSOP_64BALL_SOP28_V2 with unlabeled flash chip *(thanks DevDavisNunez)*
- Added support for SD007_BGA48_71TV_T28_DEEP with M29W640 *(thanks Cristóbal)*
- Minor bug fixes and improvements

### v3.28 (released 2023-05-05)
- Improved support for the BennVenn MBC3000 RTC cart; can now write to Real Time Clock registers
- Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums
Expand Down
4 changes: 2 additions & 2 deletions FlashGBX/FlashGBX_GUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def __init__(self, args):
self.mnuConfig.addAction("Check for &updates on application startup", lambda: [ self.EnableUpdateCheck() ])
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", lambda: self.SETTINGS.setValue("PreferChipErase", str(self.mnuConfig.actions()[2].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("Extra data &verification", lambda: self.SETTINGS.setValue("VerifyData", str(self.mnuConfig.actions()[3].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("&Verify written data", lambda: self.SETTINGS.setValue("VerifyData", str(self.mnuConfig.actions()[3].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
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.addAction("Limit &baud rate to 1Mbps for GBxCart RW v1.4 devices", lambda: [ self.SETTINGS.setValue("LimitBaudRate", str(self.mnuConfig.actions()[5].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")), self.SetLimitBaudRate() ])
self.mnuConfig.addAction("Always &generate ROM dump reports", lambda: self.SETTINGS.setValue("GenerateDumpReports", str(self.mnuConfig.actions()[6].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
Expand Down Expand Up @@ -1613,7 +1613,7 @@ def WriteRAM(self, dpath="", erase=False, test=False):
if self.CONN.GetMode() == "DMG" and mbc in (0x10, 0x110) and not self.CONN.IsClkConnected():
rtc = False
elif erase or Util.save_size_includes_rtc(mode=self.CONN.GetMode(), mbc=mbc, save_size=filesize, save_type=save_type):
msg = "A Real Time Clock cartridge was detected. Do you want the Real Time Clock register values to be also written?"
msg = "A Real Time Clock cartridge was detected. Do you want the Real Time Clock register values to be written as well?"
cb = QtWidgets.QCheckBox("&Adjust RTC", checked=True)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel)
msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
Expand Down
4 changes: 2 additions & 2 deletions FlashGBX/Flashcart.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ def WEisWR_RESET(self):
if "write_pin" not in self.CONFIG: return False
return (self.CONFIG["write_pin"] == "WR+RESET")

def GetFlashSize(self):
if "flash_size" not in self.CONFIG: return False
def GetFlashSize(self, default=False):
if "flash_size" not in self.CONFIG: return default
return self.CONFIG["flash_size"]

def GetBufferSize(self):
Expand Down
9 changes: 5 additions & 4 deletions FlashGBX/Util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

# Common constants
APPNAME = "FlashGBX"
VERSION_PEP440 = "3.28"
VERSION_PEP440 = "3.29"
VERSION = "v{:s}".format(VERSION_PEP440)
VERSION_TIMESTAMP = 1683283512
VERSION_TIMESTAMP = 1684066518
DEBUG = False
DEBUG_LOG = []
APP_PATH = ""
Expand Down Expand Up @@ -841,13 +841,14 @@ def get_mbc_name(id):
def save_size_includes_rtc(mode, mbc, save_size, save_type):
rtc_size = 0x10
if mode == "DMG":
save_type = DMG_Header_RAM_Sizes_Map.index(save_type)
if get_mbc_name(mbc) in ("MBC3", "MBC30"): rtc_size = 0x30
elif get_mbc_name(mbc) == "HuC-3": rtc_size = 0x0C
elif get_mbc_name(mbc) == "TAMA5": rtc_size = 0x28
return (((DMG_Header_RAM_Sizes_Flasher_Map[save_type] + rtc_size) % save_size) != rtc_size)
return (((DMG_Header_RAM_Sizes_Flasher_Map[save_type] + rtc_size) % save_size) == 0)
elif mode == "AGB":
rtc_size = 0x10
return (((AGB_Header_Save_Sizes[save_type] + rtc_size) % save_size) != rtc_size)
return (((AGB_Header_Save_Sizes[save_type] + rtc_size) % save_size) == 0)
return False

def validate_datetime_format(string, format):
Expand Down
6 changes: 4 additions & 2 deletions FlashGBX/config/fc_DMG_GL032M11BAIR4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"SD007_48BALL_64M with GL032M11BAIR4",
"SD007_48BALL_64M_V2 with GL032M11BAIR4",
"S29GL032N90T and ALTERA CPLD (MBC5)",
"29LV320TE and ALTERA CPLD (no PCB text)"
"29LV320TE and ALTERA CPLD (no PCB text)",
"SD007_TSOP_64BALL_SOP28_V2 with unlabeled flash chip"
],
"flash_ids":[
[ 0x02, 0x02, 0x7D, 0x7D ],
[ 0x02, 0x02, 0x7D, 0x7D ],
[ 0x02, 0x02, 0x7D, 0x7D ],
[ 0x04, 0x04, 0xF5, 0xF5 ]
[ 0x04, 0x04, 0xF5, 0xF5 ],
[ 0x02, 0x02, 0x7D, 0x7D ]
],
"voltage":3.3,
"flash_size":0x400000,
Expand Down
15 changes: 7 additions & 8 deletions FlashGBX/config/fc_DMG_M29W640.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
{
"type":"DMG",
"names":[
"SD007_48BALL_64M with M29W640"
"SD007_48BALL_64M with M29W640",
"SD007_BGA48_71TV_T28_DEEP with M29W640"
],
"flash_ids":[
[ 0x20, 0x00, 0x7D, 0x00 ],
[ 0x20, 0x00, 0x7D, 0x00 ]
],
"voltage":3.3,
"flash_size":0x400000,
"start_addr":0,
"first_bank":1,
"write_pin":"WR",
"sector_size":[
[0x02000, 8],
[0x10000, 127]
],
"chip_erase_timeout":70,
"sector_size_from_cfi":true,
"_chip_erase_timeout":70,
"command_set":"AMD",
"commands":{
"reset":[
Expand All @@ -29,15 +28,15 @@
"read_cfi":[
[ 0xAA, 0x98 ]
],
"chip_erase":[
"_chip_erase":[
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0x80 ],
[ 0xAAA, 0xA9 ],
[ 0x555, 0x56 ],
[ 0xAAA, 0x10 ]
],
"chip_erase_wait_for":[
"_chip_erase_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
Expand Down
28 changes: 16 additions & 12 deletions FlashGBX/hw_GBxCartRW.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,26 +683,26 @@ def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
if Util.DEBUG:
with open("debug_header.bin", "wb") as f: f.write(header)
if header is False or len(header) != 0x180:
print("{:s}\n{:s}Couldn’t read the cartridge information. Please try again.{:s}".format(str(header), ANSI.RED, ANSI.RESET))
return False

# Parse ROM header
if self.MODE == "DMG":
data = RomFileDMG(header).GetHeader()
if data["game_title"] == "TETRIS" and hashlib.sha1(header).digest() != bytearray([0x1D, 0x69, 0x2A, 0x4B, 0x31, 0x7A, 0xA5, 0xE9, 0x67, 0xEE, 0xC2, 0x2F, 0xCC, 0x32, 0x43, 0x8C, 0xCB, 0xC5, 0x78, 0x0B]): # Sachen
if "game_title" in data and data["game_title"] == "TETRIS" and hashlib.sha1(header).digest() != bytearray([0x1D, 0x69, 0x2A, 0x4B, 0x31, 0x7A, 0xA5, 0xE9, 0x67, 0xEE, 0xC2, 0x2F, 0xCC, 0x32, 0x43, 0x8C, 0xCB, 0xC5, 0x78, 0x0B]): # Sachen
header = self.ReadROM(0, 0x280)
data = RomFileDMG(header).GetHeader()
if data["logo_correct"] is False and not b"Future Console Design" in header: # workaround for strange bootlegs
if "logo_correct" in data and data["logo_correct"] is False and not b"Future Console Design" in header: # workaround for strange bootlegs
self._cart_write(0, 0xFF)
time.sleep(0.1)
header = self.ReadROM(0, 0x280)
data = RomFileDMG(header).GetHeader()
if data["mapper_raw"] == 0x203 or b"Future Console Design" in header: # Xploder GB version number
if "mapper_raw" in data and data["mapper_raw"] == 0x203 or b"Future Console Design" in header: # Xploder GB version number
self._cart_write(0x0006, 0)
header[0:0x10] = self.ReadROM(0x4000, 0x10)
header[0xD0:0xE0] = self.ReadROM(0x40D0, 0x10)
data = RomFileDMG(header).GetHeader()

if data == {}: return False

data["has_rtc"] = False
data["rtc_string"] = "Not available"
if data["logo_correct"] is True:
Expand Down Expand Up @@ -1100,11 +1100,13 @@ def ReadROM(self, address, length, skip_init=False, max_length=64):
elif self.MODE == "AGB":
command = "AGB_CART_READ"

for _ in range(0, num):
for n in range(0, num):
self._write(self.DEVICE_CMD[command])
temp = self._read(length)
if isinstance(temp, int): temp = bytearray([temp])
if temp is False or len(temp) != length: return bytearray()
if temp is False or len(temp) != length:
print("{:s}Error while trying to read 0x{:X} bytes from cartridge ROM at 0x{:X} in iteration {:d} of {:d}:{:s}\n{:s}".format(ANSI.RED, length, address, n, num, ANSI.RESET, str(temp)))
return bytearray()
buffer += temp
if self.INFO["action"] in (self.ACTIONS["ROM_READ"], self.ACTIONS["SAVE_READ"], self.ACTIONS["ROM_WRITE_VERIFY"]) and not self.NO_PROG_UPDATE:
self.SetProgress({"action":"READ", "bytes_added":len(temp)})
Expand Down Expand Up @@ -1379,16 +1381,18 @@ def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, ru

if ret != 0x03: self._write(self.DEVICE_CMD["FLASH_PROGRAM"])
ret = self._write(data, wait=True)
if rumble_stop and i == 0:
dprint("Sending rumble stop command")
self._cart_write(address=0xC6, value=0x00, flashcart=True)

if ret not in (0x01, 0x03):
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)

if rumble_stop and pos % flash_buffer_size == 0:
dprint("Sending rumble stop command")
self._cart_write(address=0xC6, value=0x00, flashcart=True)
rumble_stop = False

address += length
if ((pos % length) * 10 == 0) and (self.INFO["action"] in (self.ACTIONS["ROM_WRITE"], self.ACTIONS["SAVE_WRITE"]) and not self.NO_PROG_UPDATE):
Expand Down Expand Up @@ -3292,7 +3296,7 @@ def _FlashROM(self, args):
flash_capacity = len(data_import)
if sector_map is not None and sector_map is not False:
smallest_sector_size = flashcart.GetSmallestSectorSize()
sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size)
sector_offsets = flashcart.GetSectorOffsets(rom_size=flashcart.GetFlashSize(default=len(data_import)), rom_bank_size=rom_bank_size)
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"]):
Expand Down Expand Up @@ -3365,7 +3369,7 @@ def _FlashROM(self, args):

# ↓↓↓ Chip erase
chip_erase = False
if flashcart.SupportsChipErase():
if flashcart.SupportsChipErase() and not flash_offset > 0:
if flashcart.SupportsSectorErase() and args["prefer_chip_erase"] is False and sector_map is not False:
chip_erase = False
else:
Expand Down
Binary file modified FlashGBX/res/config.zip
Binary file not shown.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- SD007_48BALL_64M_V6 with 29DL163BD-90
- SD007_48BALL_64M_V8 with M29W160ET
- SD007_48BALL_SOP28 with M29W320ET
- SD007_BGA48_71TV_T28_DEEP with M29W640
- SD007_BV5 with 29LV160TE-70PFTN
- SD007_BV5_DRV with M29W320DT
- SD007_BV5_DRV with S29GL032M90TFIR4
Expand Down Expand Up @@ -207,6 +208,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
- SD007_TSOP_64BALL_SOJ28 with unlabeled flash chip
- SD007_TSOP_64BALL_SOP28 with EN29LV160AB-70TCP
- SD007_TSOP_64BALL_SOP28 with unlabeled flash chip
- SD007_TSOP_64BALL_SOP28_V2 with unlabeled flash chip
- SD008-6810-512S with MSP55LV512
- SD008-6810-V4 with MX29GL256EL
- SD008-6810-V5 with MX29CL256FH
Expand Down Expand Up @@ -302,7 +304,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, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, 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, KOOORAY, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, 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, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, Zeii, Zelante, zvxr
2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, bbsan, BennVenn, ccs21, ClassicOldSong, CodyWick13, Corborg, Cristóbal, crizzlycruz, Därk, Davidish, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, 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, KOOORAY, kscheel, kyokohunter, litlemoran, LovelyA72, Luca DS, LucentW, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, 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, Timville, twitnic, velipso, Veund, voltagex, Voultar, wickawack, Wkr, x7l7j8cc, xactoes, yosoo, Zeii, Zelante, zvxr

## DISCLAIMER

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setuptools.setup(
name="FlashGBX",
version="3.28",
version="3.29",
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",
Expand Down

0 comments on commit 97d4a3a

Please sign in to comment.