This commit is contained in:
Lesserkuma
2021-01-16 21:12:50 +01:00
parent f402510a60
commit 05e71cbdab
11 changed files with 14808 additions and 14617 deletions

View File

@@ -11,7 +11,7 @@ from . import hw_GBxCartRW
hw_devices = [hw_GBxCartRW]
APPNAME = "FlashGBX"
VERSION_PEP440 = "1.1"
VERSION_PEP440 = "1.2"
VERSION = "v{:s}".format(VERSION_PEP440)
class FlashGBX(QtWidgets.QWidget):
@@ -258,7 +258,7 @@ class FlashGBX(QtWidgets.QWidget):
if os.path.exists(self.CONFIG_PATH + "/" + zfile):
zfile_crc = zip.getinfo(zfile).CRC
with open(self.CONFIG_PATH + "/" + zfile, "rb") as ofile: buffer = ofile.read()
ofile_crc = zlib.crc32(buffer) & 0xffffffff
ofile_crc = zlib.crc32(buffer) & 0xFFFFFFFF
if zfile_crc == ofile_crc: continue
os.rename(self.CONFIG_PATH + "/" + zfile, self.CONFIG_PATH + "/" + zfile + "_" + datetime.now().strftime("%Y%m%d%H%M%S") + ".bak")
rf_list += zfile + "\n"
@@ -795,7 +795,7 @@ class FlashGBX(QtWidgets.QWidget):
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }");
self.lblStatus4a.setText("Done!")
#msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle=APPNAME, text="The ROM was dumped successfully!", standardButtons=QtWidgets.QMessageBox.Ok)
msgbox.setText("The ROM was dumped successfully!")
msgbox.setText("The ROM backup is complete and the checksum was verified successfully!")
if not dontShowAgain:
msgbox.exec()
dontShowAgain = cb.isChecked()
@@ -809,7 +809,7 @@ class FlashGBX(QtWidgets.QWidget):
self.lblAGBHeaderROMChecksumResult.setText("Valid (0x{:06X})".format(self.AGB_Global_CRC32))
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }");
self.lblStatus4a.setText("Done!")
msgbox.setText("The ROM was dumped successfully!")
msgbox.setText("The ROM backup is complete and the checksum was verified successfully!")
if not dontShowAgain:
msgbox.exec()
dontShowAgain = cb.isChecked()
@@ -818,12 +818,12 @@ class FlashGBX(QtWidgets.QWidget):
self.lblAGBHeaderROMChecksumResult.setText("0x{:06X}".format(self.CONN.INFO["rom_checksum_calc"]))
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderCGBResult.styleSheet())
self.lblStatus4a.setText("Done!")
QtWidgets.QMessageBox.information(self, APPNAME, "The ROM was dumped successfully, but its integrity could not be verified as this ROM is not in the database.", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.information(self, APPNAME, "The ROM backup is complete!", QtWidgets.QMessageBox.Ok)
else:
self.lblAGBHeaderROMChecksumResult.setText("Invalid (0x{:06X}≠0x{:06X})".format(self.CONN.INFO["rom_checksum_calc"], self.AGB_Global_CRC32))
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }");
self.lblStatus4a.setText("Done.")
QtWidgets.QMessageBox.warning(self, APPNAME, "The ROM was dumped, but the checksum doesnt match the known database entry. This may indicate a bad dump, however this can be normal for some reproduction cartridges, prototypes and patched games.", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.warning(self, APPNAME, "The ROM backup is complete, but the checksum doesnt match the known database entry. This may indicate a bad dump, however this can be normal for some reproduction cartridges, prototypes and patched games.", QtWidgets.QMessageBox.Ok)
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
self.lblStatus4a.setText("Done!")
@@ -877,7 +877,7 @@ class FlashGBX(QtWidgets.QWidget):
QtWidgets.QMessageBox.critical(self, APPNAME, "No flash cartridge type configuration files found. Try to restart the application with the “--reset” switch to reset the configuration.", QtWidgets.QMessageBox.Ok)
return 0
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="Would you like " + APPNAME + " to try and auto-detect the flash cartridge type?\n(Original game cartridges can not be re-written.)", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle=APPNAME, text="Would you like " + APPNAME + " to try and auto-detect the flash cartridge type?\n(Official game cartridges can not be re-written.)", standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
cb = QtWidgets.QCheckBox("Limit voltage to 3.3V", checked=True)
if self.CONN.GetMode() == "DMG":
msgbox.setCheckBox(cb)
@@ -898,11 +898,11 @@ class FlashGBX(QtWidgets.QWidget):
limitVoltage = False
if answer == QtWidgets.QMessageBox.Yes:
(flashid, cfi_s, cfi) = self.CONN.CheckFlashChip(limitVoltage)
(flash_id, cfi_s, cfi) = self.CONN.CheckFlashChip(limitVoltage)
if cfi_s == "":
QtWidgets.QMessageBox.information(self, APPNAME, "There was no Common Flash Interface (CFI) response from the cartridge. If a flash chip exists on the cartridge PCB, it may be too old or require unique unlocking and handling.", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.information(self, APPNAME, "Flash chip query result: <pre>" + flash_id + "</pre>There was no Common Flash Interface (CFI) response from the cartridge. If a flash chip exists on the cartridge PCB, it may be too old or require unique unlocking and handling.", QtWidgets.QMessageBox.Ok)
else:
QtWidgets.QMessageBox.information(self, APPNAME, "Flash chip query result: <pre>" + flashid + "</pre><pre>" + str(cfi_s) + "</pre>", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.information(self, APPNAME, "Flash chip query result: <pre>" + flash_id + "</pre><pre>" + str(cfi_s) + "</pre>", QtWidgets.QMessageBox.Ok)
with open(self.CONFIG_PATH + "/cfi.bin", "wb") as f: f.write(cfi['raw'])
return 0
else:
@@ -948,11 +948,11 @@ class FlashGBX(QtWidgets.QWidget):
msgbox.setEscapeButton(button_cancel)
answer = msgbox.exec()
if msgbox.clickedButton() == button_cfi:
(flashid, cfi_s, cfi) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage, cart_type=cart_types[1][cart_type])
(flash_id, cfi_s, cfi) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage, cart_type=cart_types[1][cart_type])
if cfi_s == "":
QtWidgets.QMessageBox.information(self, APPNAME, "There was no Common Flash Interface (CFI) response from the cartridge. If a flash chip exists on the cartridge PCB, it may be too old or require unique unlocking and handling.", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.information(self, APPNAME, "Flash chip query result: <pre>" + flash_id + "</pre>There was no Common Flash Interface (CFI) response from the cartridge. If a flash chip exists on the cartridge PCB, it may be too old or require unique unlocking and handling.", QtWidgets.QMessageBox.Ok)
else:
QtWidgets.QMessageBox.information(self, APPNAME, "Flash chip query result: <pre>" + flashid + "</pre><pre>" + str(cfi_s) + "</pre>", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.information(self, APPNAME, "Flash chip query result: <pre>" + flash_id + "</pre><pre>" + str(cfi_s) + "</pre>", QtWidgets.QMessageBox.Ok)
with open(self.CONFIG_PATH + "/cfi.bin", "wb") as f: f.write(cfi['raw'])
elif msgbox.clickedButton() == button_cancel: return 0
@@ -1319,9 +1319,6 @@ class FlashGBX(QtWidgets.QWidget):
def ReadCartridge(self, resetStatus=True):
if not self.CheckDeviceAlive(): return
if self.CONN.CheckROMStable() is False:
QtWidgets.QMessageBox.warning(self, APPNAME, "Unstable ROM reading detected. Please reconnect the device, make sure you selected the correct mode and that the cartridge contacts are clean.", QtWidgets.QMessageBox.Ok)
data = self.CONN.ReadInfo()
if data == False or len(data) == 0:
@@ -1334,10 +1331,7 @@ class FlashGBX(QtWidgets.QWidget):
self.cmbDMGCartridgeTypeResult.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
if "flash_type" in data:
self.cmbDMGCartridgeTypeResult.setCurrentIndex(data["flash_type"])
if "manufacturer" in data:
self.lblChipManufacturerResult.setText(data['manufacturer'])
self.lblChipIDResult.setText("0x{:02X}".format(data['chip_id']))
self.lblHeaderTitleResult.setText(data['game_title'])
if data['sgb'] in self.DMG_Header_SGB:
self.lblHeaderSGBResult.setText(self.DMG_Header_SGB[data['sgb']])
@@ -1374,6 +1368,20 @@ class FlashGBX(QtWidgets.QWidget):
if k == 0x05 or k == 0x06: self.cmbHeaderRAMSizeResult.setCurrentIndex(1) # MBC2 Save
i += 1
if data['empty'] == True: # defaults
self.lblHeaderTitleResult.setText("(No ROM data detected)")
self.lblHeaderTitleResult.setStyleSheet("QLabel { color: red; }");
#self.lblHeaderSGBResult.setText("")
#self.lblHeaderCGBResult.setText("")
#self.lblHeaderLogoValidResult.setText("")
#self.lblHeaderChecksumResult.setText("")
#self.lblHeaderROMChecksumResult.setText("")
self.cmbHeaderROMSizeResult.setCurrentIndex(11)
self.cmbHeaderRAMSizeResult.setCurrentIndex(0)
self.cmbHeaderFeaturesResult.setCurrentIndex(0)
else:
self.lblHeaderTitleResult.setStyleSheet(self.lblHeaderCGBResult.styleSheet())
self.grpAGBCartridgeInfo.setVisible(False)
self.grpDMGCartridgeInfo.setVisible(True)
@@ -1425,10 +1433,10 @@ class FlashGBX(QtWidgets.QWidget):
print("FAIL: Database for Game Boy Advance titles not found in " + "{0:s}/db_AGB.json".format(self.CONFIG_PATH))
if db_agb_entry != None:
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(self.AGB_Header_ROM_Sizes_Map.index(db_agb_entry['rom_size']))
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(self.AGB_Header_ROM_Sizes_Map.index(db_agb_entry['rs']))
if data["rom_size_calc"] < 0x400000:
self.lblAGBHeaderROMChecksumResult.setText("In database (0x{:06X})".format(db_agb_entry['rom_crc32']))
self.AGB_Global_CRC32 = db_agb_entry['rom_crc32']
self.lblAGBHeaderROMChecksumResult.setText("In database (0x{:06X})".format(db_agb_entry['rc']))
self.AGB_Global_CRC32 = db_agb_entry['rc']
elif data["rom_size"] != 0:
if not data["rom_size"] in self.AGB_Header_ROM_Sizes_Map:
@@ -1440,8 +1448,22 @@ class FlashGBX(QtWidgets.QWidget):
if data["save_type"] == None:
self.cmbAGBSaveTypeResult.setCurrentIndex(0)
if db_agb_entry != None:
if db_agb_entry['savetype'] < len(self.AGB_Header_Save_Types):
self.cmbAGBSaveTypeResult.setCurrentIndex(db_agb_entry['savetype'])
if db_agb_entry['st'] < len(self.AGB_Header_Save_Types):
self.cmbAGBSaveTypeResult.setCurrentIndex(db_agb_entry['st'])
if data['empty'] == True: # defaults
self.lblAGBHeaderTitleResult.setText("(No ROM data detected)")
self.lblAGBHeaderTitleResult.setStyleSheet("QLabel { color: red; }");
#self.lblAGBHeaderCodeResult.setText("")
#self.lblAGBHeaderVersionResult.setText("")
#self.lblAGBHeaderLogoValidResult.setText("")
#self.lblAGBHeader96hResult.setText("")
#self.lblAGBHeaderChecksumResult.setText("")
#self.lblAGBHeaderROMChecksumResult.setText("")
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(3)
self.cmbAGBSaveTypeResult.setCurrentIndex(0)
else:
self.lblHeaderTitleResult.setStyleSheet(self.lblHeaderCGBResult.styleSheet())
self.grpDMGCartridgeInfo.setVisible(False)
self.grpAGBCartridgeInfo.setVisible(True)
@@ -1455,8 +1477,12 @@ class FlashGBX(QtWidgets.QWidget):
self.FinishOperation()
if self.CONN.CheckROMStable() is False:
QtWidgets.QMessageBox.warning(self, APPNAME, "Unstable ROM reading detected. Please reconnect the device, make sure you selected the correct mode and that the cartridge contacts are clean.", QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.warning(self, APPNAME, "Unstable ROM reading detected. Please make sure that the cartridge contacts are very clean and that you selected the correct mode.", QtWidgets.QMessageBox.Ok)
return
if not data['logo_correct'] and data['empty'] == False:
QtWidgets.QMessageBox.warning(self, APPNAME, "The Nintendo Logo check failed which usually means that the cartridge couldnt be read correctly. Please make sure that the cartridge contacts are very clean and that you selected the correct mode.", QtWidgets.QMessageBox.Ok)
if data['game_title'][:11] == "YJencrypted":
QtWidgets.QMessageBox.warning(self, APPNAME, "This cartridge may be protected against reading or writing a ROM. If you dont want to risk this cartridge to render itself unusable, please do not try to write a new ROM to it.", QtWidgets.QMessageBox.Ok)

View File

@@ -41,6 +41,7 @@ class RomFileAGB:
def GetHeader(self):
buffer = self.ROMFILE
data = {}
data["empty"] = (self.ROMFILE == bytearray([buffer[0]] * len(buffer)))
data["logo_correct"] = hashlib.sha1(buffer[0x04:0xA0]).digest() == bytearray([ 0x17, 0xDA, 0xA0, 0xFE, 0xC0, 0x2F, 0xC3, 0x3C, 0x0F, 0x6A, 0xBB, 0x54, 0x9A, 0x8B, 0x80, 0xB6, 0x61, 0x3B, 0x48, 0xEE ])
game_title = bytearray(buffer[0xA0:0xAC]).decode("ascii", "replace")
game_title = re.sub(r"(\x00+)$", "", game_title).replace("\x00", "_")

View File

@@ -57,6 +57,7 @@ class RomFileDMG:
def GetHeader(self):
buffer = self.ROMFILE
data = {}
data["empty"] = (self.ROMFILE == bytearray([buffer[0]] * len(buffer)))
data["logo_correct"] = hashlib.sha1(buffer[0x104:0x134]).digest() == bytearray([ 0x07, 0x45, 0xFD, 0xEF, 0x34, 0x13, 0x2D, 0x1B, 0x3D, 0x48, 0x8C, 0xFB, 0xDF, 0x03, 0x79, 0xA3, 0x9F, 0xD5, 0x4B, 0x4C ])
game_title = bytearray(buffer[0x134:0x143]).decode("ascii", "replace")
game_title = re.sub(r"(\x00+)$", "", game_title).replace("\x00", "_")

View File

@@ -103,5 +103,5 @@ def ParseCFI(buffer):
def dprint(*args, **kwargs):
# uncomment for some debug prints
#print(" ".join(map(str, args)), **kwargs)
#print(datetime.datetime.now().strftime("[%Y-%m-%d %H:%M:%S]"), " ".join(map(str, args)), **kwargs)
pass

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
{
"type":"DMG",
"names":[
"DIY cart with AT49F040 @ AUDIO"
],
"flash_ids":[
[ 0x1F, 0x13, 0xFE, 0xFF ]
],
"voltage":5,
"flash_size":0x8000,
"start_addr":0,
"first_bank":1,
"write_pin":"AUDIO",
"chip_erase_timeout":60,
"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

@@ -0,0 +1,53 @@
{
"type":"DMG",
"names":[
"DIY cart with AT49F040 @ WR"
],
"flash_ids":[
[ 0x1F, 0x13, 0xFE, 0xFF ]
],
"voltage":5,
"flash_size":0x8000,
"start_addr":0,
"first_bank":1,
"write_pin":"WR",
"chip_erase_timeout":60,
"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

@@ -203,7 +203,7 @@ class GbxDevice:
def Close(self):
if self.IsConnected():
try:
self.set_mode(self.DEVICE_CMD['VOLTAGE_3_3V']) # set to lower voltage when closing for safety
self.set_mode(self.DEVICE_CMD["VOLTAGE_3_3V"]) # set to lower voltage when closing for safety
self.DEVICE.close()
except:
self.DEVICE = None
@@ -225,9 +225,9 @@ class GbxDevice:
return self.PORT
def LoadFirmwareVersion(self):
self.write(self.DEVICE_CMD['READ_FIRMWARE_VERSION'])
self.write(self.DEVICE_CMD["READ_FIRMWARE_VERSION"])
fw = bytearray(self.read(1))[0]
self.write(self.DEVICE_CMD['READ_PCB_VERSION'])
self.write(self.DEVICE_CMD["READ_PCB_VERSION"])
pcb = bytearray(self.read(1))[0]
self.FW = [fw, pcb]
@@ -375,22 +375,22 @@ class GbxDevice:
if self.MODE == "DMG":
if self.FAST_READ and length == 0x4000:
if set_address:
self.set_number(offset, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_mode(self.DEVICE_CMD['READ_ROM_4000H'])
self.set_number(offset, self.DEVICE_CMD["SET_START_ADDRESS"])
self.set_mode(self.DEVICE_CMD["READ_ROM_4000H"])
else:
if set_address:
self.set_number(offset, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_mode(self.DEVICE_CMD['READ_ROM_RAM'])
self.set_number(offset, self.DEVICE_CMD["SET_START_ADDRESS"])
self.set_mode(self.DEVICE_CMD["READ_ROM_RAM"])
buffer = self.read(length, last=True)
elif self.MODE == "AGB":
if self.FAST_READ and length == 0x10000:
if set_address:
self.set_number(math.floor(offset / 2), self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_mode(self.DEVICE_CMD['GBA_READ_ROM_8000H'])
self.set_number(math.floor(offset / 2), self.DEVICE_CMD["SET_START_ADDRESS"])
self.set_mode(self.DEVICE_CMD["GBA_READ_ROM_8000H"])
else:
if set_address:
self.set_number(math.floor(offset / 2), self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_mode(self.DEVICE_CMD['GBA_READ_ROM'])
self.set_number(math.floor(offset / 2), self.DEVICE_CMD["SET_START_ADDRESS"])
self.set_mode(self.DEVICE_CMD["GBA_READ_ROM"])
buffer = self.read(length, last=True)
'''
@@ -430,11 +430,11 @@ class GbxDevice:
def gba_flash_write_address_byte(self, address, data):
address = int(address / 2)
address = format(address, 'x')
buffer = self.DEVICE_CMD['GBA_FLASH_CART_WRITE_BYTE'] + address + '\x00'
buffer = self.DEVICE_CMD["GBA_FLASH_CART_WRITE_BYTE"] + address + '\x00'
self.write(buffer)
time.sleep(0.001)
data = format(data, 'x')
buffer = self.DEVICE_CMD['GBA_FLASH_CART_WRITE_BYTE'] + data + '\x00'
buffer = self.DEVICE_CMD["GBA_FLASH_CART_WRITE_BYTE"] + data + '\x00'
self.write(buffer)
time.sleep(0.001)
ack = self.wait_for_ack()
@@ -445,7 +445,7 @@ class GbxDevice:
def gb_flash_write_address_byte(self, address, data):
address = format(address, 'x')
buffer = self.DEVICE_CMD['GB_FLASH_WRITE_BYTE'] + address + '\x00'
buffer = self.DEVICE_CMD["GB_FLASH_WRITE_BYTE"] + address + '\x00'
self.write(buffer)
buffer = format(data, 'x') + '\x00'
self.write(buffer)
@@ -456,17 +456,17 @@ class GbxDevice:
return False
def gbx_flash_write_data_bytes(self, command, data):
buffer = bytearray(self.DEVICE_CMD[command], "ascii") + bytearray(data)
buffer = bytearray(command, "ascii") + bytearray(data)
self.write(buffer)
def set_bank(self, address, bank):
address = format(address, 'x')
buffer = self.DEVICE_CMD['SET_BANK'] + address + '\x00'
buffer = self.DEVICE_CMD["SET_BANK"] + address + '\x00'
self.write(buffer)
time.sleep(0.005)
bank = format(bank, 'd')
buffer = self.DEVICE_CMD['SET_BANK'] + bank + '\x00'
buffer = self.DEVICE_CMD["SET_BANK"] + bank + '\x00'
self.write(buffer)
time.sleep(0.005)
@@ -474,14 +474,16 @@ class GbxDevice:
dprint("set_mode(command={:s})".format(str(command)))
buffer = format(command, 's')
self.write(buffer)
if command in (self.DEVICE_CMD["VOLTAGE_3_3V"], self.DEVICE_CMD["VOLTAGE_5V"]):
#time.sleep(0.01)
pass
def set_number(self, number, command):
buffer = format(command, 's') + format(int(number), 'x') + '\x00'
self.write(buffer)
if command in ("SET_START_ADDRESS"):
time.sleep(0.005)
elif command in ("VOLTAGE_3_3V", "VOLTAGE_5V"):
time.sleep(0.3)
if command in (self.DEVICE_CMD["SET_START_ADDRESS"]):
#time.sleep(0.005)
pass
def EnableRAM(self, mbc=1, enable=True):
if enable:
@@ -492,28 +494,36 @@ class GbxDevice:
if mbc <= 4: self.set_bank(0x6000, 0)
time.sleep(0.2)
def SetBankROM(self, bank, mbc=5):
dprint("SetBankROM(", bank, ",", mbc, ")", bank & 0xFF, ((bank >> 8) & 0xFF))
def SetBankROM(self, bank, mbc=0, bank_count=0):
dprint("SetBankROM(bank={:d}, mbc={:d}, bank_count={:d})".format(bank, mbc, bank_count))
if mbc == 0 and bank_count == 0: mbc = 5
if mbc == 1: # MBC1
dprint("0x6000=0x00, 0x4000=0x{:X}, 0x2000=0x{:X}".format(bank >> 5, bank & 0x1F))
self.set_bank(0x6000, 0)
self.set_bank(0x4000, bank >> 5)
self.set_bank(0x2000, bank & 0x1F)
elif mbc == 1.1: # Hudson MBC1
self.set_bank(0x4000, bank >> 4)
if (bank < 10):
dprint("0x4000=0x{:X}, 0x2000=0x{:X}".format(bank >> 4, bank & 0x1F))
self.set_bank(0x2000, bank & 0x1F)
else:
dprint("0x4000=0x{:X}, 0x2000=0x{:X}".format(bank >> 4, 0x10 | (bank & 0x1F)))
self.set_bank(0x2000, 0x10 | (bank & 0x1F))
else:
elif (mbc == 0 and bank_count > 256) or mbc == 5: # MBC5
dprint("0x2100=0x{:X}, 0x3000=0x{:X}".format(bank & 0xFF, ((bank >> 8) & 0xFF)))
self.set_bank(0x2100, (bank & 0xFF))
self.set_bank(0x3000, ((bank >> 8) & 0xFF))
else: # MBC2, MBC3 and others
dprint("0x2100=0x{:X}".format(bank & 0xFF))
self.set_bank(0x2100, (bank & 0xFF))
def SetBankRAM(self, bank):
self.set_bank(0x4000, (bank & 0xFF))
def ReadFlashSaveMakerID(self):
makers = { 0x1F:"ATMEL", 0xBF:"SST/SANYO", 0xC2:"MACRONIX", 0x32:"PANASONIC", 0x62:"SANYO" }
self.set_mode(self.DEVICE_CMD['GBA_FLASH_READ_ID'])
self.set_mode(self.DEVICE_CMD["GBA_FLASH_READ_ID"])
time.sleep(0.02)
buffer = self.DEVICE.read(2)
if buffer[0] in makers.keys():
@@ -523,21 +533,24 @@ class GbxDevice:
def SetMode(self, mode):
if mode == "DMG":
self.set_mode(self.DEVICE_CMD['VOLTAGE_5V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
self.MODE = "DMG"
elif mode == "AGB":
self.set_mode(self.DEVICE_CMD['VOLTAGE_3_3V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_3_3V"])
self.MODE = "AGB"
self.set_number(0, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(0, self.DEVICE_CMD["SET_START_ADDRESS"])
def AutoDetectFlash(self, limitVoltage=False):
flash_types = []
flash_type = 0
flash_id = None
if limitVoltage:
self.set_mode(self.DEVICE_CMD['VOLTAGE_3_3V'])
if self.MODE == "DMG":
if limitVoltage:
self.set_mode(self.DEVICE_CMD["VOLTAGE_3_3V"])
else:
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
supported_carts = list(self.SUPPORTED_CARTS['DMG'].values())
for f in range(2, len(supported_carts)):
flashcart_meta = supported_carts[f]
@@ -545,16 +558,16 @@ class GbxDevice:
if flash_id not in flashcart_meta["flash_ids"]:
continue
self.set_mode(self.DEVICE_CMD['GB_CART_MODE'])
self.set_mode(self.DEVICE_CMD["GB_CART_MODE"])
if "flash_commands_on_bank_1" in flashcart_meta:
self.set_mode(self.DEVICE_CMD['GB_FLASH_BANK_1_COMMAND_WRITES'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_BANK_1_COMMAND_WRITES"])
if flashcart_meta["write_pin"] == "WR":
self.set_mode(self.DEVICE_CMD['GB_FLASH_WE_PIN'])
self.set_mode(self.DEVICE_CMD['WE_AS_WR_PIN'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_WE_PIN"])
self.set_mode(self.DEVICE_CMD["WE_AS_WR_PIN"])
elif flashcart_meta["write_pin"] in ("AUDIO", "VIN"):
self.set_mode(self.DEVICE_CMD['GB_FLASH_WE_PIN'])
self.set_mode(self.DEVICE_CMD['WE_AS_AUDIO_PIN'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_WE_PIN"])
self.set_mode(self.DEVICE_CMD["WE_AS_AUDIO_PIN"])
# Unlock Flash
if "unlock" in flashcart_meta["commands"]:
@@ -594,7 +607,7 @@ class GbxDevice:
flash_types.append(flash_type)
if not flash_id_found:
self.set_mode(self.DEVICE_CMD['VOLTAGE_5V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
elif self.MODE == "AGB":
supported_carts = list(self.SUPPORTED_CARTS['AGB'].values())
@@ -644,9 +657,6 @@ class GbxDevice:
return flash_types
def CheckFlashChip(self, limitVoltage=False, cart_type=None):
if limitVoltage:
self.set_mode(self.DEVICE_CMD['VOLTAGE_3_3V'])
flash_id_lines = []
flash_commands = [
{ 'read_cfi':[[0x555, 0x98]], 'read_identifier':[[ 0x555, 0xAA ], [ 0x2AA, 0x55 ], [ 0x555, 0x90 ]], 'reset':[[ 0x0, 0xF0 ]] },
@@ -671,6 +681,10 @@ class GbxDevice:
cfi = {'raw':b''}
if self.MODE == "DMG":
if limitVoltage:
self.set_mode(self.DEVICE_CMD["VOLTAGE_3_3V"])
else:
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
rom_string = "[ ROM ] " + rom_string
we_pins = [ "WR", "AUDIO" ]
else:
@@ -681,8 +695,8 @@ class GbxDevice:
if "method" in cfi: break
for method in flash_commands:
if self.MODE == "DMG":
self.set_mode(self.DEVICE_CMD['GB_FLASH_WE_PIN'])
self.set_mode(self.DEVICE_CMD['WE_AS_' + we + '_PIN'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_WE_PIN"])
self.set_mode(self.DEVICE_CMD["WE_AS_" + we + "_PIN"])
for i in range(0, len(method['reset'])):
self.gbx_flash_write_address_byte(method['reset'][i][0], method['reset'][i][1])
@@ -770,6 +784,32 @@ class GbxDevice:
#s += "\nSHA-1: {:s}".format(cfi["sha1"])
cfi_info = s
if cfi['raw'] == b'':
for we in we_pins:
for method in flash_commands:
for i in range(0, len(method['reset'])):
self.gbx_flash_write_address_byte(method['reset'][i][0], method["reset"][i][1])
for i in range(0, len(method['read_identifier'])):
self.gbx_flash_write_address_byte(method['read_identifier'][i][0], method["read_identifier"][i][1])
flash_id = self.ReadROM(0, 64)[0:8]
for i in range(0, len(method['reset'])):
self.gbx_flash_write_address_byte(method['reset'][i][0], method["reset"][i][1])
if flash_id != check_buffer[0:8]:
if self.MODE == "DMG":
method_string = "[" + we.ljust(5) + "/{:4X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
else:
method_string = "[{:6X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
line_exists = False
for i in range(0, len(flash_id_lines)):
if method_string == flash_id_lines[i][0]: line_exists = True
if not line_exists: flash_id_lines.append([method_string, flash_id])
for i in range(0, len(method['reset'])):
self.gbx_flash_write_address_byte(method['reset'][i][0], method['reset'][i][1])
if self.MODE == "DMG":
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
flash_id = ""
for i in range(0, len(flash_id_lines)):
flash_id += flash_id_lines[i][0] + " "
@@ -777,8 +817,7 @@ class GbxDevice:
flash_id += "{:02X} ".format(flash_id_lines[i][1][j])
flash_id += "\n"
if flash_id != "":
flash_id = rom_string + flash_id
flash_id = rom_string + flash_id
return (flash_id, cfi_info, cfi)
@@ -794,6 +833,9 @@ class GbxDevice:
if not self.IsConnected(): raise Exception("Couldnt access the the device.")
data = {}
self.POS = 0
if self.MODE == "DMG":
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
header = self.ReadROM(0, 0x180)
if self.MODE == "DMG":
@@ -813,7 +855,7 @@ class GbxDevice:
self.INFO["flash_type"] = 0
self.INFO["last_action"] = 0
self.set_mode(self.DEVICE_CMD['SET_PINS_AS_INPUTS'])
self.set_mode(self.DEVICE_CMD["SET_PINS_AS_INPUTS"])
return data
def BackupROM(self, fncSetProgress=None, path="ROM.gb", mbc=0x00, rom_banks=512, agb_rom_size=0, start_addr=0, fast_read_mode=False):
@@ -1067,13 +1109,13 @@ class GbxDevice:
endAddr = startAddr + min(save_size, bank_size)
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'])
self.set_number(eeprom_size, self.DEVICE_CMD["GBA_SET_EEPROM_SIZE"])
elif (save_type == 5) and bank > 0: # 1M SRAM
self.gbx_flash_write_address_byte(0x1000000, bank)
elif (save_type == 6 or save_type == 7) and bank > 0: # FLASH
self.set_number(bank, self.DEVICE_CMD['GBA_FLASH_SET_BANK'])
self.set_number(bank, self.DEVICE_CMD["GBA_FLASH_SET_BANK"])
self.set_number(startAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(startAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
buffer_len = transfer_size
sector = 0
@@ -1082,8 +1124,8 @@ class GbxDevice:
if self.MODE == "DMG":
self.EnableRAM(mbc=mbc, enable=False)
elif self.MODE == "AGB":
if bank > 0: self.set_number(0, self.DEVICE_CMD['GBA_FLASH_SET_BANK'])
self.set_mode(self.DEVICE_CMD['READ_ROM_RAM'])
if bank > 0: self.set_number(0, self.DEVICE_CMD["GBA_FLASH_SET_BANK"])
self.set_mode(self.DEVICE_CMD["READ_ROM_RAM"])
self.ReadInfo()
cancel_args = {"action":"ABORT", "abortable":False}
cancel_args.update(self.CANCEL_ARGS)
@@ -1111,20 +1153,21 @@ class GbxDevice:
elif mode == 3: # Restore
data = data_import[pos:pos+buffer_len]
if self.MODE == "DMG":
self.gbx_flash_write_data_bytes("WRITE_RAM", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["WRITE_RAM"], data)
elif self.MODE == "AGB":
if save_type == 6 or save_type == 7: # FLASH
if maker_id == "ATMEL":
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_ATMEL", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_ATMEL"], data)
else:
if (currAddr % 4096 == 0):
self.set_number(sector, self.DEVICE_CMD['GBA_FLASH_4K_SECTOR_ERASE'])
self.set_number(sector, self.DEVICE_CMD["GBA_FLASH_4K_SECTOR_ERASE"])
self.wait_for_ack()
sector += 1
lives = 50
while True: # wait for 0xFF (= erase done)
self.set_number(currAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
self.set_mode(self.DEVICE_CMD[read_command])
buffer = self.read(buffer_len, last=True)
if buffer[0] == 0xFF: break
@@ -1133,9 +1176,9 @@ class GbxDevice:
if lives == 0:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Writing the flash save data failed. Please make sure you selected the correct save type.", "abortable":False})
return False
self.set_number(currAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_BYTE"], data)
else: # EEPROM / SRAM
self.gbx_flash_write_data_bytes(write_command, data)
@@ -1156,7 +1199,7 @@ class GbxDevice:
if (save_type == 5) 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
self.set_number(0, self.DEVICE_CMD['GBA_FLASH_SET_BANK'])
self.set_number(0, self.DEVICE_CMD["GBA_FLASH_SET_BANK"])
if mode == 2: file.close()
@@ -1195,7 +1238,7 @@ class GbxDevice:
self._FlashROM(buffer=data_import, cart_type=cart_type, voltage=args["override_voltage"], start_addr=args["start_addr"], signal=signal, prefer_sector_erase=args["prefer_sector_erase"], reverse_sectors=args["reverse_sectors"], fast_read_mode=args["fast_read_mode"], verify_flash=args["verify_flash"])
# Reset pins to avoid save data loss
self.set_mode(self.DEVICE_CMD['SET_PINS_AS_INPUTS'])
self.set_mode(self.DEVICE_CMD["SET_PINS_AS_INPUTS"])
#######################################################################################################################################
@@ -1204,6 +1247,7 @@ class GbxDevice:
if self.INFO == None: self.ReadInfo()
self.INFO["last_action"] = 4
bank_size = 0x4000
mbc = 0
time_start = time.time()
data_import = copy.copy(buffer)
@@ -1239,32 +1283,36 @@ class GbxDevice:
# Set Voltage
if voltage == 3.3:
self.set_mode(self.DEVICE_CMD['VOLTAGE_3_3V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_3_3V"])
elif voltage == 5:
self.set_mode(self.DEVICE_CMD['VOLTAGE_5V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
elif flashcart_meta["voltage"] == 3.3:
self.set_mode(self.DEVICE_CMD['VOLTAGE_3_3V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_3_3V"])
elif flashcart_meta["voltage"] == 5:
self.set_mode(self.DEVICE_CMD['VOLTAGE_5V'])
self.set_mode(self.DEVICE_CMD["VOLTAGE_5V"])
if "mbc" in flashcart_meta:
mbc = flashcart_meta['mbc']
dprint("Set MBC to {:d}".format(mbc))
if self.MODE == "DMG":
self.set_mode(self.DEVICE_CMD['GB_CART_MODE'])
self.set_mode(self.DEVICE_CMD["GB_CART_MODE"])
if "flash_commands_on_bank_1" in flashcart_meta and flashcart_meta["flash_commands_on_bank_1"]:
self.set_mode(self.DEVICE_CMD['GB_FLASH_BANK_1_COMMAND_WRITES'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_BANK_1_COMMAND_WRITES"])
dprint("Setting GB_FLASH_BANK_1_COMMAND_WRITES...")
self.set_mode(self.DEVICE_CMD['GB_FLASH_WE_PIN'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_WE_PIN"])
if flashcart_meta["write_pin"] == "WR":
self.set_mode(self.DEVICE_CMD['WE_AS_WR_PIN'])
self.set_mode(self.DEVICE_CMD["WE_AS_WR_PIN"])
dprint("Setting WE_AS_WR_PIN...")
elif flashcart_meta["write_pin"] in ("AUDIO", "VIN"):
self.set_mode(self.DEVICE_CMD['WE_AS_AUDIO_PIN'])
self.set_mode(self.DEVICE_CMD["WE_AS_AUDIO_PIN"])
dprint("Setting WE_AS_AUDIO_PIN...")
if "single_write" in flashcart_meta["commands"] and len(flashcart_meta["commands"]["single_write"]) == 4:
# Submit flash program commands to firmware
dprint("Setting GB_FLASH_PROGRAM_METHOD...")
self.set_mode(self.DEVICE_CMD['GB_FLASH_PROGRAM_METHOD'])
self.set_mode(self.DEVICE_CMD["GB_FLASH_PROGRAM_METHOD"])
for i in range(0, 3):
dprint("single_write_command(",i,"):", hex(flashcart_meta["commands"]["single_write"][i][0]), "=", hex(flashcart_meta["commands"]["single_write"][i][1]))
self.write(bytearray(format(flashcart_meta["commands"]["single_write"][i][0], "x"), "ascii") + b'\x00', True)
@@ -1331,7 +1379,7 @@ class GbxDevice:
dprint("Chip erase took {:d} seconds".format(math.ceil(time.time() - time_start)))
self.set_number(0, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(0, self.DEVICE_CMD["SET_START_ADDRESS"])
# Write Flash
pos = 0
@@ -1342,7 +1390,7 @@ class GbxDevice:
if "start_addr" in flashcart_meta: currAddr = flashcart_meta["start_addr"]
endAddr = 0x7FFF
bank_count = math.ceil(len(data_import) / bank_size)
if start_addr == 0: self.SetBankROM(0)
if start_addr == 0: self.SetBankROM(0, mbc=mbc, bank_count=bank_count)
elif self.MODE == "AGB":
currAddr = 0
endAddr = len(data_import)
@@ -1356,7 +1404,7 @@ class GbxDevice:
# Fast Forward
if start_addr > 0:
first_bank = math.floor(start_addr / bank_size)
self.SetBankROM(first_bank)
self.SetBankROM(first_bank, mbc=mbc, bank_count=bank_count)
offset = start_addr % bank_size
currAddr = bank_size + offset
if "sector_erase" in flashcart_meta["commands"]:
@@ -1391,7 +1439,7 @@ class GbxDevice:
dprint("BANK {:d}".format(bank))
if self.MODE == "DMG":
if bank > first_bank: currAddr = bank_size
self.set_number(currAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
while (currAddr < endAddr):
if pos >= len(data_import): break
@@ -1413,7 +1461,7 @@ class GbxDevice:
if "reset" in flashcart_meta["commands"]:
for i in range(0, len(flashcart_meta["commands"]["reset"])):
self.gbx_flash_write_address_byte(flashcart_meta["commands"]["reset"][i][0], flashcart_meta["commands"]["reset"][i][1])
self.SetBankROM(bank)
self.SetBankROM(bank, mbc=mbc, bank_count=bank_count)
time.sleep(0.05)
# Sector Erase (if supported)
@@ -1480,9 +1528,9 @@ class GbxDevice:
self.gbx_flash_write_address_byte(flashcart_meta["commands"]["reset"][i][0], flashcart_meta["commands"]["reset"][i][1])
if self.MODE == "DMG":
self.set_number(currAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
elif self.MODE == "AGB":
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
if sector_count is not None:
sector_count -= 1
@@ -1499,9 +1547,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GB_FLASH_WRITE_INTEL_BUFFERED_32BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GB_FLASH_WRITE_INTEL_BUFFERED_32BYTE"], data)
ack = self.wait_for_ack()
currAddr += 32
pos += 32
@@ -1518,9 +1566,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_INTEL_INTERLEAVED_256BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_INTEL_INTERLEAVED_256BYTE"], data)
ack = self.wait_for_ack()
currAddr += 256
pos += 256
@@ -1539,7 +1587,7 @@ class GbxDevice:
data = data_import[pos:pos+2]
if int(currAddr % 0x8000) == 0:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
# R24+
elif (int(self.FW[0]) >= 24) and "single_write_7FC0_to_7FFF" not in flashcart_meta:
@@ -1548,9 +1596,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_INTEL_256BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_INTEL_256BYTE"], data)
ack = self.wait_for_ack()
currAddr += 256
@@ -1562,9 +1610,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_INTEL_64BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_INTEL_64BYTE"], data)
ack = self.wait_for_ack()
currAddr += 64
@@ -1577,9 +1625,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_BUFFERED_256BYTE_SWAPPED_D0D1", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_BUFFERED_256BYTE_SWAPPED_D0D1"], data)
ack = self.wait_for_ack()
currAddr += 256
pos += 256
@@ -1598,7 +1646,7 @@ class GbxDevice:
data = data_import[pos:pos+2]
if currAddr == 256:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
else:
data = data_import[pos:pos+256]
@@ -1606,9 +1654,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_BUFFERED_256BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_BUFFERED_256BYTE"], data)
ack = self.wait_for_ack()
currAddr += 256
pos += 256
@@ -1629,14 +1677,14 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
if "pulse_reset_after_write" in flashcart_meta and flashcart_meta["pulse_reset_after_write"]:
self.gbx_flash_write_data_bytes("GB_FLASH_WRITE_64BYTE_PULSE_RESET", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GB_FLASH_WRITE_64BYTE_PULSE_RESET"], data)
elif len(data) == 64:
self.gbx_flash_write_data_bytes("GB_FLASH_WRITE_64BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GB_FLASH_WRITE_64BYTE"], data)
elif len(data) == 256:
self.gbx_flash_write_data_bytes("GB_FLASH_WRITE_UNBUFFERED_256BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GB_FLASH_WRITE_UNBUFFERED_256BYTE"], data)
ack = self.wait_for_ack()
currAddr += len(data)
@@ -1650,9 +1698,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_256BYTE_SWAPPED_D0D1", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_256BYTE_SWAPPED_D0D1"], data)
ack = self.wait_for_ack()
currAddr += 256
pos += 256
@@ -1664,9 +1712,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_INTEL_64BYTE_WORD", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_INTEL_64BYTE_WORD"], data)
ack = self.wait_for_ack()
currAddr += 64
pos += 64
@@ -1678,9 +1726,9 @@ class GbxDevice:
skipping = True
else:
if skipping:
self.set_number(currAddr / 2, self.DEVICE_CMD['SET_START_ADDRESS'])
self.set_number(currAddr / 2, self.DEVICE_CMD["SET_START_ADDRESS"])
skipping = False
self.gbx_flash_write_data_bytes("GBA_FLASH_WRITE_SHARP_64BYTE", data)
self.gbx_flash_write_data_bytes(self.DEVICE_CMD["GBA_FLASH_WRITE_SHARP_64BYTE"], data)
ack = self.wait_for_ack()
currAddr += 64
pos += 64
@@ -1697,7 +1745,7 @@ class GbxDevice:
data = data_import[pos:pos+2]
if ack == False:
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldnt write {:d} bytes to flash at position 0x{:X}. Please make sure that the cartridge contacts are clean, and that the selected cartridge type and settings are correct.".format(len(data), pos), "abortable":False})
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldnt write {:d} bytes to flash at position 0x{:X}. Please make sure that the cartridge contacts are clean, and that the selected cartridge type and settings are correct.".format(len(data), pos-len(data)), "abortable":False})
return False
else:
self.SetProgress({"action":"WRITE", "bytes_added":len(data), "skipping":skipping})
@@ -1717,7 +1765,6 @@ class GbxDevice:
rom_size = len(data_import)
buffer_len = 0x1000
if self.MODE == "DMG":
mbc = 5
if fast_read_mode:
buffer_len = 0x4000
self.FAST_READ = True
@@ -1748,7 +1795,7 @@ class GbxDevice:
if bank > 0:
startAddr = bank_size
endAddr = startAddr + bank_size
self.SetBankROM(bank, mbc)
self.SetBankROM(bank, mbc=mbc, bank_count=bank_count)
for currAddr in range(startAddr, endAddr, buffer_len):
if self.CANCEL:
@@ -1786,4 +1833,7 @@ class GbxDevice:
verified = True
self.POS = 0
if self.MODE == "DMG":
self.SetBankROM(0, mbc=mbc, bank_count=bank_count)
self.SetProgress({"action":"FINISHED", "verified":verified})

Binary file not shown.

View File

@@ -29,6 +29,7 @@ by Lesserkuma
- BUNG Doctor GB Card 64M
- DIY cart with AM29F016/AM29F016B
- DIY cart with AT49F040
- GB Smart 32M
- insideGadgets 32 KB
- insideGadgets 512 KB
@@ -149,13 +150,14 @@ The author would like to thank the following very kind people for their help and
- djedditt (testing)
- easthighNerd (feature suggestions)
- Frost Clock (flash chip info)
- howie0210 (flash chip info)
- Icesythe7 (feature suggestions)
- JFox (help with properly packaging the app for pip)
- julgr (macOS help, testing)
- litlemoran (flash chip info)
- LovelyA72 (flash chip info)
- LucentW (flash chip info, testing)
- marv17 (flash chip info, testing, feature suggestions)
- marv17 (flash chip info, testing, bug reports, feature suggestions)
- Paradoxical (flash chip info)
- RevZ (Linux help, testing, bug reports, flash chip info)
- Super Maker (flash chip info, testing)
@@ -264,3 +266,8 @@ The author would like to thank the following very kind people for their help and
- Added a warning for very old reproduction cartridges with DRM that show “YJencrypted” as their game title *(thanks Super Maker)*
- Added a firmware check when writing to cartridges with flash chips manufactured by Sharp (unsupported by GBxCart RW firmware R25)
- Added optional verification of written data after ROM flashing *(thanks marv17 for the suggestion)*
### v1.2 (released 2021-01-16)
- Fixed a bug introduced in v1.1 that broke MBC3 handling *(thanks marv17 for reporting)*
- Will now default back to 5V for Game Boy cartridges after unsuccessful flash chip auto-detection
- Added support for DIY carts with the AT49F040 flash chip *(thanks howie0210)*

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="1.1",
version="1.2",
author="Lesserkuma",
description="A GUI application that can read and write Game Boy and Game Boy Advance cartridge data. Currently supports the GBxCart RW hardware device by insideGadgets.",
url="https://github.com/lesserkuma/FlashGBX",