mirror of
https://github.com/lesserkuma/FlashGBX.git
synced 2026-08-25 02:05:14 -05:00
3.29
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -202,7 +202,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||
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")))
|
||||
@@ -1613,7 +1613,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||
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)
|
||||
|
||||
@@ -129,8 +129,8 @@ class Flashcart:
|
||||
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):
|
||||
|
||||
@@ -7,9 +7,9 @@ from enum import Enum
|
||||
|
||||
# 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 = ""
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"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,
|
||||
@@ -11,11 +13,8 @@
|
||||
"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":[
|
||||
@@ -29,7 +28,7 @@
|
||||
"read_cfi":[
|
||||
[ 0xAA, 0x98 ]
|
||||
],
|
||||
"chip_erase":[
|
||||
"_chip_erase":[
|
||||
[ 0xAAA, 0xA9 ],
|
||||
[ 0x555, 0x56 ],
|
||||
[ 0xAAA, 0x80 ],
|
||||
@@ -37,7 +36,7 @@
|
||||
[ 0x555, 0x56 ],
|
||||
[ 0xAAA, 0x10 ]
|
||||
],
|
||||
"chip_erase_wait_for":[
|
||||
"_chip_erase_wait_for":[
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
[ null, null, null ],
|
||||
|
||||
@@ -683,26 +683,26 @@ class GbxDevice:
|
||||
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:
|
||||
@@ -1100,11 +1100,13 @@ class GbxDevice:
|
||||
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)})
|
||||
@@ -1379,9 +1381,6 @@ class GbxDevice:
|
||||
|
||||
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)))
|
||||
@@ -1389,6 +1388,11 @@ class GbxDevice:
|
||||
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):
|
||||
@@ -3292,7 +3296,7 @@ class GbxDevice:
|
||||
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"]):
|
||||
@@ -3365,7 +3369,7 @@ class GbxDevice:
|
||||
|
||||
# ↓↓↓ 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:
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
2
setup.py
2
setup.py
@@ -8,7 +8,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
|
||||
|
||||
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",
|
||||
|
||||
Reference in New Issue
Block a user