mirror of
https://github.com/lesserkuma/FlashGBX.git
synced 2026-04-26 10:30:17 -05:00
3.8
This commit is contained in:
parent
4a91df2f71
commit
50b4601d72
14
CHANGES.md
14
CHANGES.md
|
|
@ -1,4 +1,18 @@
|
||||||
# Release notes
|
# Release notes
|
||||||
|
### v3.8 (released 2022-04-21)
|
||||||
|
- Added support for AGB-E05-01 with MSP55LV100G *(thanks EmperorOfTigers)*
|
||||||
|
- Added support for DV15 with MSP55LV100G *(thanks zvxr)*
|
||||||
|
- Added support for SD007_T40_64BALL_TSOP28 with 29LV016T *(thanks Wkr)*
|
||||||
|
- Confirmed support for AGB-E05-03H with M29W128GH *(thanks Wkr)*
|
||||||
|
- The integrated firmware updater can now also update the firmware of insideGadgets GBxCart RW v1.1, v1.2, XMAS v1.0 and Mini v1.0 device revisions
|
||||||
|
- Added experimental support for writing ISX files to Game Boy flash cartridges
|
||||||
|
- Bundles GBxCart RW v1.4 firmware version R34+L6 (changes the way Game Boy cartridges are read and enables support for some more flash cartridges)
|
||||||
|
- Added support for the BLAZE Xploder GB unlicensed cheat cartridge (requires firmware version L6+)
|
||||||
|
- Added support for the unlicensed Sachen mapper (requires firmware version L6+)
|
||||||
|
- Added support for insideGadgets 128 KB flash cartridges *(thanks AlexiG)*
|
||||||
|
- Added support for insideGadgets 256 KB flash cartridges *(thanks AlexiG)*
|
||||||
|
- Minor bug fixes and improvements
|
||||||
|
|
||||||
### v3.7 (released 2022-03-31)
|
### v3.7 (released 2022-03-31)
|
||||||
- Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums (improves support for Classic NES Series, NES Classics and Famicom Mini cartridges)
|
- Updated the Game Boy Advance lookup database for save types, ROM sizes and checksums (improves support for Classic NES Series, NES Classics and Famicom Mini cartridges)
|
||||||
- When writing new ROMs to Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN), hidden sector data will now be auto-generated if it’s not provided by the user in form of a .map file
|
- When writing new ROMs to Nintendo Power GB Memory Cartridges (DMG-MMSA-JPN), hidden sector data will now be auto-generated if it’s not provided by the user in form of a .map file
|
||||||
|
|
|
||||||
|
|
@ -127,11 +127,10 @@ 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-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", choices=["auto", "1", "2", "3", "5", "6", "7"], type=str.lower, default="auto", help="set memory bank controller type of Game Boy cartridge")
|
ap_cli2.add_argument("--dmg-mbc", choices=["auto", "1", "2", "3", "5", "6", "7"], type=str.lower, default="auto", help="set memory bank controller type of Game Boy cartridge")
|
||||||
ap_cli2.add_argument("--dmg-savesize", choices=["auto", "4k", "16k", "64k", "256k", "512k", "1m", "eeprom2k", "eeprom4k", "tama5", "4m"], type=str.lower, default="auto", help="set size of Game Boy cartridge save data")
|
ap_cli2.add_argument("--dmg-savesize", choices=["auto", "4k", "16k", "64k", "256k", "512k", "1m", "eeprom2k", "eeprom4k", "tama5", "4m"], type=str.lower, default="auto", help="set size of Game Boy cartridge save data")
|
||||||
ap_cli2.add_argument("--agb-romsize", choices=["auto", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb"], type=str.lower, default="auto", help="set size of Game Boy Advance cartridge ROM data")
|
ap_cli2.add_argument("--agb-romsize", choices=["auto", "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-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("--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("--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")
|
ap_cli2.add_argument("--ignore-bad-header", action="store_true", help="don’t stop if invalid data found in cartridge header data")
|
||||||
#ap_cli2.add_argument("--fast-read-mode", action="store_true", help="enable experimental fast read mode for GBxCart RW v1.3")
|
|
||||||
ap_cli2.add_argument("--flashcart-type", type=str, default="autodetect", help="name of flash cart; see txt files in config directory")
|
ap_cli2.add_argument("--flashcart-type", type=str, default="autodetect", help="name of flash cart; see txt files in config directory")
|
||||||
ap_cli2.add_argument("--prefer-chip-erase", action="store_true", help="prefer full chip erase over sector erase when both available")
|
ap_cli2.add_argument("--prefer-chip-erase", action="store_true", help="prefer full chip erase over sector erase when both available")
|
||||||
ap_cli2.add_argument("--reversed-sectors", action="store_true", help="use reversed flash sectors if possible")
|
ap_cli2.add_argument("--reversed-sectors", action="store_true", help="use reversed flash sectors if possible")
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ class FlashGBX_CLI():
|
||||||
print("\n{:s}Couldn’t read cartridge header. Please try again.{:s}\n".format(ANSI.RED, ANSI.RESET))
|
print("\n{:s}Couldn’t read cartridge header. Please try again.{:s}\n".format(ANSI.RED, ANSI.RESET))
|
||||||
self.DisconnectDevice()
|
self.DisconnectDevice()
|
||||||
return
|
return
|
||||||
elif bad_read and not args.ignore_bad_header:
|
elif bad_read and not args.ignore_bad_header and ("mapper_raw" in header and header["mapper_raw"] != 0x203):
|
||||||
print("\n{:s}Invalid data was detected which usually means that the cartridge couldn’t be read correctly. Please make sure you selected the correct mode and that the cartridge contacts are clean. This check can be disabled with the command line switch “--ignore-bad-header”.{:s}\n".format(ANSI.RED, ANSI.RESET))
|
print("\n{:s}Invalid data was detected which usually means that the cartridge couldn’t be read correctly. Please make sure you selected the correct mode and that the cartridge contacts are clean. This check can be disabled with the command line switch “--ignore-bad-header”.{:s}\n".format(ANSI.RED, ANSI.RESET))
|
||||||
print("Cartridge Information:")
|
print("Cartridge Information:")
|
||||||
print(s_header)
|
print(s_header)
|
||||||
|
|
@ -227,6 +227,8 @@ class FlashGBX_CLI():
|
||||||
print("\033[KPlease wait while the flash chip is being unlocked... (Elapsed time: {:s})".format(Util.formatProgressTime(elapsed)), end="\r")
|
print("\033[KPlease wait while the flash chip is being unlocked... (Elapsed time: {:s})".format(Util.formatProgressTime(elapsed)), end="\r")
|
||||||
elif args["action"] == "SECTOR_ERASE":
|
elif args["action"] == "SECTOR_ERASE":
|
||||||
print("\033[KErasing flash sector at address 0x{:X}...".format(args["sector_pos"]), end="\r")
|
print("\033[KErasing flash sector at address 0x{:X}...".format(args["sector_pos"]), end="\r")
|
||||||
|
elif args["action"] == "UPDATE_RTC":
|
||||||
|
print("\nUpdating Real Time Clock...")
|
||||||
elif args["action"] == "ABORTING":
|
elif args["action"] == "ABORTING":
|
||||||
print("\nStopping...")
|
print("\nStopping...")
|
||||||
elif args["action"] == "FINISHED":
|
elif args["action"] == "FINISHED":
|
||||||
|
|
@ -244,7 +246,7 @@ class FlashGBX_CLI():
|
||||||
return
|
return
|
||||||
elif args["action"] == "PROGRESS":
|
elif args["action"] == "PROGRESS":
|
||||||
# pv style progress status
|
# pv style progress status
|
||||||
prog_str = "{:s}/{:s} {:s} [{:s}KB/s] [{:s}] {:s}% ETA {:s} ".format(Util.formatFileSize(size=pos).replace(" ", "").replace("Bytes", "B").replace("Byte", "B").rjust(8), Util.formatFileSize(size=size).replace(" ", "").replace("Bytes", "B"), Util.formatProgressTimeShort(elapsed), "{:.2f}".format(speed).rjust(6), "%PROG_BAR%", "{:d}".format(int(pos/size*100)).rjust(3), Util.formatProgressTimeShort(left))
|
prog_str = "{:s}/{:s} {:s} [{:s}KB/s] [{:s}] {:s}% ETA {:s} ".format(Util.formatFileSize(size=pos).replace(" ", "").replace("Bytes", "B").replace("Byte", "B").rjust(8), Util.formatFileSize(size=size).replace(" ", "").replace("Bytes", "B"), Util.formatProgressTimeShort(elapsed), "{:.2f}".format(speed).rjust(6), "%PROG_BAR%", "{:d}".format(int(pos/size*100)).rjust(3), Util.formatProgressTimeShort(left))
|
||||||
prog_width = shutil.get_terminal_size((80, 20))[0] - (len(prog_str) - 10)
|
prog_width = shutil.get_terminal_size((80, 20))[0] - (len(prog_str) - 10)
|
||||||
progress = min(1, max(0, pos/size))
|
progress = min(1, max(0, pos/size))
|
||||||
whole_width = math.floor(progress * prog_width)
|
whole_width = math.floor(progress * prog_width)
|
||||||
|
|
@ -272,23 +274,36 @@ class FlashGBX_CLI():
|
||||||
elif self.CONN.INFO["last_action"] == 1: # Backup ROM
|
elif self.CONN.INFO["last_action"] == 1: # Backup ROM
|
||||||
self.CONN.INFO["last_action"] = 0
|
self.CONN.INFO["last_action"] = 0
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
print("CRC32: {:04X}".format(self.CONN.INFO["file_crc32"]))
|
print("CRC32: {:08x}".format(self.CONN.INFO["file_crc32"]))
|
||||||
print("SHA-1: {:s}\n".format(self.CONN.INFO["file_sha1"]))
|
print("SHA-1: {:s}\n".format(self.CONN.INFO["file_sha1"]))
|
||||||
if self.CONN.INFO["rom_checksum"] == self.CONN.INFO["rom_checksum_calc"]:
|
if self.CONN.INFO["rom_checksum"] == self.CONN.INFO["rom_checksum_calc"]:
|
||||||
print("{:s}The ROM backup is complete and the checksum was verified successfully!{:s}".format(ANSI.GREEN, ANSI.RESET))
|
print("{:s}The ROM backup is complete and the checksum was verified successfully!{:s}".format(ANSI.GREEN, ANSI.RESET))
|
||||||
elif ("DMG-MMSA-JPN" in self.ARGS["argparsed"].flashcart_type) or ("features_raw" in self.CONN.INFO and self.CONN.INFO["features_raw"] in (0x105, 0x202)):
|
elif ("DMG-MMSA-JPN" in self.ARGS["argparsed"].flashcart_type) or ("mapper_raw" in self.CONN.INFO and self.CONN.INFO["mapper_raw"] in (0x105, 0x202)):
|
||||||
print("The ROM backup is complete!")
|
print("The ROM backup is complete!")
|
||||||
else:
|
else:
|
||||||
print("{:s}The ROM was dumped, but the checksum is not correct. This may indicate a bad dump, however this can be normal for some reproduction prototypes, unlicensed games, patched games and intentional overdumps.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
msg = "The ROM was dumped, but the checksum is not correct."
|
||||||
|
if self.CONN.INFO["loop_detected"] is not False:
|
||||||
|
msg += "\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
|
||||||
|
else:
|
||||||
|
msg += "\nThis may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
|
||||||
|
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
|
||||||
elif self.CONN.GetMode() == "AGB":
|
elif self.CONN.GetMode() == "AGB":
|
||||||
print("CRC32: {:04X}".format(self.CONN.INFO["file_crc32"]))
|
print("CRC32: {:08x}".format(self.CONN.INFO["file_crc32"]))
|
||||||
print("SHA-1: {:s}\n".format(self.CONN.INFO["file_sha1"]))
|
print("SHA-1: {:s}\n".format(self.CONN.INFO["file_sha1"]))
|
||||||
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
|
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
|
||||||
print("{:s}The ROM backup is complete and the checksum was verified successfully!{:s}".format(ANSI.GREEN, ANSI.RESET))
|
print("{:s}The ROM backup is complete and the checksum was verified successfully!{:s}".format(ANSI.GREEN, ANSI.RESET))
|
||||||
elif Util.AGB_Global_CRC32 == 0:
|
elif Util.AGB_Global_CRC32 == 0:
|
||||||
print("The ROM backup is complete! As there is no known checksum for this ROM in the database, verification was skipped.")
|
msg = "The ROM backup is complete! As there is no known checksum for this ROM in the database, verification was skipped."
|
||||||
|
if self.CONN.INFO["loop_detected"] is not False:
|
||||||
|
msg += "\nNOTE: A data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
|
||||||
|
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
|
||||||
else:
|
else:
|
||||||
print("{:s}The ROM backup is complete, but the checksum doesn’t match the known database entry. This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
msg = "The ROM backup is complete, but the checksum doesn’t match the known database entry."
|
||||||
|
if self.CONN.INFO["loop_detected"] is not False:
|
||||||
|
msg += "\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
|
||||||
|
else:
|
||||||
|
msg += "\nThis may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
|
||||||
|
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
|
||||||
|
|
||||||
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
|
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
|
||||||
self.CONN.INFO["last_action"] = 0
|
self.CONN.INFO["last_action"] = 0
|
||||||
|
|
@ -376,6 +391,10 @@ class FlashGBX_CLI():
|
||||||
self.CONN = None
|
self.CONN = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if dev.FW_UPDATE_REQ:
|
||||||
|
print("{:s}A firmware update for your {:s} device is required to fully use this software.\nPlease visit the official website at {:s} for updates.\n{:s}Current firmware version: {:s}{:s}".format(ANSI.RED, dev.GetFullName(), dev.GetOfficialWebsite(), ANSI.YELLOW, dev.GetFirmwareVersion(), ANSI.RESET))
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
self.CONN = dev
|
self.CONN = dev
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -409,7 +428,7 @@ class FlashGBX_CLI():
|
||||||
if data['has_rtc']:
|
if data['has_rtc']:
|
||||||
if 'rtc_buffer' in data:
|
if 'rtc_buffer' in data:
|
||||||
try:
|
try:
|
||||||
if data['features_raw'] == 0x10: # MBC3
|
if data['mapper_raw'] == 0x10: # MBC3
|
||||||
rtc_s = data["rtc_buffer"][0x00]
|
rtc_s = data["rtc_buffer"][0x00]
|
||||||
rtc_m = data["rtc_buffer"][0x04]
|
rtc_m = data["rtc_buffer"][0x04]
|
||||||
rtc_h = data["rtc_buffer"][0x08]
|
rtc_h = data["rtc_buffer"][0x08]
|
||||||
|
|
@ -420,7 +439,7 @@ class FlashGBX_CLI():
|
||||||
s += "Invalid state"
|
s += "Invalid state"
|
||||||
else:
|
else:
|
||||||
s += "{:d} days, {:02d}:{:02d}:{:02d}".format(rtc_d, rtc_h, rtc_m, rtc_s)
|
s += "{:d} days, {:02d}:{:02d}:{:02d}".format(rtc_d, rtc_h, rtc_m, rtc_s)
|
||||||
elif data['features_raw'] == 0xFE: # HuC-3
|
elif data['mapper_raw'] == 0xFE: # HuC-3
|
||||||
rtc_buffer = struct.unpack("<I", data["rtc_buffer"][0:4])[0]
|
rtc_buffer = struct.unpack("<I", data["rtc_buffer"][0:4])[0]
|
||||||
rtc_h = math.floor((rtc_buffer & 0xFFF) / 60)
|
rtc_h = math.floor((rtc_buffer & 0xFFF) / 60)
|
||||||
rtc_m = (rtc_buffer & 0xFFF) % 60
|
rtc_m = (rtc_buffer & 0xFFF) % 60
|
||||||
|
|
@ -435,7 +454,7 @@ class FlashGBX_CLI():
|
||||||
if 'no_rtc_reason' in data:
|
if 'no_rtc_reason' in data:
|
||||||
if data["no_rtc_reason"] == -1:
|
if data["no_rtc_reason"] == -1:
|
||||||
temp = "Unknown"
|
temp = "Unknown"
|
||||||
if data['features_raw'] == 0xFD: # TAMA5
|
if data['mapper_raw'] == 0xFD: # TAMA5
|
||||||
temp = "OK"
|
temp = "OK"
|
||||||
s += temp
|
s += temp
|
||||||
s += "\n"
|
s += "\n"
|
||||||
|
|
@ -459,15 +478,15 @@ class FlashGBX_CLI():
|
||||||
bad_read = True
|
bad_read = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if data['features_raw'] == 0x06: # MBC2
|
if data['mapper_raw'] == 0x06: # MBC2
|
||||||
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[1])
|
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[1])
|
||||||
elif data['features_raw'] == 0x22 and data["game_title"] in ("KORO2 KIRBYKKKJ", "KIRBY TNT__KTNE"): # MBC7 Kirby
|
elif data['mapper_raw'] == 0x22 and data["game_title"] in ("KORO2 KIRBYKKKJ", "KIRBY TNT__KTNE"): # MBC7 Kirby
|
||||||
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x101)])
|
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x101)])
|
||||||
elif data['features_raw'] == 0x22 and data["game_title"] in ("CMASTER____KCEJ"): # MBC7 Command Master
|
elif data['mapper_raw'] == 0x22 and data["game_title"] in ("CMASTER____KCEJ"): # MBC7 Command Master
|
||||||
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x102)])
|
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x102)])
|
||||||
elif data['features_raw'] == 0xFD: # TAMA5
|
elif data['mapper_raw'] == 0xFD: # TAMA5
|
||||||
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x103)])
|
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x103)])
|
||||||
elif data['features_raw'] == 0x20: # MBC6
|
elif data['mapper_raw'] == 0x20: # MBC6
|
||||||
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x104)])
|
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(0x104)])
|
||||||
else:
|
else:
|
||||||
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(data['ram_size_raw'])])
|
s += "Save Type: {:s}\n".format(Util.DMG_Header_RAM_Sizes[Util.DMG_Header_RAM_Sizes_Map.index(data['ram_size_raw'])])
|
||||||
|
|
@ -475,12 +494,12 @@ class FlashGBX_CLI():
|
||||||
s += "Save Type: Not detected\n"
|
s += "Save Type: Not detected\n"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s += "Mapper Type: {:s}\n".format(Util.DMG_Header_Mapper[data['features_raw']])
|
s += "Mapper Type: {:s}\n".format(Util.DMG_Header_Mapper[data['mapper_raw']])
|
||||||
except:
|
except:
|
||||||
s += "Mapper Type: {:s}Not detected{:s}\n".format(ANSI.RED, ANSI.RESET)
|
s += "Mapper Type: {:s}Not detected{:s}\n".format(ANSI.RED, ANSI.RESET)
|
||||||
bad_read = True
|
bad_read = True
|
||||||
|
|
||||||
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["features_raw"]):
|
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["mapper_raw"]):
|
||||||
print("{:s}\nWARNING: This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates.{:s}".format(ANSI.YELLOW, APPNAME, self.CONN.GetFullName(), ANSI.RESET))
|
print("{:s}\nWARNING: This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates.{:s}".format(ANSI.YELLOW, APPNAME, self.CONN.GetFullName(), ANSI.RESET))
|
||||||
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU ") and self.ARGS["argparsed"].flashcart_type == "autodetect":
|
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU ") and self.ARGS["argparsed"].flashcart_type == "autodetect":
|
||||||
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
||||||
|
|
@ -662,16 +681,27 @@ class FlashGBX_CLI():
|
||||||
else:
|
else:
|
||||||
(flash_id, cfi_s, _) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage)
|
(flash_id, cfi_s, _) = self.CONN.CheckFlashChip(limitVoltage=limitVoltage)
|
||||||
if (len(flash_id.split("\n")) > 2) and ((self.CONN.GetMode() == "DMG") or ("dacs_8m" in header and header["dacs_8m"] is not True)):
|
if (len(flash_id.split("\n")) > 2) and ((self.CONN.GetMode() == "DMG") or ("dacs_8m" in header and header["dacs_8m"] is not True)):
|
||||||
msg_cart_type_s = "Cartridge Type: Unknown flash cartridge – Please submit the displayed information along with a picture of the cartridge’s circuit board."
|
msg_cart_type_s = "<b>Cartridge Type:</b> Unknown flash cartridge – Please submit the displayed information along with a picture of the cartridge’s circuit board."
|
||||||
if ("[ 0/90]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (0/90)” a try."
|
if ("[ 0/90]" in flash_id):
|
||||||
elif ("[ AAA/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AAA/AA)” a try."
|
try_this = "Generic Flash Cartridge (0/90)"
|
||||||
elif ("[ AAA/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AAA/A9)” a try."
|
elif ("[ AAA/AA]" in flash_id):
|
||||||
elif ("[WR / AAA/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/AAA/AA)” a try."
|
try_this = "Generic Flash Cartridge (AAA/AA)"
|
||||||
elif ("[WR / AAA/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/AAA/A9)” a try."
|
elif ("[ AAA/A9]" in flash_id):
|
||||||
elif ("[WR / 555/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/555/AA)” a try."
|
try_this = "Generic Flash Cartridge (AAA/A9)"
|
||||||
elif ("[WR / 555/A9]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (WR/555/A9)” a try."
|
elif ("[WR / AAA/AA]" in flash_id):
|
||||||
elif ("[AUDIO/ AAA/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AUDIO/AAA/AA)” a try."
|
try_this = "Generic Flash Cartridge (WR/AAA/AA)"
|
||||||
elif ("[AUDIO/ 555/AA]" in flash_id): msg_cart_type_s += " For ROM writing, you can give the option called “Generic Flash Cartridge (AUDIO/555/AA)” a try."
|
elif ("[WR / AAA/A9]" in flash_id):
|
||||||
|
try_this = "Generic Flash Cartridge (WR/AAA/A9)"
|
||||||
|
elif ("[WR / 555/AA]" in flash_id):
|
||||||
|
try_this = "Generic Flash Cartridge (WR/555/AA)"
|
||||||
|
elif ("[WR / 555/A9]" in flash_id):
|
||||||
|
try_this = "Generic Flash Cartridge (WR/555/A9)"
|
||||||
|
elif ("[AUDIO/ AAA/AA]" in flash_id):
|
||||||
|
try_this = "Generic Flash Cartridge (AUDIO/AAA/AA)"
|
||||||
|
elif ("[AUDIO/ 555/AA]" in flash_id):
|
||||||
|
try_this = "Generic Flash Cartridge (AUDIO/555/AA)"
|
||||||
|
if try_this is not None:
|
||||||
|
msg_cart_type_s += " For ROM writing, you can give the option called “{:s}” a try at your own risk.".format(try_this)
|
||||||
msg_cart_type_s += "\n"
|
msg_cart_type_s += "\n"
|
||||||
else:
|
else:
|
||||||
msg_cart_type_s = "Cartridge Type: Generic ROM Cartridge (not rewritable or not auto-detectable)\n"
|
msg_cart_type_s = "Cartridge Type: Generic ROM Cartridge (not rewritable or not auto-detectable)\n"
|
||||||
|
|
@ -696,11 +726,11 @@ class FlashGBX_CLI():
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
if args.dmg_mbc == "auto":
|
if args.dmg_mbc == "auto":
|
||||||
try:
|
try:
|
||||||
mbc = header["features_raw"]
|
mbc = header["mapper_raw"]
|
||||||
if mbc == 0: mbc = 5
|
if mbc == 0: mbc = 0x19
|
||||||
except:
|
except:
|
||||||
print("{:s}Couldn’t determine MBC type, will try to use MBC5. It can also be manually set with the “--dmg-mbc” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
print("{:s}Couldn’t determine MBC type, will try to use MBC5. It can also be manually set with the “--dmg-mbc” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
||||||
mbc = 5
|
mbc = 0x19
|
||||||
else:
|
else:
|
||||||
mbc = int(args.dmg_mbc)
|
mbc = int(args.dmg_mbc)
|
||||||
if mbc == 2: mbc = 0x06
|
if mbc == 2: mbc = 0x06
|
||||||
|
|
@ -733,7 +763,7 @@ class FlashGBX_CLI():
|
||||||
if args.agb_romsize == "auto":
|
if args.agb_romsize == "auto":
|
||||||
rom_size = header["rom_size"]
|
rom_size = header["rom_size"]
|
||||||
else:
|
else:
|
||||||
sizes = [ "auto", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb" ]
|
sizes = [ "auto", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb" ]
|
||||||
rom_size = Util.AGB_Header_ROM_Sizes_Map[sizes.index(args.agb_romsize) - 1]
|
rom_size = Util.AGB_Header_ROM_Sizes_Map[sizes.index(args.agb_romsize) - 1]
|
||||||
|
|
||||||
path = header["game_title"].strip().encode('ascii', 'ignore').decode('ascii')
|
path = header["game_title"].strip().encode('ascii', 'ignore').decode('ascii')
|
||||||
|
|
@ -806,6 +836,7 @@ class FlashGBX_CLI():
|
||||||
return
|
return
|
||||||
|
|
||||||
cart_type = 0
|
cart_type = 0
|
||||||
|
mbc = 0
|
||||||
for i in range(0, len(carts)):
|
for i in range(0, len(carts)):
|
||||||
if not "names" in carts[i]: continue
|
if not "names" in carts[i]: continue
|
||||||
if carts[i]["type"] != mode: continue
|
if carts[i]["type"] != mode: continue
|
||||||
|
|
@ -840,23 +871,31 @@ class FlashGBX_CLI():
|
||||||
elif os.path.getsize(path) < 0x400:
|
elif os.path.getsize(path) < 0x400:
|
||||||
print("{:s}ROM files smaller than 1 KB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
|
print("{:s}ROM files smaller than 1 KB are not supported.{:s}".format(ANSI.RED, ANSI.RESET))
|
||||||
return
|
return
|
||||||
with open(path, "rb") as file: buffer = bytearray(file.read())
|
#with open(path, "rb") as file: buffer = bytearray(file.read())
|
||||||
|
|
||||||
|
with open(path, "rb") as file:
|
||||||
|
ext = os.path.splitext(path)[1]
|
||||||
|
if ext.lower() == ".isx":
|
||||||
|
buffer = bytearray(file.read())
|
||||||
|
buffer = Util.isx2bin(buffer)
|
||||||
|
else:
|
||||||
|
buffer = bytearray(file.read(0x1000))
|
||||||
|
rom_size = os.stat(path).st_size
|
||||||
|
if "flash_size" in carts[cart_type]:
|
||||||
|
if rom_size > carts[cart_type]['flash_size']:
|
||||||
|
msg = "The selected flash cartridge type seems to support ROMs that are up to {:s} in size, but the file you selected is {:s}.".format(Util.formatFileSize(carts[cart_type]['flash_size']), Util.formatFileSize(os.path.getsize(path), roundUp=True))
|
||||||
|
msg += " You can still give it a try, but it’s possible that it’s too large which may cause the ROM writing to fail."
|
||||||
|
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
|
||||||
|
answer = input("Do you want to continue? [y/N]: ").strip().lower()
|
||||||
|
print("")
|
||||||
|
if answer != "y":
|
||||||
|
print("Canceled.")
|
||||||
|
return
|
||||||
|
|
||||||
except (PermissionError, FileNotFoundError):
|
except (PermissionError, FileNotFoundError):
|
||||||
print("{:s}Couldn’t access file path “{:s}”.{:s}".format(ANSI.RED, args.path, ANSI.RESET))
|
print("{:s}Couldn’t access file path “{:s}”.{:s}".format(ANSI.RED, args.path, ANSI.RESET))
|
||||||
return
|
return
|
||||||
|
|
||||||
rom_size = len(buffer)
|
|
||||||
if "flash_size" in carts[cart_type]:
|
|
||||||
if rom_size > carts[cart_type]['flash_size']:
|
|
||||||
msg = "The selected flash cartridge type seems to support ROMs that are up to {:s} in size, but the file you selected is {:s}.".format(Util.formatFileSize(carts[cart_type]['flash_size']), Util.formatFileSize(os.path.getsize(path)))
|
|
||||||
msg += " It’s possible that it’s too large which may cause the ROM writing to fail."
|
|
||||||
print("{:s}{:s}{:s}".format(ANSI.YELLOW, msg, ANSI.RESET))
|
|
||||||
answer = input("Do you want to continue? [y/N]: ").strip().lower()
|
|
||||||
print("")
|
|
||||||
if answer != "y":
|
|
||||||
print("Canceled.")
|
|
||||||
return
|
|
||||||
|
|
||||||
override_voltage = False
|
override_voltage = False
|
||||||
if args.force_5v is True:
|
if args.force_5v is True:
|
||||||
override_voltage = 5
|
override_voltage = 5
|
||||||
|
|
@ -874,26 +913,24 @@ class FlashGBX_CLI():
|
||||||
if not prefer_chip_erase and 'chip_erase' in carts[cart_type]['commands'] and 'sector_erase' in carts[cart_type]['commands']:
|
if not prefer_chip_erase and 'chip_erase' in carts[cart_type]['commands'] and 'sector_erase' in carts[cart_type]['commands']:
|
||||||
print("This flash cartridge supports both Sector Erase and Full Chip Erase methods. You can use the “--prefer-chip-erase” command line switch if necessary.")
|
print("This flash cartridge supports both Sector Erase and Full Chip Erase methods. You can use the “--prefer-chip-erase” command line switch if necessary.")
|
||||||
|
|
||||||
|
if "mbc" in carts[cart_type]:
|
||||||
|
mbc = carts[cart_type]["mbc"]
|
||||||
|
|
||||||
verify_write = args.no_verify_write is False
|
verify_write = args.no_verify_write is False
|
||||||
|
|
||||||
fix_header = False
|
fix_header = False
|
||||||
try:
|
if self.CONN.GetMode() == "DMG":
|
||||||
if self.CONN.GetMode() == "DMG":
|
hdr = RomFileDMG(buffer).GetHeader()
|
||||||
hdr = RomFileDMG(path).GetHeader()
|
elif self.CONN.GetMode() == "AGB":
|
||||||
elif self.CONN.GetMode() == "AGB":
|
hdr = RomFileAGB(buffer).GetHeader()
|
||||||
hdr = RomFileAGB(path).GetHeader()
|
if not hdr["logo_correct"] and mbc != 0x203:
|
||||||
if not hdr["logo_correct"]:
|
print("{:s}WARNING: The ROM file you selected will not boot on actual hardware due to invalid logo data.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
||||||
print("{:s}WARNING: The ROM file you selected will not boot on actual hardware due to invalid logo data.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
if not hdr["header_checksum_correct"] and mbc != 0x203:
|
||||||
if not hdr["header_checksum_correct"]:
|
print("{:s}WARNING: The ROM file you selected will not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).{:s}".format(ANSI.YELLOW, hdr["header_checksum_calc"], hdr["header_checksum"], ANSI.RESET))
|
||||||
print("{:s}WARNING: The ROM file you selected will not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).{:s}".format(ANSI.YELLOW, hdr["header_checksum_calc"], hdr["header_checksum"], ANSI.RESET))
|
answer = input("Fix the header checksum before continuing? [Y/n]: ").strip().lower()
|
||||||
answer = input("Fix the header checksum before continuing? [Y/n]: ").strip().lower()
|
print("")
|
||||||
print("")
|
if answer != "n":
|
||||||
if answer != "n":
|
fix_header = True
|
||||||
fix_header = True
|
|
||||||
|
|
||||||
except:
|
|
||||||
print("{:s}The selected file could not be read.{:s}".format(ANSI.RED, ANSI.RESET))
|
|
||||||
return
|
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
v = carts[cart_type]["voltage"]
|
v = carts[cart_type]["voltage"]
|
||||||
|
|
@ -901,7 +938,13 @@ class FlashGBX_CLI():
|
||||||
print("The following ROM file will now be written to the flash cartridge at {:s}V:\n{:s}".format(str(v), os.path.abspath(path)))
|
print("The following ROM file will now be written to the flash cartridge at {:s}V:\n{:s}".format(str(v), os.path.abspath(path)))
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
self.CONN.TransferData(args={ 'mode':4, 'path':path, 'cart_type':cart_type, 'override_voltage':override_voltage, 'start_addr':0, 'buffer':buffer, 'prefer_chip_erase':prefer_chip_erase, 'reverse_sectors':reverse_sectors, 'fast_read_mode':True, 'verify_write':verify_write, 'fix_header':fix_header }, signal=self.PROGRESS.SetProgress)
|
if len(buffer) > 0x1000:
|
||||||
|
args = { "mode":4, "path":"", "buffer":buffer, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header }
|
||||||
|
else:
|
||||||
|
args = { "mode":4, "path":path, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header }
|
||||||
|
self.CONN.TransferData(signal=self.PROGRESS.SetProgress, args=args)
|
||||||
|
#self.CONN.TransferData(args={ 'mode':4, 'path':path, 'cart_type':cart_type, 'override_voltage':override_voltage, 'start_addr':0, 'buffer':buffer, 'prefer_chip_erase':prefer_chip_erase, 'reverse_sectors':reverse_sectors, 'fast_read_mode':True, 'verify_write':verify_write, 'fix_header':fix_header }, signal=self.PROGRESS.SetProgress)
|
||||||
|
|
||||||
buffer = None
|
buffer = None
|
||||||
|
|
||||||
def BackupRestoreRAM(self, args, header):
|
def BackupRestoreRAM(self, args, header):
|
||||||
|
|
@ -911,11 +954,11 @@ class FlashGBX_CLI():
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
if args.dmg_mbc == "auto":
|
if args.dmg_mbc == "auto":
|
||||||
try:
|
try:
|
||||||
mbc = header["features_raw"]
|
mbc = header["mapper_raw"]
|
||||||
if mbc == 0: mbc = 5
|
if mbc == 0: mbc = 0x19
|
||||||
except:
|
except:
|
||||||
print("{:s}Couldn’t determine MBC type, will try to use MBC5. It can also be manually set with the “--dmg-mbc” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
print("{:s}Couldn’t determine MBC type, will try to use MBC5. It can also be manually set with the “--dmg-mbc” command line switch.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
||||||
mbc = 5
|
mbc = 0x19
|
||||||
else:
|
else:
|
||||||
mbc = int(args.dmg_mbc)
|
mbc = int(args.dmg_mbc)
|
||||||
if mbc == 2: mbc = 0x06
|
if mbc == 2: mbc = 0x06
|
||||||
|
|
@ -926,15 +969,15 @@ class FlashGBX_CLI():
|
||||||
|
|
||||||
if args.dmg_savesize == "auto":
|
if args.dmg_savesize == "auto":
|
||||||
try:
|
try:
|
||||||
if header['features_raw'] == 0x06: # MBC2
|
if header['mapper_raw'] == 0x06: # MBC2
|
||||||
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[1]
|
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[1]
|
||||||
elif header['features_raw'] == 0x22 and header["game_title"] in ("KORO2 KIRBYKKKJ", "KIRBY TNT_KTNE"): # MBC7 Kirby
|
elif header['mapper_raw'] == 0x22 and header["game_title"] in ("KORO2 KIRBYKKKJ", "KIRBY TNT_KTNE"): # MBC7 Kirby
|
||||||
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x101)]
|
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x101)]
|
||||||
elif header['features_raw'] == 0x22 and header["game_title"] in ("CMASTER_KCEJ"): # MBC7 Command Master
|
elif header['mapper_raw'] == 0x22 and header["game_title"] in ("CMASTER_KCEJ"): # MBC7 Command Master
|
||||||
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x102)]
|
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x102)]
|
||||||
elif header['features_raw'] == 0xFD: # TAMA5
|
elif header['mapper_raw'] == 0xFD: # TAMA5
|
||||||
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x103)]
|
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x103)]
|
||||||
elif header['features_raw'] == 0x20: # TAMA5
|
elif header['mapper_raw'] == 0x20: # TAMA5
|
||||||
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x104)]
|
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(0x104)]
|
||||||
else:
|
else:
|
||||||
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(header['ram_size_raw'])]
|
save_type = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(header['ram_size_raw'])]
|
||||||
|
|
@ -1017,7 +1060,7 @@ class FlashGBX_CLI():
|
||||||
if self.CONN.GetMode() == "AGB":
|
if self.CONN.GetMode() == "AGB":
|
||||||
print("Using Save Type “{:s}”.".format(Util.AGB_Header_Save_Types[save_type]))
|
print("Using Save Type “{:s}”.".format(Util.AGB_Header_Save_Types[save_type]))
|
||||||
elif self.CONN.GetMode() == "DMG":
|
elif self.CONN.GetMode() == "DMG":
|
||||||
if rtc and header["features_raw"] in (0x10, 0xFE): # RTC of MBC3, HuC-3
|
if rtc and header["mapper_raw"] in (0x10, 0xFE): # RTC of MBC3, HuC-3
|
||||||
print("Real Time Clock register values will also be written if applicable/possible.")
|
print("Real Time Clock register values will also be written if applicable/possible.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -1102,9 +1145,9 @@ class FlashGBX_CLI():
|
||||||
found_length = len(test2) - found_offset
|
found_length = len(test2) - found_offset
|
||||||
|
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
print("\nDone! The writable save data size is {:s} out of {:s} checked.".format(Util.formatFileSize(found_length), Util.formatFileSize(save_type)))
|
print("\n{:s}Done! The writable save data size is {:s} out of {:s} checked.{:s}".format(ANSI.GREEN, Util.formatFileSize(found_length), Util.formatFileSize(save_type), ANSI.RESET))
|
||||||
elif self.CONN.GetMode() == "AGB":
|
elif self.CONN.GetMode() == "AGB":
|
||||||
print("\nDone! The writable save data size using save type “{:s}” is {:s}.".format(Util.AGB_Header_Save_Types[save_type], Util.formatFileSize(found_length)))
|
print("\n{:s}Done! The writable save data size using save type “{:s}” is {:s}.{:s}".format(ANSI.GREEN, Util.AGB_Header_Save_Types[save_type], Util.formatFileSize(found_length), ANSI.RESET))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(_, _, cfi) = self.CONN.CheckFlashChip(limitVoltage=False)
|
(_, _, cfi) = self.CONN.CheckFlashChip(limitVoltage=False)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
rowActionsGeneral2.addWidget(self.btnBackupRAM)
|
rowActionsGeneral2.addWidget(self.btnBackupRAM)
|
||||||
|
|
||||||
self.cmbDMGCartridgeTypeResult.currentIndexChanged.connect(self.CartridgeTypeChanged)
|
self.cmbDMGCartridgeTypeResult.currentIndexChanged.connect(self.CartridgeTypeChanged)
|
||||||
|
self.cmbHeaderFeaturesResult.currentIndexChanged.connect(self.DMGMapperTypeChanged)
|
||||||
|
|
||||||
rowActionsGeneral3 = QtWidgets.QHBoxLayout()
|
rowActionsGeneral3 = QtWidgets.QHBoxLayout()
|
||||||
self.btnFlashROM = QtWidgets.QPushButton("&Write ROM")
|
self.btnFlashROM = QtWidgets.QPushButton("&Write ROM")
|
||||||
|
|
@ -665,9 +666,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
if dontShowAgain: self.SETTINGS.setValue("SkipFirmwareUpdate", "enabled")
|
if dontShowAgain: self.SETTINGS.setValue("SkipFirmwareUpdate", "enabled")
|
||||||
if answer == QtWidgets.QMessageBox.Yes:
|
if answer == QtWidgets.QMessageBox.Yes:
|
||||||
self.ShowFirmwareUpdateWindow()
|
self.ShowFirmwareUpdateWindow()
|
||||||
else:
|
elif dev.FW_UPDATE_REQ:
|
||||||
#self.mnuTools.actions()[2].setEnabled(False)
|
text = "A firmware update for your {:s} device is required to use this software. Please visit the official website (<a href=\"{:s}\">{:s}</a>) for updates.<br><br>Current firmware version: {:s}".format(dev.GetFullName(), dev.GetOfficialWebsite(), dev.GetOfficialWebsite(), dev.GetFirmwareVersion())
|
||||||
pass
|
if not Util.DEBUG:
|
||||||
|
self.DisconnectDevice()
|
||||||
|
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), text, QtWidgets.QMessageBox.Ok)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
@ -755,7 +758,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
dontShowAgain = str(self.SETTINGS.value("SkipFinishMessage", default="disabled")).lower() == "enabled"
|
dontShowAgain = str(self.SETTINGS.value("SkipFinishMessage", default="disabled")).lower() == "enabled"
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Operation complete!", standardButtons=QtWidgets.QMessageBox.Ok)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Information, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text="Operation complete!", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||||
cb = QtWidgets.QCheckBox("Don’t show this message again", checked=False)
|
cb = QtWidgets.QCheckBox("Don’t show this message again", checked=False)
|
||||||
msgbox.setCheckBox(cb)
|
|
||||||
|
|
||||||
time_elapsed = None
|
time_elapsed = None
|
||||||
if "time_start" in self.STATUS and self.STATUS["time_start"] > 0:
|
if "time_start" in self.STATUS and self.STATUS["time_start"] > 0:
|
||||||
|
|
@ -791,32 +793,41 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
|
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
|
||||||
self.lblStatus4a.setText("Done!")
|
self.lblStatus4a.setText("Done!")
|
||||||
msg = "The ROM backup is complete and the checksum was verified successfully!"
|
msg = "The ROM backup is complete and the checksum was verified successfully!"
|
||||||
msg += "\n\nCRC32: {:04X}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
msg += "\n\nCRC32: {:08x}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
||||||
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
||||||
msgbox.setText(msg)
|
msgbox.setText(msg)
|
||||||
|
msgbox.setCheckBox(cb)
|
||||||
if not dontShowAgain:
|
if not dontShowAgain:
|
||||||
msgbox.exec()
|
msgbox.exec()
|
||||||
dontShowAgain = cb.isChecked()
|
dontShowAgain = cb.isChecked()
|
||||||
else:
|
else:
|
||||||
self.lblHeaderROMChecksumResult.setText("Invalid (0x{:04X}≠0x{:04X})".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["rom_checksum"]))
|
|
||||||
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
|
|
||||||
self.lblStatus4a.setText("Done.")
|
self.lblStatus4a.setText("Done.")
|
||||||
if ("cart_type" in self.STATUS and "dmg-mmsa-jpn" in self.STATUS["cart_type"]) or ("features_raw" in self.CONN.INFO and self.CONN.INFO["features_raw"] in (0x105, 0x202)):
|
if ("cart_type" in self.STATUS and "dmg-mmsa-jpn" in self.STATUS["cart_type"]) or ("mapper_raw" in self.CONN.INFO and self.CONN.INFO["mapper_raw"] in (0x105, 0x202, 0x203)):
|
||||||
msg = "The ROM backup is complete."
|
msg = "The ROM backup is complete."
|
||||||
msg += "\n\nCRC32: {:04X}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
msg += "\n\nCRC32: {:08x}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
||||||
QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
|
msgbox.setText(msg)
|
||||||
|
msgbox.exec()
|
||||||
else:
|
else:
|
||||||
msg = "The ROM was dumped, but the checksum is not correct. This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
|
self.lblHeaderROMChecksumResult.setText("Invalid (0x{:04X}≠0x{:04X})".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["rom_checksum"]))
|
||||||
msg += "\n\nCRC32: {:04X}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
self.lblHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||||
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
|
msg = "The ROM was dumped, but the checksum is not correct."
|
||||||
|
if self.CONN.INFO["loop_detected"] is not False:
|
||||||
|
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
|
||||||
|
else:
|
||||||
|
msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
|
||||||
|
msg += "\n\nCRC32: {:08x}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
||||||
|
msgbox.setText(msg)
|
||||||
|
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
|
||||||
|
msgbox.exec()
|
||||||
elif self.CONN.GetMode() == "AGB":
|
elif self.CONN.GetMode() == "AGB":
|
||||||
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
|
if Util.AGB_Global_CRC32 == self.CONN.INFO["rom_checksum_calc"]:
|
||||||
self.lblAGBHeaderROMChecksumResult.setText("Valid (0x{:06X})".format(Util.AGB_Global_CRC32))
|
self.lblAGBHeaderROMChecksumResult.setText("Valid (0x{:06X})".format(Util.AGB_Global_CRC32))
|
||||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
|
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: green; }")
|
||||||
self.lblStatus4a.setText("Done!")
|
self.lblStatus4a.setText("Done!")
|
||||||
msg = "The ROM backup is complete and the checksum was verified successfully!"
|
msg = "The ROM backup is complete and the checksum was verified successfully!"
|
||||||
msg += "\n\nCRC32: {:04X}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
msg += "\n\nCRC32: {:08x}\nSHA-1: {:s}".format(self.CONN.INFO["file_crc32"], self.CONN.INFO["file_sha1"])
|
||||||
msgbox.setText(msg)
|
msgbox.setText(msg)
|
||||||
|
msgbox.setCheckBox(cb)
|
||||||
if not dontShowAgain:
|
if not dontShowAgain:
|
||||||
msgbox.exec()
|
msgbox.exec()
|
||||||
dontShowAgain = cb.isChecked()
|
dontShowAgain = cb.isChecked()
|
||||||
|
|
@ -825,17 +836,27 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
self.lblAGBHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
self.lblStatus4a.setText("Done!")
|
self.lblStatus4a.setText("Done!")
|
||||||
msg = "The ROM backup is complete! As there is no known checksum for this ROM in the database, verification was skipped."
|
msg = "The ROM backup is complete! As there is no known checksum for this ROM in the database, verification was skipped."
|
||||||
msg += "\n\nCRC32: {:04X}\nSHA-1: {:s}".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["file_sha1"])
|
if self.CONN.INFO["loop_detected"] is not False:
|
||||||
|
msg += "\n\nNOTE: A data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
|
||||||
|
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
|
||||||
|
msg += "\n\nCRC32: {:08x}\nSHA-1: {:s}".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["file_sha1"])
|
||||||
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
||||||
QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
|
msgbox.setText(msg)
|
||||||
|
msgbox.exec()
|
||||||
else:
|
else:
|
||||||
self.lblAGBHeaderROMChecksumResult.setText("Invalid (0x{:06X}≠0x{:06X})".format(self.CONN.INFO["rom_checksum_calc"], Util.AGB_Global_CRC32))
|
self.lblAGBHeaderROMChecksumResult.setText("Invalid (0x{:06X}≠0x{:06X})".format(self.CONN.INFO["rom_checksum_calc"], Util.AGB_Global_CRC32))
|
||||||
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
|
self.lblAGBHeaderROMChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||||
self.lblStatus4a.setText("Done.")
|
self.lblStatus4a.setText("Done.")
|
||||||
msg = "The ROM backup is complete, but the checksum doesn’t match the known database entry. This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
|
msg = "The ROM backup is complete, but the checksum doesn’t match the known database entry."
|
||||||
msg += "\n\nCRC32: {:04X}\nSHA-1: {:s}".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["file_sha1"])
|
if self.CONN.INFO["loop_detected"] is not False:
|
||||||
|
msg += "\n\nA data loop was detected in the ROM backup at position 0x{:X} ({:s}). This may indicate a bad dump or overdump.".format(self.CONN.INFO["loop_detected"], Util.formatFileSize(self.CONN.INFO["loop_detected"], asInt=True))
|
||||||
|
else:
|
||||||
|
msg += " This may indicate a bad dump, however this can be normal for some reproduction cartridges, unlicensed games, prototypes, patched games and intentional overdumps."
|
||||||
|
msg += "\n\nCRC32: {:08x}\nSHA-1: {:s}".format(self.CONN.INFO["rom_checksum_calc"], self.CONN.INFO["file_sha1"])
|
||||||
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
||||||
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
|
msgbox.setText(msg)
|
||||||
|
msgbox.setIcon(QtWidgets.QMessageBox.Warning)
|
||||||
|
msgbox.exec()
|
||||||
|
|
||||||
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
|
elif self.CONN.INFO["last_action"] == 2: # Backup RAM
|
||||||
self.lblStatus4a.setText("Done!")
|
self.lblStatus4a.setText("Done!")
|
||||||
|
|
@ -853,6 +874,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
return
|
return
|
||||||
|
|
||||||
msgbox.setText("The save data backup is complete!")
|
msgbox.setText("The save data backup is complete!")
|
||||||
|
msgbox.setCheckBox(cb)
|
||||||
if not dontShowAgain:
|
if not dontShowAgain:
|
||||||
msgbox.exec()
|
msgbox.exec()
|
||||||
dontShowAgain = cb.isChecked()
|
dontShowAgain = cb.isChecked()
|
||||||
|
|
@ -868,6 +890,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
else:
|
else:
|
||||||
msg_text = "Save data writing complete!"
|
msg_text = "Save data writing complete!"
|
||||||
msgbox.setText(msg_text)
|
msgbox.setText(msg_text)
|
||||||
|
msgbox.setCheckBox(cb)
|
||||||
if not dontShowAgain:
|
if not dontShowAgain:
|
||||||
msgbox.exec()
|
msgbox.exec()
|
||||||
dontShowAgain = cb.isChecked()
|
dontShowAgain = cb.isChecked()
|
||||||
|
|
@ -895,6 +918,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
|
|
||||||
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
if time_elapsed is not None: msg += "\n\nTotal time elapsed: {:s}".format(Util.formatProgressTime(time_elapsed))
|
||||||
msgbox.setText(msg)
|
msgbox.setText(msg)
|
||||||
|
msgbox.setCheckBox(cb)
|
||||||
if not dontShowAgain:
|
if not dontShowAgain:
|
||||||
msgbox.exec()
|
msgbox.exec()
|
||||||
dontShowAgain = cb.isChecked()
|
dontShowAgain = cb.isChecked()
|
||||||
|
|
@ -908,8 +932,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
|
|
||||||
if dontShowAgain: self.SETTINGS.setValue("SkipFinishMessage", "enabled")
|
if dontShowAgain: self.SETTINGS.setValue("SkipFinishMessage", "enabled")
|
||||||
self.SetProgressBars(min=0, max=1, value=1)
|
self.SetProgressBars(min=0, max=1, value=1)
|
||||||
|
|
||||||
|
def DMGMapperTypeChanged(self, index):
|
||||||
|
if index in (-1, 0): return
|
||||||
|
#if ((list(Util.DMG_Header_Mapper.items())[index])[0]) == 0x203: # Xploder GB
|
||||||
|
# self.cmbHeaderROMSizeResult.setCurrentIndex(Util.DMG_Header_ROM_Sizes_Flasher_Map.index(0x40000))
|
||||||
|
|
||||||
def CartridgeTypeChanged(self, index):
|
def CartridgeTypeChanged(self, index):
|
||||||
|
if index in (-1, 0): return
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
||||||
if cart_types[1][index] == "RETAIL": # special keyword
|
if cart_types[1][index] == "RETAIL": # special keyword
|
||||||
|
|
@ -996,7 +1026,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
just_erase = False
|
just_erase = False
|
||||||
path = ""
|
path = ""
|
||||||
if dpath != "":
|
if dpath != "":
|
||||||
text = "The following ROM file will now be written to the flash cartridge:\n" + dpath
|
ext = os.path.splitext(dpath)[1]
|
||||||
|
if ext.lower() == ".isx":
|
||||||
|
text = "The following ISX file will now be converted to a regular ROM file and then written to the flash cartridge:\n" + dpath
|
||||||
|
else:
|
||||||
|
text = "The following ROM file will now be written to the flash cartridge:\n" + dpath
|
||||||
answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok)
|
answer = QtWidgets.QMessageBox.question(self, "{:s} {:s}".format(APPNAME, VERSION), text, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Ok)
|
||||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||||
path = dpath
|
path = dpath
|
||||||
|
|
@ -1029,9 +1063,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
elif self.CONN.GetMode() == "AGB":
|
elif self.CONN.GetMode() == "AGB":
|
||||||
self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
|
self.cmbAGBCartridgeTypeResult.setCurrentIndex(cart_type)
|
||||||
|
|
||||||
|
mbc = (list(Util.DMG_Header_Mapper.items())[self.cmbHeaderFeaturesResult.currentIndex()])[0]
|
||||||
|
|
||||||
if (path == ""):
|
if (path == ""):
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
path = QtWidgets.QFileDialog.getOpenFileName(self, "Write ROM", last_dir, "Game Boy ROM File (*.gb *.gbc *.sgb *.bin);;All Files (*.*)")[0]
|
path = QtWidgets.QFileDialog.getOpenFileName(self, "Write ROM", last_dir, "Game Boy ROM File (*.gb *.gbc *.sgb *.bin *.isx);;All Files (*.*)")[0]
|
||||||
elif self.CONN.GetMode() == "AGB":
|
elif self.CONN.GetMode() == "AGB":
|
||||||
path = QtWidgets.QFileDialog.getOpenFileName(self, "Write ROM", last_dir, "Game Boy Advance ROM File (*.gba *.srl *.bin);;All Files (*.*)")[0]
|
path = QtWidgets.QFileDialog.getOpenFileName(self, "Write ROM", last_dir, "Game Boy Advance ROM File (*.gba *.srl *.bin);;All Files (*.*)")[0]
|
||||||
|
|
||||||
|
|
@ -1049,7 +1085,13 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "ROM files bigger than 256 MB are not supported.", QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "ROM files bigger than 256 MB are not supported.", QtWidgets.QMessageBox.Ok)
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(path, "rb") as file: buffer = bytearray(file.read(0x1000))
|
with open(path, "rb") as file:
|
||||||
|
ext = os.path.splitext(path)[1]
|
||||||
|
if ext.lower() == ".isx":
|
||||||
|
buffer = bytearray(file.read())
|
||||||
|
buffer = Util.isx2bin(buffer)
|
||||||
|
else:
|
||||||
|
buffer = bytearray(file.read(0x1000))
|
||||||
rom_size = os.stat(path).st_size
|
rom_size = os.stat(path).st_size
|
||||||
if "flash_size" in carts[cart_type]:
|
if "flash_size" in carts[cart_type]:
|
||||||
if rom_size > carts[cart_type]['flash_size']:
|
if rom_size > carts[cart_type]['flash_size']:
|
||||||
|
|
@ -1057,6 +1099,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
msg += " You can still give it a try, but it’s possible that it’s too large which may cause the ROM writing to fail."
|
msg += " You can still give it a try, but it’s possible that it’s too large which may cause the ROM writing to fail."
|
||||||
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
|
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
|
||||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||||
|
if "mbc" in carts[cart_type]:
|
||||||
|
mbc = carts[cart_type]["mbc"]
|
||||||
|
|
||||||
override_voltage = False
|
override_voltage = False
|
||||||
if 'voltage_variants' in carts[cart_type] and carts[cart_type]['voltage'] == 3.3:
|
if 'voltage_variants' in carts[cart_type] and carts[cart_type]['voltage'] == 3.3:
|
||||||
|
|
@ -1102,10 +1146,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
hdr = RomFileDMG(buffer).GetHeader()
|
hdr = RomFileDMG(buffer).GetHeader()
|
||||||
elif self.CONN.GetMode() == "AGB":
|
elif self.CONN.GetMode() == "AGB":
|
||||||
hdr = RomFileAGB(buffer).GetHeader()
|
hdr = RomFileAGB(buffer).GetHeader()
|
||||||
if not hdr["logo_correct"]:
|
if not hdr["logo_correct"] and mbc != 0x203:
|
||||||
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "Warning: The ROM file you selected will not boot on actual hardware due to invalid logo data.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
|
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "Warning: The ROM file you selected will not boot on actual hardware due to invalid logo data.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
|
||||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||||
if not hdr["header_checksum_correct"]:
|
if not hdr["header_checksum_correct"] and mbc != 0x203:
|
||||||
msg_text = "Warning: The ROM file you selected will not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).".format(hdr["header_checksum_calc"], hdr["header_checksum"])
|
msg_text = "Warning: The ROM file you selected will not boot on actual hardware due to an invalid header checksum (expected 0x{:02X} instead of 0x{:02X}).".format(hdr["header_checksum_calc"], hdr["header_checksum"])
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg_text)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=msg_text)
|
||||||
button_fix = msgbox.addButton(" &Fix and Continue ", QtWidgets.QMessageBox.ActionRole)
|
button_fix = msgbox.addButton(" &Fix and Continue ", QtWidgets.QMessageBox.ActionRole)
|
||||||
|
|
@ -1128,9 +1172,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.btnConfig.setEnabled(False)
|
self.btnConfig.setEnabled(False)
|
||||||
self.lblStatus4a.setText("Preparing...")
|
self.lblStatus4a.setText("Preparing...")
|
||||||
qt_app.processEvents()
|
qt_app.processEvents()
|
||||||
if just_erase:
|
if len(buffer) > 0x1000 or just_erase:
|
||||||
prefer_chip_erase = True
|
if just_erase:
|
||||||
verify_write = False
|
prefer_chip_erase = True
|
||||||
|
verify_write = False
|
||||||
args = { "path":"", "buffer":buffer, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header }
|
args = { "path":"", "buffer":buffer, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header }
|
||||||
else:
|
else:
|
||||||
args = { "path":path, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header }
|
args = { "path":path, "cart_type":cart_type, "override_voltage":override_voltage, "prefer_chip_erase":prefer_chip_erase, "reverse_sectors":reverse_sectors, "fast_read_mode":True, "verify_write":verify_write, "fix_header":fix_header }
|
||||||
|
|
@ -1254,7 +1299,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "The save data on your cartridge will now be erased.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
|
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "The save data on your cartridge will now be erased.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
|
||||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||||
else:
|
else:
|
||||||
path = path + ".sav"
|
path = re.sub(r"[<>:\"/\\|\?\*]", "_", path) + ".sav"
|
||||||
path = QtWidgets.QFileDialog.getOpenFileName(self, "Restore Save Data", last_dir + "/" + path, "Save Data File (*.sav);;All Files (*.*)")[0]
|
path = QtWidgets.QFileDialog.getOpenFileName(self, "Restore Save Data", last_dir + "/" + path, "Save Data File (*.sav);;All Files (*.*)")[0]
|
||||||
if not path == "": self.SETTINGS.setValue(setting_name, os.path.dirname(path))
|
if not path == "": self.SETTINGS.setValue(setting_name, os.path.dirname(path))
|
||||||
if (path == ""): return
|
if (path == ""): return
|
||||||
|
|
@ -1282,7 +1327,10 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
cb = QtWidgets.QCheckBox("&Adjust RTC", checked=True)
|
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 = 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)
|
msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
|
||||||
msgbox.setCheckBox(cb)
|
if erase:
|
||||||
|
cb.setChecked(True)
|
||||||
|
else:
|
||||||
|
msgbox.setCheckBox(cb)
|
||||||
answer = msgbox.exec()
|
answer = msgbox.exec()
|
||||||
if answer == QtWidgets.QMessageBox.Cancel: return
|
if answer == QtWidgets.QMessageBox.Cancel: return
|
||||||
rtc_advance = cb.isChecked()
|
rtc_advance = cb.isChecked()
|
||||||
|
|
@ -1442,7 +1490,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
if data['has_rtc']:
|
if data['has_rtc']:
|
||||||
if 'rtc_buffer' in data:
|
if 'rtc_buffer' in data:
|
||||||
try:
|
try:
|
||||||
if data['features_raw'] == 0x10: # MBC3
|
if data['mapper_raw'] == 0x10: # MBC3
|
||||||
rtc_s = data["rtc_buffer"][0x00]
|
rtc_s = data["rtc_buffer"][0x00]
|
||||||
rtc_m = data["rtc_buffer"][0x04]
|
rtc_m = data["rtc_buffer"][0x04]
|
||||||
rtc_h = data["rtc_buffer"][0x08]
|
rtc_h = data["rtc_buffer"][0x08]
|
||||||
|
|
@ -1456,7 +1504,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.lblHeaderRtcResult.setText("{:d} day, {:02d}:{:02d}:{:02d}".format(rtc_d, rtc_h, rtc_m, rtc_s))
|
self.lblHeaderRtcResult.setText("{:d} day, {:02d}:{:02d}:{:02d}".format(rtc_d, rtc_h, rtc_m, rtc_s))
|
||||||
else:
|
else:
|
||||||
self.lblHeaderRtcResult.setText("{:d} days, {:02d}:{:02d}:{:02d}".format(rtc_d, rtc_h, rtc_m, rtc_s))
|
self.lblHeaderRtcResult.setText("{:d} days, {:02d}:{:02d}:{:02d}".format(rtc_d, rtc_h, rtc_m, rtc_s))
|
||||||
elif data['features_raw'] == 0xFE: # HuC-3
|
elif data['mapper_raw'] == 0xFE: # HuC-3
|
||||||
rtc_buffer = struct.unpack("<I", data["rtc_buffer"][0:4])[0]
|
rtc_buffer = struct.unpack("<I", data["rtc_buffer"][0:4])[0]
|
||||||
rtc_h = math.floor((rtc_buffer & 0xFFF) / 60)
|
rtc_h = math.floor((rtc_buffer & 0xFFF) / 60)
|
||||||
rtc_m = (rtc_buffer & 0xFFF) % 60
|
rtc_m = (rtc_buffer & 0xFFF) % 60
|
||||||
|
|
@ -1474,7 +1522,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
if 'no_rtc_reason' in data:
|
if 'no_rtc_reason' in data:
|
||||||
if data["no_rtc_reason"] == -1:
|
if data["no_rtc_reason"] == -1:
|
||||||
self.lblHeaderRtcResult.setText("Unknown")
|
self.lblHeaderRtcResult.setText("Unknown")
|
||||||
if data['features_raw'] == 0xFD: # TAMA5
|
if data['mapper_raw'] == 0xFD: # TAMA5
|
||||||
self.lblHeaderRtcResult.setText("OK")
|
self.lblHeaderRtcResult.setText("OK")
|
||||||
|
|
||||||
if data['logo_correct']:
|
if data['logo_correct']:
|
||||||
|
|
@ -1491,13 +1539,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.lblHeaderChecksumResult.setStyleSheet("QLabel { color: red; }")
|
self.lblHeaderChecksumResult.setStyleSheet("QLabel { color: red; }")
|
||||||
self.lblHeaderROMChecksumResult.setText("0x{:04X}".format(data['rom_checksum']))
|
self.lblHeaderROMChecksumResult.setText("0x{:04X}".format(data['rom_checksum']))
|
||||||
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
|
|
||||||
self.cmbHeaderROMSizeResult.setCurrentIndex(data["rom_size_raw"])
|
self.cmbHeaderROMSizeResult.setCurrentIndex(data["rom_size_raw"])
|
||||||
for i in range(0, len(Util.DMG_Header_RAM_Sizes_Map)):
|
for i in range(0, len(Util.DMG_Header_RAM_Sizes_Map)):
|
||||||
if data["ram_size_raw"] == Util.DMG_Header_RAM_Sizes_Map[i]:
|
if data["ram_size_raw"] == Util.DMG_Header_RAM_Sizes_Map[i]:
|
||||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(i)
|
self.cmbHeaderRAMSizeResult.setCurrentIndex(i)
|
||||||
i = 0
|
i = 0
|
||||||
for k in Util.DMG_Header_Mapper.keys():
|
for k in Util.DMG_Header_Mapper.keys():
|
||||||
if data["features_raw"] == k:
|
if data["mapper_raw"] == k:
|
||||||
self.cmbHeaderFeaturesResult.setCurrentIndex(i)
|
self.cmbHeaderFeaturesResult.setCurrentIndex(i)
|
||||||
if k == 0x06: # MBC2
|
if k == 0x06: # MBC2
|
||||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(1)
|
self.cmbHeaderRAMSizeResult.setCurrentIndex(1)
|
||||||
|
|
@ -1518,12 +1567,12 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
else:
|
else:
|
||||||
self.lblHeaderTitleResult.setText("(No ROM data detected)")
|
self.lblHeaderTitleResult.setText("(No ROM data detected)")
|
||||||
self.lblHeaderTitleResult.setStyleSheet("QLabel { color: red; }")
|
self.lblHeaderTitleResult.setStyleSheet("QLabel { color: red; }")
|
||||||
self.cmbHeaderROMSizeResult.setCurrentIndex(11)
|
self.cmbHeaderROMSizeResult.setCurrentIndex(0)
|
||||||
self.cmbHeaderRAMSizeResult.setCurrentIndex(0)
|
self.cmbHeaderRAMSizeResult.setCurrentIndex(0)
|
||||||
self.cmbHeaderFeaturesResult.setCurrentIndex(0)
|
self.cmbHeaderFeaturesResult.setCurrentIndex(0)
|
||||||
else:
|
else:
|
||||||
self.lblHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
self.lblHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["features_raw"]) and resetStatus:
|
if data['logo_correct'] and not self.CONN.IsSupportedMbc(data["mapper_raw"]) and resetStatus:
|
||||||
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates in the Tools menu or the maker’s website.".format(APPNAME, self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "This cartridge uses a mapper that may not be completely supported by {:s} using the current firmware version of the {:s} device. Please check for firmware updates in the Tools menu or the maker’s website.".format(APPNAME, self.CONN.GetFullName()), QtWidgets.QMessageBox.Ok)
|
||||||
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU "):
|
if data['logo_correct'] and data['game_title'] in ("NP M-MENU MENU", "DMG MULTI MENU "):
|
||||||
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
cart_types = self.CONN.GetSupportedCartridgesDMG()
|
||||||
|
|
@ -1531,6 +1580,20 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
if "dmg-mmsa-jpn" in cart_types[1][i]:
|
if "dmg-mmsa-jpn" in cart_types[1][i]:
|
||||||
self.cmbDMGCartridgeTypeResult.setCurrentIndex(i)
|
self.cmbDMGCartridgeTypeResult.setCurrentIndex(i)
|
||||||
|
|
||||||
|
if data["mapper_raw"] == 0x203: # Xploder GB
|
||||||
|
self.lblHeaderRtcResult.setText("")
|
||||||
|
self.lblHeaderLogoValidResult.setText("")
|
||||||
|
self.lblHeaderLogoValidResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
|
self.lblHeaderChecksumResult.setText("")
|
||||||
|
self.lblHeaderChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
|
self.lblHeaderROMChecksumResult.setText("")
|
||||||
|
self.lblHeaderROMChecksumResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
|
elif data["mapper_raw"] == 0x204: # Sachen
|
||||||
|
self.lblHeaderRtcResult.setText("")
|
||||||
|
self.lblHeaderRevisionResult.setText("")
|
||||||
|
self.lblHeaderLogoValidResult.setText("")
|
||||||
|
self.lblHeaderLogoValidResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
|
|
||||||
self.grpAGBCartridgeInfo.setVisible(False)
|
self.grpAGBCartridgeInfo.setVisible(False)
|
||||||
self.grpDMGCartridgeInfo.setVisible(True)
|
self.grpDMGCartridgeInfo.setVisible(True)
|
||||||
|
|
||||||
|
|
@ -1627,7 +1690,6 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
else:
|
else:
|
||||||
self.lblAGBHeaderTitleResult.setText("(No ROM data detected)")
|
self.lblAGBHeaderTitleResult.setText("(No ROM data detected)")
|
||||||
self.lblAGBHeaderTitleResult.setStyleSheet("QLabel { color: red; }")
|
self.lblAGBHeaderTitleResult.setStyleSheet("QLabel { color: red; }")
|
||||||
self.cmbAGBHeaderROMSizeResult.setCurrentIndex(3)
|
|
||||||
self.cmbAGBSaveTypeResult.setCurrentIndex(0)
|
self.cmbAGBSaveTypeResult.setCurrentIndex(0)
|
||||||
else:
|
else:
|
||||||
self.lblAGBHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
self.lblAGBHeaderTitleResult.setStyleSheet(self.lblHeaderRevisionResult.styleSheet())
|
||||||
|
|
@ -1646,7 +1708,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.grpStatus.setTitle("Transfer Status")
|
self.grpStatus.setTitle("Transfer Status")
|
||||||
self.FinishOperation()
|
self.FinishOperation()
|
||||||
|
|
||||||
if not data['logo_correct'] and data['empty'] == False and resetStatus:
|
if not data['logo_correct'] and data['empty'] == False and resetStatus and not (self.CONN.GetMode() == "DMG" and data["mapper_raw"] in (0x203, 0x204)):
|
||||||
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "The Nintendo Logo check failed which usually means that the cartridge can’t be read correctly. Please make sure you selected the correct mode and that the cartridge contacts are clean.", QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), "The Nintendo Logo check failed which usually means that the cartridge can’t be read correctly. Please make sure you selected the correct mode and that the cartridge contacts are clean.", QtWidgets.QMessageBox.Ok)
|
||||||
|
|
||||||
if data['game_title'][:11] == "YJencrypted" and resetStatus:
|
if data['game_title'][:11] == "YJencrypted" and resetStatus:
|
||||||
|
|
@ -1852,6 +1914,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
msg_fw = ""
|
msg_fw = ""
|
||||||
button_clipboard = None
|
button_clipboard = None
|
||||||
|
|
||||||
|
if self.CONN.GetMode() == "DMG" and limitVoltage and (is_generic or not found_supported):
|
||||||
|
text = "No known flash cartridge type could be detected. The option “Limit voltage to 3.3V when detecting Game Boy flash cartridges” has been enabled which can cause auto-detection to fail. As it is usually not recommended to enable this option, do you now want to disable it and try again?"
|
||||||
|
answer = QtWidgets.QMessageBox.warning(self, "{:s} {:s}".format(APPNAME, VERSION), text, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
|
||||||
|
if answer == QtWidgets.QMessageBox.Yes:
|
||||||
|
self.SETTINGS.setValue("AutoDetectLimitVoltage", "disabled")
|
||||||
|
self.mnuConfig.actions()[4].setChecked(False)
|
||||||
|
return self.DetectCartridge()
|
||||||
|
|
||||||
temp = "{:s}{:s}{:s}{:s}{:s}{:s}{:s}{:s}".format(msg, msg_header_s, msg_flash_size_s, msg_save_type_s, msg_flash_id_s, msg_cfi_s, msg_cart_type_s_detail, msg_fw)
|
temp = "{:s}{:s}{:s}{:s}{:s}{:s}{:s}{:s}".format(msg, msg_header_s, msg_flash_size_s, msg_save_type_s, msg_flash_id_s, msg_cfi_s, msg_cart_type_s_detail, msg_fw)
|
||||||
temp = temp[:-4]
|
temp = temp[:-4]
|
||||||
msgbox.setText(temp)
|
msgbox.setText(temp)
|
||||||
|
|
@ -1945,6 +2015,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.lblStatus4aResult.setText("")
|
self.lblStatus4aResult.setText("")
|
||||||
self.btnCancel.setEnabled(args["abortable"])
|
self.btnCancel.setEnabled(args["abortable"])
|
||||||
self.SetProgressBars(min=0, max=size, value=pos)
|
self.SetProgressBars(min=0, max=size, value=pos)
|
||||||
|
elif args["action"] == "UPDATE_RTC":
|
||||||
|
self.lblStatus1aResult.setText("Pending...")
|
||||||
|
self.lblStatus2aResult.setText("Pending...")
|
||||||
|
self.lblStatus3aResult.setText("Pending...")
|
||||||
|
self.lblStatus4a.setText("Updating Real Time Clock...")
|
||||||
|
self.lblStatus4aResult.setText("")
|
||||||
|
self.btnCancel.setEnabled(False)
|
||||||
|
self.SetProgressBars(min=0, max=size, value=pos)
|
||||||
elif args["action"] == "SECTOR_ERASE":
|
elif args["action"] == "SECTOR_ERASE":
|
||||||
if elapsed >= 1:
|
if elapsed >= 1:
|
||||||
self.lblStatus3aResult.setText(Util.formatProgressTime(elapsed))
|
self.lblStatus3aResult.setText(Util.formatProgressTime(elapsed))
|
||||||
|
|
@ -2065,7 +2143,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
self.SetMode()
|
self.SetMode()
|
||||||
if self.CONN.GetMode() == "DMG":
|
if self.CONN.GetMode() == "DMG":
|
||||||
header = self.CONN.ReadInfo(setPinsAsInputs=True)
|
header = self.CONN.ReadInfo(setPinsAsInputs=True)
|
||||||
if header["features_raw"] == 252: # GBD
|
if header["mapper_raw"] == 252: # GBD
|
||||||
args = { "path":None, "mbc":252, "save_type":128*1024, "rtc":False }
|
args = { "path":None, "mbc":252, "save_type":128*1024, "rtc":False }
|
||||||
self.CONN.BackupRAM(fncSetProgress=False, args=args)
|
self.CONN.BackupRAM(fncSetProgress=False, args=args)
|
||||||
data = self.CONN.INFO["data"]
|
data = self.CONN.INFO["data"]
|
||||||
|
|
@ -2100,7 +2178,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
fn_split = os.path.splitext(os.path.abspath(fn))
|
fn_split = os.path.splitext(os.path.abspath(fn))
|
||||||
if fn_split[1].lower() == ".sav":
|
if fn_split[1].lower() == ".sav":
|
||||||
return True
|
return True
|
||||||
elif self.CONN.GetMode() == "DMG" and fn_split[1].lower() in (".gb", ".sgb", ".gbc", ".bin"):
|
elif self.CONN.GetMode() == "DMG" and fn_split[1].lower() in (".gb", ".sgb", ".gbc", ".bin", ".isx"):
|
||||||
return True
|
return True
|
||||||
elif self.CONN.GetMode() == "AGB" and fn_split[1].lower() in (".gba", ".srl"):
|
elif self.CONN.GetMode() == "AGB" and fn_split[1].lower() in (".gba", ".srl"):
|
||||||
return True
|
return True
|
||||||
|
|
@ -2120,7 +2198,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
|
||||||
fn = str(url.toLocalFile())
|
fn = str(url.toLocalFile())
|
||||||
|
|
||||||
fn_split = os.path.splitext(os.path.abspath(fn))
|
fn_split = os.path.splitext(os.path.abspath(fn))
|
||||||
if fn_split[1].lower() in (".gb", ".sgb", ".gbc", ".bin", ".gba", ".srl"):
|
if fn_split[1].lower() in (".gb", ".sgb", ".gbc", ".bin", ".isx", ".gba", ".srl"):
|
||||||
self.FlashROM(fn)
|
self.FlashROM(fn)
|
||||||
elif fn_split[1].lower() == ".sav":
|
elif fn_split[1].lower() == ".sav":
|
||||||
self.WriteRAM(fn)
|
self.WriteRAM(fn)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ class Flashcart:
|
||||||
CONFIG = {}
|
CONFIG = {}
|
||||||
COMMAND_SET = None
|
COMMAND_SET = None
|
||||||
CART_WRITE_FNCPTR = None
|
CART_WRITE_FNCPTR = None
|
||||||
|
CART_WRITE_FAST_FNCPTR = None
|
||||||
CART_READ_FNCPTR = None
|
CART_READ_FNCPTR = None
|
||||||
CART_POWERCYCLE_FNCPTR = None
|
CART_POWERCYCLE_FNCPTR = None
|
||||||
PROGRESS_FNCPTR = None
|
PROGRESS_FNCPTR = None
|
||||||
|
|
@ -17,9 +18,10 @@ class Flashcart:
|
||||||
SECTOR_MAP = None
|
SECTOR_MAP = None
|
||||||
CFI = None
|
CFI = None
|
||||||
|
|
||||||
def __init__(self, config=None, cart_write_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, progress_fncptr=None):
|
def __init__(self, config=None, cart_write_fncptr=None, cart_write_fast_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, progress_fncptr=None):
|
||||||
if config is None: config = {}
|
if config is None: config = {}
|
||||||
self.CART_WRITE_FNCPTR = cart_write_fncptr
|
self.CART_WRITE_FNCPTR = cart_write_fncptr
|
||||||
|
self.CART_WRITE_FAST_FNCPTR = cart_write_fast_fncptr
|
||||||
self.CART_READ_FNCPTR = cart_read_fncptr
|
self.CART_READ_FNCPTR = cart_read_fncptr
|
||||||
self.CART_POWERCYCLE_FNCPTR = cart_powercycle_fncptr
|
self.CART_POWERCYCLE_FNCPTR = cart_powercycle_fncptr
|
||||||
self.PROGRESS_FNCPTR = progress_fncptr
|
self.PROGRESS_FNCPTR = progress_fncptr
|
||||||
|
|
@ -40,12 +42,16 @@ class Flashcart:
|
||||||
return self.CART_READ_FNCPTR(address, length)
|
return self.CART_READ_FNCPTR(address, length)
|
||||||
|
|
||||||
def CartWrite(self, commands, flashcart=True, sram=False):
|
def CartWrite(self, commands, flashcart=True, sram=False):
|
||||||
if "command_set" in self.CONFIG and self.CONFIG["command_set"] == "DMG-MBC5-32M-FLASH": flashcart = False
|
if "command_set" in self.CONFIG and self.CONFIG["command_set"] in ("GBMEMORY", "DMG-MBC5-32M-FLASH"): flashcart = False
|
||||||
for command in commands:
|
dprint(commands, flashcart, sram)
|
||||||
address = command[0]
|
if flashcart and not sram:
|
||||||
value = command[1]
|
self.CART_WRITE_FAST_FNCPTR(commands, flashcart=True)
|
||||||
self.CART_WRITE_FNCPTR(address, value, flashcart=flashcart, sram=sram)
|
else:
|
||||||
|
for command in commands:
|
||||||
|
address = command[0]
|
||||||
|
value = command[1]
|
||||||
|
self.CART_WRITE_FNCPTR(address, value, flashcart=flashcart, sram=sram)
|
||||||
|
|
||||||
def GetCommandSetType(self):
|
def GetCommandSetType(self):
|
||||||
return self.CONFIG["_command_set"].upper()
|
return self.CONFIG["_command_set"].upper()
|
||||||
|
|
||||||
|
|
@ -144,6 +150,12 @@ class Flashcart:
|
||||||
|
|
||||||
def Unlock(self):
|
def Unlock(self):
|
||||||
self.CartRead(0) # dummy read
|
self.CartRead(0) # dummy read
|
||||||
|
if "unlock_read" in self.CONFIG["commands"]:
|
||||||
|
for command in self.CONFIG["commands"]["unlock_read"]:
|
||||||
|
for _ in range(0, command[2]):
|
||||||
|
temp = self.CartRead(command[0], command[1])
|
||||||
|
dprint("Reading 0x{:X} bytes from cartridge at 0x{:X} = {:s}".format(command[1], command[0], str(temp)))
|
||||||
|
time.sleep(0.001)
|
||||||
if "unlock" in self.CONFIG["commands"]:
|
if "unlock" in self.CONFIG["commands"]:
|
||||||
self.CartWrite(self.CONFIG["commands"]["unlock"])
|
self.CartWrite(self.CONFIG["commands"]["unlock"])
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
|
|
@ -167,13 +179,18 @@ class Flashcart:
|
||||||
def VerifyFlashID(self):
|
def VerifyFlashID(self):
|
||||||
if "read_identifier" not in self.CONFIG["commands"]: return False
|
if "read_identifier" not in self.CONFIG["commands"]: return False
|
||||||
if len(self.CONFIG["flash_ids"]) == 0: return False
|
if len(self.CONFIG["flash_ids"]) == 0: return False
|
||||||
|
if "power_cycle" in self.CONFIG and self.CONFIG["power_cycle"] is True:
|
||||||
|
self.CART_POWERCYCLE_FNCPTR()
|
||||||
self.Reset()
|
self.Reset()
|
||||||
rom = list(self.CartRead(0, len(self.CONFIG["flash_ids"][0])))
|
rom = list(self.CartRead(0, len(self.CONFIG["flash_ids"][0])))
|
||||||
self.Unlock()
|
self.Unlock()
|
||||||
self.CartWrite(self.CONFIG["commands"]["read_identifier"])
|
self.CartWrite(self.CONFIG["commands"]["read_identifier"])
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
cart_flash_id = list(self.CartRead(0, len(self.CONFIG["flash_ids"][0])))
|
read_identifier_at = 0
|
||||||
|
if "read_identifier_at" in self.CONFIG: read_identifier_at = self.CONFIG["read_identifier_at"]
|
||||||
|
cart_flash_id = list(self.CartRead(read_identifier_at, len(self.CONFIG["flash_ids"][0])))
|
||||||
self.Reset()
|
self.Reset()
|
||||||
|
dprint(self.CONFIG["names"], self.CONFIG["commands"]["read_identifier"])
|
||||||
dprint("Flash ID: {:s}".format(' '.join(format(x, '02X') for x in cart_flash_id)))
|
dprint("Flash ID: {:s}".format(' '.join(format(x, '02X') for x in cart_flash_id)))
|
||||||
verified = True
|
verified = True
|
||||||
if (rom == cart_flash_id):
|
if (rom == cart_flash_id):
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ class GBMemoryMap:
|
||||||
IS_MENU = False
|
IS_MENU = False
|
||||||
|
|
||||||
def __init__(self, rom=None):
|
def __init__(self, rom=None):
|
||||||
if rom is not None:
|
if rom == bytearray([0xFF] * len(rom)):
|
||||||
|
self.MAP_DATA = bytearray([0x00] * 0x80)
|
||||||
|
elif rom is not None:
|
||||||
self.ImportROM(rom)
|
self.ImportROM(rom)
|
||||||
|
|
||||||
def ImportROM(self, data):
|
def ImportROM(self, data):
|
||||||
|
|
@ -26,7 +28,7 @@ class GBMemoryMap:
|
||||||
data = bytearray(data) + bytearray([0xFF] * (0x20000 - len(data)))
|
data = bytearray(data) + bytearray([0xFF] * (0x20000 - len(data)))
|
||||||
|
|
||||||
if not self.IS_MENU:
|
if not self.IS_MENU:
|
||||||
mbc_type = self.MapperToMBCType(info["rom_header"]["features_raw"])
|
mbc_type = self.MapperToMBCType(info["rom_header"]["mapper_raw"])
|
||||||
if mbc_type is False: return
|
if mbc_type is False: return
|
||||||
if len(data) <= 0x20000:
|
if len(data) <= 0x20000:
|
||||||
rom_size = 0b010
|
rom_size = 0b010
|
||||||
|
|
@ -127,7 +129,7 @@ class GBMemoryMap:
|
||||||
info["ram_start_block"] = int(ram_offset / 0x800)
|
info["ram_start_block"] = int(ram_offset / 0x800)
|
||||||
ram_offset += info["ram_data_size"]
|
ram_offset += info["ram_data_size"]
|
||||||
info["rom_header"] = RomFileDMG(data[info["rom_data_offset"]:info["rom_data_offset"]+0x180]).GetHeader()
|
info["rom_header"] = RomFileDMG(data[info["rom_data_offset"]:info["rom_data_offset"]+0x180]).GetHeader()
|
||||||
mbc_type = self.MapperToMBCType(info["rom_header"]["features_raw"])
|
mbc_type = self.MapperToMBCType(info["rom_header"]["mapper_raw"])
|
||||||
if mbc_type is False: return
|
if mbc_type is False: return
|
||||||
|
|
||||||
if info["rom_data_size"] <= 0x20000:
|
if info["rom_data_size"] <= 0x20000:
|
||||||
|
|
@ -192,7 +194,7 @@ class GBMemoryMap:
|
||||||
mbc_type = 2
|
mbc_type = 2
|
||||||
elif mbc in (0x10, 0x13): # MBC3
|
elif mbc in (0x10, 0x13): # MBC3
|
||||||
mbc_type = 3
|
mbc_type = 3
|
||||||
elif mbc in (0x19, 0x1A, 0x1B, 0x1C, 0x1E): # MBC5
|
elif mbc in (0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x105): # MBC5
|
||||||
mbc_type = 5
|
mbc_type = 5
|
||||||
else:
|
else:
|
||||||
mbc_type = False
|
mbc_type = False
|
||||||
|
|
@ -217,5 +219,6 @@ class GBMemoryMap:
|
||||||
return self.IS_MENU
|
return self.IS_MENU
|
||||||
|
|
||||||
def GetMapData(self):
|
def GetMapData(self):
|
||||||
if self.MAP_DATA == bytearray([0xFF] * 0x80): return False
|
if self.MAP_DATA == bytearray([0xFF] * 0x80):
|
||||||
|
return False
|
||||||
return self.MAP_DATA
|
return self.MAP_DATA
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ class DMG_MBC:
|
||||||
RAM_BANK_SIZE = 0x2000
|
RAM_BANK_SIZE = 0x2000
|
||||||
ROM_BANK_NUM = 0
|
ROM_BANK_NUM = 0
|
||||||
CURRENT_ROM_BANK = 0
|
CURRENT_ROM_BANK = 0
|
||||||
|
START_BANK = 0
|
||||||
|
|
||||||
def __init__(self, args=None, cart_write_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, clk_toggle_fncptr=None):
|
def __init__(self, args=None, cart_write_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, clk_toggle_fncptr=None):
|
||||||
if args is None: args = {}
|
if args is None: args = {}
|
||||||
|
|
@ -66,6 +67,10 @@ class DMG_MBC:
|
||||||
return DMG_Unlicensed_256M(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
return DMG_Unlicensed_256M(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||||
elif mbc_id == 0x202: # 0x202:'Wisdom Tree Mapper',
|
elif mbc_id == 0x202: # 0x202:'Wisdom Tree Mapper',
|
||||||
return DMG_Unlicensed_WisdomTree(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
return DMG_Unlicensed_WisdomTree(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||||
|
elif mbc_id == 0x203: # 0x203:'Xploder GB',
|
||||||
|
return DMG_Unlicensed_XploderGB(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||||
|
elif mbc_id == 0x204: # 0x204:'Sachen',
|
||||||
|
return DMG_Unlicensed_Sachen(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||||
else:
|
else:
|
||||||
self.__init__(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
self.__init__(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=clk_toggle_fncptr)
|
||||||
return self
|
return self
|
||||||
|
|
@ -83,6 +88,9 @@ class DMG_MBC:
|
||||||
self.CART_WRITE_FNCPTR(address, value)
|
self.CART_WRITE_FNCPTR(address, value)
|
||||||
if delay is not False: time.sleep(delay)
|
if delay is not False: time.sleep(delay)
|
||||||
|
|
||||||
|
def GetID(self):
|
||||||
|
return self.MBC_ID
|
||||||
|
|
||||||
def GetName(self):
|
def GetName(self):
|
||||||
return "Unknown MBC {:d}".format(self.MBC_ID)
|
return "Unknown MBC {:d}".format(self.MBC_ID)
|
||||||
|
|
||||||
|
|
@ -145,6 +153,9 @@ class DMG_MBC:
|
||||||
start_address = 0
|
start_address = 0
|
||||||
self.CartWrite(commands)
|
self.CartWrite(commands)
|
||||||
return (start_address, self.RAM_BANK_SIZE)
|
return (start_address, self.RAM_BANK_SIZE)
|
||||||
|
|
||||||
|
def SetStartBank(self, index):
|
||||||
|
self.START_BANK = index
|
||||||
|
|
||||||
def HasHiddenSector(self):
|
def HasHiddenSector(self):
|
||||||
return False
|
return False
|
||||||
|
|
@ -210,19 +221,19 @@ class DMG_MBC3(DMG_MBC):
|
||||||
def EnableRAM(self, enable=True):
|
def EnableRAM(self, enable=True):
|
||||||
dprint(self.GetName(), "|", enable)
|
dprint(self.GetName(), "|", enable)
|
||||||
commands = [
|
commands = [
|
||||||
#[ 0x6000, 0x01 if enable else 0x00 ],
|
|
||||||
[ 0x0000, 0x0A if enable else 0x00 ],
|
[ 0x0000, 0x0A if enable else 0x00 ],
|
||||||
]
|
]
|
||||||
self.CartWrite(commands)
|
self.CartWrite(commands)
|
||||||
|
|
||||||
def HasRTC(self):
|
def HasRTC(self):
|
||||||
dprint(self.GetName())
|
dprint("Checking for RTC")
|
||||||
if self.MBC_ID != 16: return False
|
if self.MBC_ID != 16:
|
||||||
|
dprint("No RTC because wrong MBC ID", self.MBC_ID)
|
||||||
|
return False
|
||||||
self.EnableRAM(enable=False)
|
self.EnableRAM(enable=False)
|
||||||
self.EnableRAM(enable=True)
|
self.EnableRAM(enable=True)
|
||||||
#self.CartWrite([ [0x4000, 0x08] ])
|
|
||||||
self.LatchRTC()
|
self.LatchRTC()
|
||||||
|
|
||||||
skipped = True
|
skipped = True
|
||||||
for i in range(0x08, 0x0D):
|
for i in range(0x08, 0x0D):
|
||||||
self.CLK_TOGGLE_FNCPTR(60)
|
self.CLK_TOGGLE_FNCPTR(60)
|
||||||
|
|
@ -230,26 +241,20 @@ class DMG_MBC3(DMG_MBC):
|
||||||
data = self.CartRead(0xA000, 0x800)
|
data = self.CartRead(0xA000, 0x800)
|
||||||
if data[0] in (0, 0xFF): continue
|
if data[0] in (0, 0xFF): continue
|
||||||
skipped = False
|
skipped = False
|
||||||
if data != bytearray([data[0]] * 0x800): return False
|
if data != bytearray([data[0]] * 0x800):
|
||||||
return skipped is False
|
dprint("No RTC because whole bank is not the same value", data[0])
|
||||||
|
skipped = True
|
||||||
|
break
|
||||||
|
|
||||||
#ram1 = self.CartRead(0xA000, 0x10)
|
self.EnableRAM(enable=False)
|
||||||
#ram2 = ram1
|
self.CartWrite([ [0x4000, 0] ])
|
||||||
#t1 = time.time()
|
return skipped is False
|
||||||
#t2 = 0
|
|
||||||
#while t2 < (t1 + 1):
|
|
||||||
# self.LatchRTC()
|
|
||||||
# ram2 = self.CartRead(0xA000, 0x10)
|
|
||||||
# if ram1 != ram2: break
|
|
||||||
# t2 = time.time()
|
|
||||||
#dprint("RTC_S {:02X} != {:02X}?".format(ram1[0], ram2[0]), ram1 != ram2)
|
|
||||||
#time.sleep(0.1)
|
|
||||||
#return (ram1 != ram2)
|
|
||||||
|
|
||||||
def GetRTCBufferSize(self):
|
def GetRTCBufferSize(self):
|
||||||
return 0x30
|
return 0x30
|
||||||
|
|
||||||
def LatchRTC(self):
|
def LatchRTC(self):
|
||||||
|
dprint("Latching RTC")
|
||||||
self.CLK_TOGGLE_FNCPTR(60)
|
self.CLK_TOGGLE_FNCPTR(60)
|
||||||
self.CartWrite([ [ 0x0000, 0x0A ] ])
|
self.CartWrite([ [ 0x0000, 0x0A ] ])
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
@ -260,7 +265,9 @@ class DMG_MBC3(DMG_MBC):
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
def ReadRTC(self):
|
def ReadRTC(self):
|
||||||
#self.EnableRAM(enable=True)
|
dprint("Reading RTC")
|
||||||
|
self.EnableRAM(enable=True)
|
||||||
|
|
||||||
buffer = bytearray()
|
buffer = bytearray()
|
||||||
for i in range(0x08, 0x0D):
|
for i in range(0x08, 0x0D):
|
||||||
self.CLK_TOGGLE_FNCPTR(60)
|
self.CLK_TOGGLE_FNCPTR(60)
|
||||||
|
|
@ -272,15 +279,17 @@ class DMG_MBC3(DMG_MBC):
|
||||||
ts = int(time.time())
|
ts = int(time.time())
|
||||||
buffer.extend(struct.pack("<Q", ts))
|
buffer.extend(struct.pack("<Q", ts))
|
||||||
|
|
||||||
|
self.EnableRAM(enable=False)
|
||||||
|
self.CartWrite([ [0x4000, 0] ])
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
def WriteRTC(self, buffer, advance=False):
|
def WriteRTC(self, buffer, advance=False):
|
||||||
dprint(buffer)
|
dprint("Writing RTC:", buffer)
|
||||||
#self.LatchRTC()
|
self.EnableRAM(enable=True)
|
||||||
if advance:
|
if advance:
|
||||||
try:
|
try:
|
||||||
dt_now = datetime.datetime.fromtimestamp(time.time())
|
dt_now = datetime.datetime.fromtimestamp(time.time())
|
||||||
if buffer == bytearray([0xFF] * len(buffer)): # Reset
|
if buffer == bytearray([0x00] * len(buffer)): # Reset
|
||||||
seconds = 0
|
seconds = 0
|
||||||
minutes = 0
|
minutes = 0
|
||||||
hours = 0
|
hours = 0
|
||||||
|
|
@ -295,10 +304,6 @@ class DMG_MBC3(DMG_MBC):
|
||||||
days = days & 0x1FF
|
days = days & 0x1FF
|
||||||
timestamp_then = struct.unpack("<Q", buffer[-8:])[0]
|
timestamp_then = struct.unpack("<Q", buffer[-8:])[0]
|
||||||
timestamp_now = int(time.time())
|
timestamp_now = int(time.time())
|
||||||
#debug
|
|
||||||
#timestamp_now = 1663106370 # 2022-09-13 23:59:30
|
|
||||||
#dt_now = datetime.datetime.fromtimestamp(timestamp_now)
|
|
||||||
#debug
|
|
||||||
dprint(seconds, minutes, hours, days, carry)
|
dprint(seconds, minutes, hours, days, carry)
|
||||||
if timestamp_then < timestamp_now:
|
if timestamp_then < timestamp_now:
|
||||||
dt_then = datetime.datetime.fromtimestamp(timestamp_then)
|
dt_then = datetime.datetime.fromtimestamp(timestamp_then)
|
||||||
|
|
@ -364,6 +369,9 @@ class DMG_MBC3(DMG_MBC):
|
||||||
self.CartWrite([ [ 0x6000, 0x01 ] ])
|
self.CartWrite([ [ 0x6000, 0x01 ] ])
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
self.CartWrite([ [0x4000, 0] ])
|
||||||
|
self.EnableRAM(enable=False)
|
||||||
|
|
||||||
class DMG_MBC5(DMG_MBC):
|
class DMG_MBC5(DMG_MBC):
|
||||||
def GetName(self):
|
def GetName(self):
|
||||||
return "MBC5"
|
return "MBC5"
|
||||||
|
|
@ -817,7 +825,8 @@ class DMG_HuC3(DMG_MBC):
|
||||||
if advance:
|
if advance:
|
||||||
try:
|
try:
|
||||||
dt_now = datetime.datetime.fromtimestamp(time.time())
|
dt_now = datetime.datetime.fromtimestamp(time.time())
|
||||||
if buffer == bytearray([0xFF] * len(buffer)): # Reset
|
print(buffer)
|
||||||
|
if buffer == bytearray([0x00] * len(buffer)): # Reset
|
||||||
hours = 0
|
hours = 0
|
||||||
minutes = 0
|
minutes = 0
|
||||||
days = 0
|
days = 0
|
||||||
|
|
@ -987,7 +996,7 @@ class DMG_TAMA5(DMG_MBC):
|
||||||
if advance:
|
if advance:
|
||||||
try:
|
try:
|
||||||
dt_now = datetime.datetime.fromtimestamp(time.time())
|
dt_now = datetime.datetime.fromtimestamp(time.time())
|
||||||
if buffer == bytearray([0xFF] * len(buffer)): # Reset
|
if buffer == bytearray([0x00] * len(buffer)): # Reset
|
||||||
seconds = 0
|
seconds = 0
|
||||||
minutes = 0
|
minutes = 0
|
||||||
hours = 0
|
hours = 0
|
||||||
|
|
@ -1067,8 +1076,7 @@ class DMG_Unlicensed_256M(DMG_MBC5):
|
||||||
dprint(self.GetName(), "|", index)
|
dprint(self.GetName(), "|", index)
|
||||||
|
|
||||||
flash_bank = math.floor(index / 512)
|
flash_bank = math.floor(index / 512)
|
||||||
if index >= 512:
|
index = index % 512
|
||||||
index -= (512 * flash_bank)
|
|
||||||
|
|
||||||
if index == 0:
|
if index == 0:
|
||||||
self.CART_POWERCYCLE_FNCPTR()
|
self.CART_POWERCYCLE_FNCPTR()
|
||||||
|
|
@ -1122,9 +1130,6 @@ class DMG_Unlicensed_WisdomTree(DMG_MBC):
|
||||||
if args is None: args = {}
|
if args is None: args = {}
|
||||||
self.ROM_BANK_SIZE = 0x8000
|
self.ROM_BANK_SIZE = 0x8000
|
||||||
super().__init__(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=None)
|
super().__init__(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=None)
|
||||||
|
|
||||||
#def GetROMSize(self):
|
|
||||||
# return self.ROM_BANK_SIZE * math.floor(self.ROM_BANK_NUM / 2)
|
|
||||||
|
|
||||||
def SelectBankROM(self, index):
|
def SelectBankROM(self, index):
|
||||||
dprint(self.GetName(), "|", index)
|
dprint(self.GetName(), "|", index)
|
||||||
|
|
@ -1134,6 +1139,44 @@ class DMG_Unlicensed_WisdomTree(DMG_MBC):
|
||||||
self.CartWrite(commands)
|
self.CartWrite(commands)
|
||||||
return (0, 0x8000)
|
return (0, 0x8000)
|
||||||
|
|
||||||
|
class DMG_Unlicensed_XploderGB(DMG_MBC):
|
||||||
|
def GetName(self):
|
||||||
|
return "Xploder GB"
|
||||||
|
|
||||||
|
def __init__(self, args=None, cart_write_fncptr=None, cart_read_fncptr=None, cart_powercycle_fncptr=None, clk_toggle_fncptr=None):
|
||||||
|
if args is None: args = {}
|
||||||
|
super().__init__(args=args, cart_write_fncptr=cart_write_fncptr, cart_read_fncptr=cart_read_fncptr, cart_powercycle_fncptr=cart_powercycle_fncptr, clk_toggle_fncptr=None)
|
||||||
|
self.RAM_BANK_SIZE = 0x4000
|
||||||
|
|
||||||
|
def SelectBankROM(self, index):
|
||||||
|
dprint(self.GetName(), "|", index)
|
||||||
|
if index == 0:
|
||||||
|
self.CART_POWERCYCLE_FNCPTR()
|
||||||
|
self.CartRead(0x0102, 1)
|
||||||
|
self.CartWrite([[ 0x0006, index & 0xFF ]])
|
||||||
|
start_address = 0x4000
|
||||||
|
return (start_address, self.ROM_BANK_SIZE)
|
||||||
|
|
||||||
|
def SelectBankRAM(self, index):
|
||||||
|
dprint(self.GetName(), "|", index)
|
||||||
|
if index == 0:
|
||||||
|
self.CART_POWERCYCLE_FNCPTR()
|
||||||
|
self.CartRead(0x0102, 1)
|
||||||
|
return self.SelectBankROM(index + 8)
|
||||||
|
|
||||||
|
class DMG_Unlicensed_Sachen(DMG_MBC):
|
||||||
|
def GetName(self):
|
||||||
|
return "Sachen"
|
||||||
|
|
||||||
|
def SelectBankROM(self, index):
|
||||||
|
dprint(self.GetName(), "|", index)
|
||||||
|
commands = [
|
||||||
|
[ 0x2000, index + self.START_BANK ]
|
||||||
|
]
|
||||||
|
self.CartWrite(commands)
|
||||||
|
start_address = 0x4000
|
||||||
|
return (start_address, self.ROM_BANK_SIZE)
|
||||||
|
|
||||||
|
|
||||||
class AGB_GPIO:
|
class AGB_GPIO:
|
||||||
CART_WRITE_FNCPTR = None
|
CART_WRITE_FNCPTR = None
|
||||||
|
|
@ -1328,16 +1371,11 @@ class AGB_GPIO:
|
||||||
seconds = Util.DecodeBCD(buffer[0x06])
|
seconds = Util.DecodeBCD(buffer[0x06])
|
||||||
timestamp_then = struct.unpack("<Q", buffer[-8:])[0]
|
timestamp_then = struct.unpack("<Q", buffer[-8:])[0]
|
||||||
timestamp_now = int(time.time())
|
timestamp_now = int(time.time())
|
||||||
#debug
|
|
||||||
#timestamp_now = 4102441140 # 2099-12-23 23:59:00
|
|
||||||
#dt_now = datetime.datetime.fromtimestamp(timestamp_now)
|
|
||||||
#debug
|
|
||||||
if timestamp_then < timestamp_now:
|
if timestamp_then < timestamp_now:
|
||||||
dt_then = datetime.datetime.fromtimestamp(timestamp_then)
|
dt_then = datetime.datetime.fromtimestamp(timestamp_then)
|
||||||
dt_buffer = datetime.datetime.strptime("{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(years + 2000, months % 12, days % 31, hours % 60, minutes % 60, seconds % 60), "%Y-%m-%d %H:%M:%S")
|
dt_buffer = datetime.datetime.strptime("{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(years + 2000, months % 12, days % 31, hours % 60, minutes % 60, seconds % 60), "%Y-%m-%d %H:%M:%S")
|
||||||
rd = relativedelta(dt_now, dt_then)
|
rd = relativedelta(dt_now, dt_then)
|
||||||
dt_new = dt_buffer + rd
|
dt_new = dt_buffer + rd
|
||||||
#print(dt_then, dt_now, dt_buffer, dt_new, sep="\n")
|
|
||||||
years = dt_new.year - 2000
|
years = dt_new.year - 2000
|
||||||
months = dt_new.month
|
months = dt_new.month
|
||||||
days = dt_new.day
|
days = dt_new.day
|
||||||
|
|
@ -1380,8 +1418,3 @@ class AGB_GPIO:
|
||||||
[ self.GPIO_REG_DAT, 1 ],
|
[ self.GPIO_REG_DAT, 1 ],
|
||||||
[ self.GPIO_REG_RE, 0 ], # Disable RTC Mapping
|
[ self.GPIO_REG_RE, 0 ], # Disable RTC Mapping
|
||||||
])
|
])
|
||||||
#[0F] 00 01 27 05 08 24 08 51 4B 7C 60 00 00 00 00
|
|
||||||
#[0F] 00 01 27 05 08 25 03 88 4B 7C 60 00 00 00 00
|
|
||||||
#[0F] 00 01 01 05 08 00 15 96 4B 7C 60 00 00 00 00
|
|
||||||
#[0F] 00 01 01 05 08 00 39 BF 4B 7C 60 00 00 00 00
|
|
||||||
#[0F] 00 01 01 05 08 00 46 BB 4B 7C 60 00 00 00 00
|
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ class PocketCamera:
|
||||||
pic = pic.resize((pic.width * scale, pic.height * scale), Image.NEAREST)
|
pic = pic.resize((pic.width * scale, pic.height * scale), Image.NEAREST)
|
||||||
|
|
||||||
ext = os.path.splitext(path)[1]
|
ext = os.path.splitext(path)[1]
|
||||||
if ext.lower() == ".png":
|
if ext == "" or ext.lower() == ".png":
|
||||||
outpic = pic
|
outpic = pic
|
||||||
outpic.save(path, pnginfo=pnginfo)
|
outpic.save(path, pnginfo=pnginfo)
|
||||||
elif ext.lower() == ".gif":
|
elif ext.lower() == ".gif":
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
# Author: Lesserkuma (github.com/lesserkuma)
|
# Author: Lesserkuma (github.com/lesserkuma)
|
||||||
|
|
||||||
import hashlib, re, zlib, string
|
import hashlib, re, zlib, string
|
||||||
|
from .Util import dprint
|
||||||
|
|
||||||
class RomFileAGB:
|
class RomFileAGB:
|
||||||
ROMFILE_PATH = None
|
ROMFILE_PATH = None
|
||||||
|
|
@ -40,10 +41,17 @@ class RomFileAGB:
|
||||||
return self.ROMFILE[0:0x200]
|
return self.ROMFILE[0:0x200]
|
||||||
|
|
||||||
def GetHeader(self):
|
def GetHeader(self):
|
||||||
buffer = self.ROMFILE
|
buffer = bytearray(self.ROMFILE)
|
||||||
data = {}
|
data = {}
|
||||||
data["empty"] = (buffer[0x04:0xA0] == bytearray([buffer[0x04]] * 0x9C))
|
hash = hashlib.sha1(buffer[0:0x180]).digest()
|
||||||
data["empty_nocart"] = (buffer == bytearray([0x00] * len(buffer)))
|
nocart_hashes = []
|
||||||
|
nocart_hashes.append(bytearray([ 0x4F, 0xE9, 0x3E, 0xEE, 0xBC, 0x55, 0x93, 0xFE, 0x2E, 0x23, 0x1A, 0x39, 0x86, 0xCE, 0x86, 0xC9, 0x5C, 0x11, 0x00, 0xDD ])) # Method 0
|
||||||
|
nocart_hashes.append(bytearray([ 0xA5, 0x03, 0xA1, 0xB5, 0xF5, 0xDD, 0xBE, 0xFC, 0x87, 0xC7, 0x9B, 0x13, 0x59, 0xF7, 0xE1, 0xA5, 0xCF, 0xE0, 0xAC, 0x9F ])) # Method 1
|
||||||
|
nocart_hashes.append(bytearray([ 0x46, 0x86, 0xE3, 0x81, 0xB2, 0x4A, 0x2D, 0xB0, 0x7D, 0xE8, 0x3D, 0x45, 0x2F, 0xA3, 0x1E, 0x8A, 0x04, 0x4B, 0x3A, 0x50 ])) # Method 2
|
||||||
|
data["empty_nocart"] = hash in nocart_hashes
|
||||||
|
data["empty"] = (buffer[0x04:0xA0] == bytearray([buffer[0x04]] * 0x9C)) or data["empty_nocart"]
|
||||||
|
dprint("Hash: 0x{:s} -- Is Empty?".format((' '.join(format(x, '02X') for x in hash).replace(" ", ", 0x"))), data["empty_nocart"])
|
||||||
|
if data["empty_nocart"]: buffer = bytearray([0x00] * 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 ])
|
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 = bytearray(buffer[0xA0:0xAC]).decode("ascii", "replace")
|
||||||
game_title = re.sub(r"(\x00+)$", "", game_title)
|
game_title = re.sub(r"(\x00+)$", "", game_title)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# FlashGBX
|
# FlashGBX
|
||||||
# Author: Lesserkuma (github.com/lesserkuma)
|
# Author: Lesserkuma (github.com/lesserkuma)
|
||||||
|
|
||||||
import hashlib, re, string
|
import hashlib, re, string, struct
|
||||||
from . import Util
|
from . import Util
|
||||||
|
|
||||||
class RomFileDMG:
|
class RomFileDMG:
|
||||||
|
|
@ -55,7 +55,6 @@ class RomFileDMG:
|
||||||
buffer = self.ROMFILE
|
buffer = self.ROMFILE
|
||||||
data = {}
|
data = {}
|
||||||
if len(buffer) < 0x180: return {}
|
if len(buffer) < 0x180: return {}
|
||||||
|
|
||||||
data["empty"] = (buffer[0x104:0x134] == bytearray([buffer[0x104]] * 0x30))
|
data["empty"] = (buffer[0x104:0x134] == bytearray([buffer[0x104]] * 0x30))
|
||||||
data["empty_nocart"] = (buffer == bytearray([0x00] * len(buffer)))
|
data["empty_nocart"] = (buffer == bytearray([0x00] * 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 ])
|
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 ])
|
||||||
|
|
@ -76,13 +75,13 @@ class RomFileDMG:
|
||||||
maker_code = bytearray(buffer[0x144:0x146]).decode("ascii", "replace")
|
maker_code = bytearray(buffer[0x144:0x146]).decode("ascii", "replace")
|
||||||
maker_code = ''.join(filter(lambda x: x in set(string.printable), maker_code))
|
maker_code = ''.join(filter(lambda x: x in set(string.printable), maker_code))
|
||||||
data["maker_code_new"] = maker_code
|
data["maker_code_new"] = maker_code
|
||||||
data["features_raw"] = int(buffer[0x147])
|
data["mapper_raw"] = int(buffer[0x147])
|
||||||
data["features"] = "?"
|
data["mapper"] = "?"
|
||||||
data["rom_size_raw"] = int(buffer[0x148])
|
data["rom_size_raw"] = int(buffer[0x148])
|
||||||
data["rom_size"] = "?"
|
data["rom_size"] = "?"
|
||||||
if buffer[0x148] < len(Util.DMG_Header_ROM_Sizes): data["rom_size"] = Util.DMG_Header_ROM_Sizes[buffer[0x148]]
|
if buffer[0x148] < len(Util.DMG_Header_ROM_Sizes): data["rom_size"] = Util.DMG_Header_ROM_Sizes[buffer[0x148]]
|
||||||
data["ram_size_raw"] = int(buffer[0x149])
|
data["ram_size_raw"] = int(buffer[0x149])
|
||||||
if data["features_raw"] == 0x05 or data["features_raw"] == 0x06:
|
if data["mapper_raw"] == 0x05 or data["mapper_raw"] == 0x06:
|
||||||
data["ram_size"] = 0x200
|
data["ram_size"] = 0x200
|
||||||
else:
|
else:
|
||||||
data["ram_size"] = "?"
|
data["ram_size"] = "?"
|
||||||
|
|
@ -97,61 +96,191 @@ class RomFileDMG:
|
||||||
data["rom_checksum_correct"] = data["rom_checksum"] == data["rom_checksum_calc"]
|
data["rom_checksum_correct"] = data["rom_checksum"] == data["rom_checksum_calc"]
|
||||||
|
|
||||||
# MBC1M
|
# MBC1M
|
||||||
if data["features_raw"] == 0x03 and data["game_title"] == "MOMOCOL" and data["header_checksum"] == 0x28 or \
|
if data["mapper_raw"] == 0x03 and data["game_title"] == "MOMOCOL" and data["header_checksum"] == 0x28 or \
|
||||||
data["features_raw"] == 0x01 and data["game_title"] == "BOMCOL" and data["header_checksum"] == 0x86 or \
|
data["mapper_raw"] == 0x01 and data["game_title"] == "BOMCOL" and data["header_checksum"] == 0x86 or \
|
||||||
data["features_raw"] == 0x01 and data["game_title"] == "GENCOL" and data["header_checksum"] == 0x8A or \
|
data["mapper_raw"] == 0x01 and data["game_title"] == "GENCOL" and data["header_checksum"] == 0x8A or \
|
||||||
data["features_raw"] == 0x01 and data["game_title"] == "SUPERCHINESE 123" and data["header_checksum"] == 0xE4 or \
|
data["mapper_raw"] == 0x01 and data["game_title"] == "SUPERCHINESE 123" and data["header_checksum"] == 0xE4 or \
|
||||||
data["features_raw"] == 0x01 and data["game_title"] == "MORTALKOMBATI&II" and data["header_checksum"] == 0xB9 or \
|
data["mapper_raw"] == 0x01 and data["game_title"] == "MORTALKOMBATI&II" and data["header_checksum"] == 0xB9 or \
|
||||||
data["features_raw"] == 0x01 and data["game_title"] == "MORTALKOMBAT DUO" and data["header_checksum"] == 0xA7:
|
data["mapper_raw"] == 0x01 and data["game_title"] == "MORTALKOMBAT DUO" and data["header_checksum"] == 0xA7:
|
||||||
data["features_raw"] += 0x100
|
data["mapper_raw"] += 0x100
|
||||||
|
|
||||||
# GB Memory
|
# GB Memory
|
||||||
if data["features_raw"] == 0x19 and data["game_title"] == "NP M-MENU MENU" and data["header_checksum"] == 0xD3:
|
if data["mapper_raw"] == 0x19 and data["game_title"] == "NP M-MENU MENU" and data["header_checksum"] == 0xD3:
|
||||||
data["features_raw"] = 0x105
|
|
||||||
data["ram_size_raw"] = 0x04
|
data["ram_size_raw"] = 0x04
|
||||||
elif data["features_raw"] == 0x01 and data["game_title"] == "DMG MULTI MENU " and data["header_checksum"] == 0x36:
|
data["mapper_raw"] = 0x105
|
||||||
data["features_raw"] = 0x105
|
elif data["mapper_raw"] == 0x01 and data["game_title"] == "DMG MULTI MENU " and data["header_checksum"] == 0x36:
|
||||||
data["ram_size_raw"] = 0x04
|
data["ram_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x105
|
||||||
|
|
||||||
# M161 (Mani 4 in 1)
|
# M161 (Mani 4 in 1)
|
||||||
elif data["features_raw"] == 0x10 and data["game_title"] == "TETRIS SET" and data["header_checksum"] == 0x3F:
|
elif data["mapper_raw"] == 0x10 and data["game_title"] == "TETRIS SET" and data["header_checksum"] == 0x3F:
|
||||||
data["features_raw"] = 0x104
|
data["mapper_raw"] = 0x104
|
||||||
|
|
||||||
# MMM01 (Mani 4 in 1)
|
# MMM01 (Mani 4 in 1)
|
||||||
elif data["features_raw"] == 0x11 and data["game_title"] == "BOUKENJIMA2 SET" and data["header_checksum"] == 0 or \
|
elif data["mapper_raw"] == 0x11 and data["game_title"] == "BOUKENJIMA2 SET" and data["header_checksum"] == 0 or \
|
||||||
data["features_raw"] == 0x11 and data["game_title"] == "BUBBLEBOBBLE SET" and data["header_checksum"] == 0xC6 or \
|
data["mapper_raw"] == 0x11 and data["game_title"] == "BUBBLEBOBBLE SET" and data["header_checksum"] == 0xC6 or \
|
||||||
data["features_raw"] == 0x11 and data["game_title"] == "GANBARUGA SET" and data["header_checksum"] == 0x90 or \
|
data["mapper_raw"] == 0x11 and data["game_title"] == "GANBARUGA SET" and data["header_checksum"] == 0x90 or \
|
||||||
data["features_raw"] == 0x11 and data["game_title"] == "RTYPE 2 SET" and data["header_checksum"] == 0x32:
|
data["mapper_raw"] == 0x11 and data["game_title"] == "RTYPE 2 SET" and data["header_checksum"] == 0x32:
|
||||||
data["features_raw"] = 0x0B
|
data["mapper_raw"] = 0x0B
|
||||||
|
|
||||||
# Unlicensed 256M Mapper
|
# Unlicensed 256M Mapper
|
||||||
elif (data["game_title"].upper() == "GB HICOL" and data["header_checksum"] in (0x4A, 0x49, 0xE9)) or \
|
elif (data["game_title"].upper() == "GB HICOL" and data["header_checksum"] in (0x4A, 0x49, 0xE9)) or \
|
||||||
(data["game_title"] == "BennVenn" and data["header_checksum"] == 0x48):
|
(data["game_title"] == "BennVenn" and data["header_checksum"] == 0x48):
|
||||||
data["features_raw"] = 0x201
|
|
||||||
data["rom_size_raw"] = 0x0A
|
data["rom_size_raw"] = 0x0A
|
||||||
data["ram_size_raw"] = 0x201
|
data["ram_size_raw"] = 0x201
|
||||||
|
data["mapper_raw"] = 0x201
|
||||||
elif buffer[0x150:0x160].decode("ascii", "replace") == "256M ROM Builder":
|
elif buffer[0x150:0x160].decode("ascii", "replace") == "256M ROM Builder":
|
||||||
data["features_raw"] = 0x201
|
|
||||||
data["ram_size_raw"] = 0x201
|
data["ram_size_raw"] = 0x201
|
||||||
|
data["mapper_raw"] = 0x201
|
||||||
|
|
||||||
# Unlicensed Wisdom Tree Mapper
|
# Unlicensed Wisdom Tree Mapper
|
||||||
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xF5, 0xD2, 0x91, 0x7D, 0x5E, 0x5B, 0xAB, 0xD8, 0x5F, 0x0A, 0xC7, 0xBA, 0x56, 0xEB, 0x49, 0x8A, 0xBA, 0x12, 0x49, 0x13 ]): # Exodus / Joshua
|
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xF5, 0xD2, 0x91, 0x7D, 0x5E, 0x5B, 0xAB, 0xD8, 0x5F, 0x0A, 0xC7, 0xBA, 0x56, 0xEB, 0x49, 0x8A, 0xBA, 0x12, 0x49, 0x13 ]): # Exodus / Joshua
|
||||||
data["features_raw"] = 0x202
|
|
||||||
data["rom_size_raw"] = 0x02
|
data["rom_size_raw"] = 0x02
|
||||||
|
data["mapper_raw"] = 0x202
|
||||||
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xE9, 0xF8, 0x32, 0x78, 0x39, 0x19, 0xE3, 0xB2, 0xFC, 0x6F, 0xC2, 0x60, 0x30, 0x33, 0x20, 0xD0, 0x3B, 0x1A, 0xA9, 0xA2 ]): # Spiritual Warfare
|
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xE9, 0xF8, 0x32, 0x78, 0x39, 0x19, 0xE3, 0xB2, 0xFC, 0x6F, 0xC2, 0x60, 0x30, 0x33, 0x20, 0xD0, 0x3B, 0x1A, 0xA9, 0xA2 ]): # Spiritual Warfare
|
||||||
data["features_raw"] = 0x202
|
|
||||||
data["rom_size_raw"] = 0x03
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x202
|
||||||
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xE6, 0xC0, 0x39, 0x7F, 0xA5, 0x99, 0xD6, 0x60, 0xD7, 0x90, 0x45, 0xB9, 0xF0, 0x64, 0x3B, 0x2A, 0x41, 0xA4, 0xD6, 0x35 ]): # King James Bible
|
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0xE6, 0xC0, 0x39, 0x7F, 0xA5, 0x99, 0xD6, 0x60, 0xD7, 0x90, 0x45, 0xB9, 0xF0, 0x64, 0x3B, 0x2A, 0x41, 0xA4, 0xD6, 0x35 ]): # King James Bible
|
||||||
data["features_raw"] = 0x202
|
|
||||||
data["rom_size_raw"] = 0x05
|
data["rom_size_raw"] = 0x05
|
||||||
|
data["mapper_raw"] = 0x202
|
||||||
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0x36, 0x89, 0x60, 0xDD, 0x1B, 0xE1, 0x73, 0x86, 0x8B, 0x24, 0xA3, 0xDC, 0x57, 0xA5, 0xCB, 0x7C, 0xCA, 0x62, 0xDD, 0x34 ]): # NIV Bible
|
elif hashlib.sha1(buffer[0x0:0x150]).digest() == bytearray([ 0x36, 0x89, 0x60, 0xDD, 0x1B, 0xE1, 0x73, 0x86, 0x8B, 0x24, 0xA3, 0xDC, 0x57, 0xA5, 0xCB, 0x7C, 0xCA, 0x62, 0xDD, 0x34 ]): # NIV Bible
|
||||||
data["features_raw"] = 0x202
|
|
||||||
data["rom_size_raw"] = 0x06
|
data["rom_size_raw"] = 0x06
|
||||||
|
data["mapper_raw"] = 0x202
|
||||||
|
|
||||||
|
# Unlicensed Xploder GB Mapper
|
||||||
|
elif hashlib.sha1(buffer[0x104:0x150]).digest() == bytearray([ 0x06, 0xAC, 0xDC, 0xB6, 0xD1, 0x9B, 0xD9, 0xE3, 0x95, 0xA2, 0x38, 0xB8, 0x00, 0x97, 0x0D, 0x78, 0x3F, 0xC6, 0xB7, 0xBD ]):
|
||||||
|
data["rom_size_raw"] = 0x02
|
||||||
|
data["ram_size_raw"] = 0x203
|
||||||
|
data["mapper_raw"] = 0x203
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
try:
|
||||||
|
game_title = bytearray(buffer[0:0x10]).decode("ascii", "replace").replace("\xFF", "")
|
||||||
|
game_title = re.sub(r"(\x00+)$", "", game_title)
|
||||||
|
game_title = re.sub(r"((_)_+|(\x00)\x00+|(\s)\s+)", "\\2\\3\\4", game_title).replace("\x00", "")
|
||||||
|
game_title = ''.join(filter(lambda x: x in set(string.printable), game_title))
|
||||||
|
data["game_title"] = game_title
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
data["version"] = "{:d}.{:d}.{:d}:{:c} ({:02d}:{:02d} {:02d}-{:02d}-{:02d} / {:04X})".format(buffer[0xD8], buffer[0xD9], buffer[0xDA], buffer[0xD7], buffer[0xD0], buffer[0xD1], buffer[0xD2], buffer[0xD3], buffer[0xD4], struct.unpack("<H", buffer[0xD5:0xD7])[0]).replace("\x00", "")
|
||||||
|
|
||||||
|
# Unlicensed Sachen MMC1/MMC2
|
||||||
|
elif len(buffer) >= 0x280:
|
||||||
|
sachen_hash = hashlib.sha1(buffer[0x200:0x280]).digest()
|
||||||
|
if sachen_hash == bytearray([ 0x73, 0x9B, 0x46, 0x86, 0x97, 0x1C, 0x0B, 0xAE, 0xAF, 0x26, 0xF1, 0x73, 0xAC, 0xAE, 0x4B, 0x2B, 0xBF, 0x00, 0x70, 0x77 ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x8793
|
||||||
|
data["game_title"] = "SACHEN 4B-001"
|
||||||
|
elif sachen_hash == bytearray([ 0x22, 0x8B, 0x40, 0x4F, 0x1C, 0xCF, 0x4D, 0xDC, 0x4D, 0xF2, 0x35, 0xF3, 0x7B, 0x6D, 0x61, 0x5E, 0xBE, 0xF1, 0xEF, 0x42 ]):
|
||||||
|
data["rom_size_raw"] = 0x01
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0xB180
|
||||||
|
data["game_title"] = "SACHEN 4B-002"
|
||||||
|
elif sachen_hash == bytearray([ 0xF9, 0xB8, 0x6A, 0x8F, 0x2E, 0x8B, 0x31, 0xD4, 0xC5, 0x02, 0xC8, 0x80, 0x75, 0x35, 0x9C, 0x02, 0xB3, 0xB5, 0x68, 0x01 ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x7CFD
|
||||||
|
data["game_title"] = "SACHEN 4B-004"
|
||||||
|
elif sachen_hash == bytearray([ 0xD6, 0xB5, 0x33, 0x81, 0x1A, 0x01, 0x0D, 0x4D, 0x1C, 0xCC, 0x5A, 0x2C, 0x34, 0x9D, 0x0F, 0x63, 0xD3, 0xF4, 0x9D, 0x34 ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x80DF
|
||||||
|
data["game_title"] = "SACHEN 4B-005"
|
||||||
|
elif sachen_hash == bytearray([ 0x3E, 0x56, 0xCC, 0x2D, 0xDF, 0xE0, 0x00, 0xED, 0x53, 0xA7, 0x9D, 0x62, 0xC8, 0xBF, 0x7F, 0x20, 0x27, 0x47, 0xCD, 0x8E ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x55E6
|
||||||
|
data["game_title"] = "SACHEN 4B-006"
|
||||||
|
elif sachen_hash == bytearray([ 0xD8, 0x24, 0xD2, 0xB2, 0x71, 0x6B, 0x08, 0xFA, 0xEA, 0xA4, 0xFB, 0xD9, 0x7D, 0x81, 0x94, 0x57, 0x46, 0x77, 0x91, 0x60 ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x8E9F
|
||||||
|
data["game_title"] = "SACHEN 4B-007"
|
||||||
|
elif sachen_hash == bytearray([ 0x19, 0x3E, 0xF8, 0xE2, 0x12, 0x8A, 0x24, 0x10, 0xFE, 0xE9, 0xEA, 0x27, 0xC9, 0x1B, 0xC4, 0xDD, 0x04, 0x74, 0x1B, 0xA8 ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x99C7
|
||||||
|
data["game_title"] = "SACHEN 4B-008"
|
||||||
|
elif sachen_hash == bytearray([ 0xA5, 0x07, 0xCB, 0xB0, 0x63, 0x7A, 0xE7, 0x1A, 0xF2, 0xC8, 0x32, 0x9B, 0xA6, 0x6D, 0xC4, 0x21, 0x68, 0x78, 0xE5, 0x39 ]):
|
||||||
|
data["rom_size_raw"] = 0x03
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0xCDD3
|
||||||
|
data["game_title"] = "SACHEN 4B-009"
|
||||||
|
elif sachen_hash == bytearray([ 0x18, 0xEC, 0x2B, 0x15, 0x97, 0xD7, 0x80, 0x51, 0x58, 0xB2, 0xB8, 0x53, 0xA7, 0x00, 0xD7, 0x0B, 0xCE, 0x0A, 0xB3, 0xFF ]):
|
||||||
|
data["rom_size_raw"] = 0x01
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x3889
|
||||||
|
data["game_title"] = "SACHEN 4B-003"
|
||||||
|
elif sachen_hash == bytearray([ 0x96, 0x1C, 0xE3, 0x5D, 0x3A, 0x81, 0x44, 0x95, 0xCF, 0x42, 0x92, 0x42, 0x30, 0x83, 0x14, 0x17, 0xA9, 0xBF, 0xE0, 0x9F ]):
|
||||||
|
data["rom_size_raw"] = 0x06
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x3934
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 31B-001"
|
||||||
|
elif sachen_hash == bytearray([ 0xB8, 0x59, 0x61, 0x1C, 0x03, 0xAF, 0x5F, 0x7F, 0x50, 0x3E, 0x8C, 0xB0, 0x9C, 0x81, 0x4A, 0x0C, 0xE8, 0xBA, 0xB5, 0x99 ]):
|
||||||
|
data["rom_size_raw"] = 0x06
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x2E45
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 31B-001"
|
||||||
|
elif sachen_hash == bytearray([ 0xF5, 0xC6, 0xC2, 0xE6, 0xA6, 0xF2, 0xEE, 0x86, 0x29, 0x22, 0x3D, 0x7C, 0x72, 0xF9, 0xDD, 0x6F, 0x32, 0x0A, 0xA0, 0x9D ]):
|
||||||
|
data["rom_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x125B
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 8B-001"
|
||||||
|
elif sachen_hash == bytearray([ 0xF2, 0x8A, 0xDF, 0x84, 0xBA, 0x56, 0x8C, 0x54, 0xF9, 0x4B, 0x25, 0xFA, 0x12, 0x92, 0x4E, 0xD6, 0x7D, 0xD1, 0x7E, 0x9D ]):
|
||||||
|
data["rom_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x598F
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 8B-002"
|
||||||
|
elif sachen_hash == bytearray([ 0x1C, 0x08, 0x6F, 0x94, 0xD8, 0xFD, 0x40, 0x4D, 0xA3, 0x85, 0xCE, 0x57, 0x35, 0xF3, 0x43, 0x92, 0xEE, 0xB7, 0x26, 0xE1 ]):
|
||||||
|
data["rom_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x6485
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 8B-003"
|
||||||
|
elif sachen_hash == bytearray([ 0x2C, 0xFD, 0xE1, 0x8D, 0x2C, 0x57, 0xBA, 0xDB, 0xC0, 0xF8, 0xDF, 0x52, 0x79, 0x38, 0x44, 0x56, 0x3B, 0xB0, 0xA0, 0xDE ]):
|
||||||
|
data["rom_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x02A4
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 8B-004"
|
||||||
|
elif sachen_hash == bytearray([ 0x4E, 0xEA, 0x3C, 0x0A, 0x23, 0x5C, 0xF9, 0x2D, 0xC6, 0x22, 0xC2, 0x21, 0xD3, 0xBB, 0x73, 0x3B, 0xA7, 0x21, 0xFB, 0x78 ]):
|
||||||
|
data["rom_size_raw"] = 0x02
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0xA709
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN"
|
||||||
|
elif sachen_hash == bytearray([ 0x3F, 0xE9, 0xB7, 0xAB, 0xBC, 0x18, 0x95, 0x60, 0x80, 0xF7, 0xDF, 0x9B, 0x5E, 0x5A, 0x0C, 0x9F, 0x18, 0x63, 0x34, 0x7B ]):
|
||||||
|
data["rom_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x929D
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 1B-003"
|
||||||
|
elif sachen_hash == bytearray([ 0xD8, 0x9B, 0x0D, 0x55, 0x48, 0x97, 0x7F, 0xD5, 0x0E, 0x46, 0x20, 0xD6, 0x9E, 0x0B, 0x8C, 0x6B, 0x05, 0xD4, 0x8F, 0x2C ]):
|
||||||
|
data["rom_size_raw"] = 0x04
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x2F50
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN 4B-003"
|
||||||
|
elif sachen_hash == bytearray([ 0x8B, 0x98, 0xB1, 0xD3, 0x6B, 0x84, 0x66, 0x51, 0xC0, 0x23, 0x19, 0xF2, 0xDC, 0xD3, 0xF4, 0x97, 0xDB, 0x39, 0x47, 0xE7 ]):
|
||||||
|
data["rom_size_raw"] = 0x06
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x9769
|
||||||
|
data["cgb"] = 0x80
|
||||||
|
data["game_title"] = "SACHEN"
|
||||||
|
elif sachen_hash == bytearray([ 0xD0, 0xAE, 0xC9, 0xFB, 0xF0, 0x8D, 0x7A, 0x72, 0x34, 0x8E, 0x96, 0xB6, 0x75, 0x6B, 0x30, 0xC1, 0xCB, 0xF6, 0x2F, 0x00 ]):
|
||||||
|
data["rom_size_raw"] = 0x06
|
||||||
|
data["mapper_raw"] = 0x204
|
||||||
|
data["rom_checksum"] = 0x0346
|
||||||
|
data["game_title"] = "SACHEN 6B-001"
|
||||||
|
|
||||||
if data["features_raw"] in Util.DMG_Header_Mapper:
|
|
||||||
data["features"] = Util.DMG_Header_Mapper[data["features_raw"]]
|
if data["mapper_raw"] in Util.DMG_Header_Mapper:
|
||||||
|
data["mapper"] = Util.DMG_Header_Mapper[data["mapper_raw"]]
|
||||||
elif data["logo_correct"]:
|
elif data["logo_correct"]:
|
||||||
print("{:s}WARNING: Unknown memory bank controller type 0x{:02X}{:s}".format(Util.ANSI.YELLOW, data["features_raw"], Util.ANSI.RESET))
|
print("{:s}WARNING: Unknown memory bank controller type 0x{:02X}{:s}".format(Util.ANSI.YELLOW, data["mapper_raw"], Util.ANSI.RESET))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
# FlashGBX
|
# FlashGBX
|
||||||
# Author: Lesserkuma (github.com/lesserkuma)
|
# Author: Lesserkuma (github.com/lesserkuma)
|
||||||
|
|
||||||
import math, time, datetime, copy, configparser, threading, statistics, os, platform, traceback
|
import math, time, datetime, copy, configparser, threading, statistics, os, platform, traceback, io, struct
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
# Common constants
|
# Common constants
|
||||||
APPNAME = "FlashGBX"
|
APPNAME = "FlashGBX"
|
||||||
VERSION_PEP440 = "3.7"
|
VERSION_PEP440 = "3.8"
|
||||||
VERSION = "v{:s}".format(VERSION_PEP440)
|
VERSION = "v{:s}".format(VERSION_PEP440)
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
|
|
@ -19,13 +19,13 @@ AGB_Global_CRC32 = 0
|
||||||
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS" }
|
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS" }
|
||||||
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000 ]
|
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000 ]
|
||||||
|
|
||||||
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper' }
|
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper' }
|
||||||
DMG_Header_ROM_Sizes = [ "32 KB", "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", "8 MB", "16 MB", "32 MB" ]
|
DMG_Header_ROM_Sizes = [ "32 KB", "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", "8 MB", "16 MB", "32 MB" ]
|
||||||
DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A ]
|
DMG_Header_ROM_Sizes_Map = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A ]
|
||||||
DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000 ]
|
DMG_Header_ROM_Sizes_Flasher_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000 ]
|
||||||
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "16K SRAM (2 KB)", "64K SRAM (8 KB)", "256K SRAM (32 KB)", "512K SRAM (64 KB)", "1M SRAM (128 KB)", "MBC6 SRAM+FLASH (1.03 MB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KB)" ]
|
DMG_Header_RAM_Sizes = [ "None", "4K SRAM (512 Bytes)", "16K SRAM (2 KB)", "64K SRAM (8 KB)", "256K SRAM (32 KB)", "512K SRAM (64 KB)", "1M SRAM (128 KB)", "MBC6 SRAM+FLASH (1.03 MB)", "MBC7 2K EEPROM (256 Bytes)", "MBC7 4K EEPROM (512 Bytes)", "TAMA5 EEPROM (32 Bytes)", "Unlicensed 4M SRAM (512 KB)", "Unlicensed 1M EEPROM (128 KB)" ]
|
||||||
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201 ]
|
DMG_Header_RAM_Sizes_Map = [ 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x04, 0x104, 0x101, 0x102, 0x103, 0x201, 0x203 ]
|
||||||
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x800, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000 ] # RAM size in bytes
|
DMG_Header_RAM_Sizes_Flasher_Map = [ 0, 0x200, 0x800, 0x2000, 0x8000, 0x10000, 0x20000, 0x108000, 0x100, 0x200, 0x20, 0x80000, 0x20000 ] # RAM size in bytes
|
||||||
DMG_Header_SGB = { 0x00:'No support', 0x03:'Supported' }
|
DMG_Header_SGB = { 0x00:'No support', 0x03:'Supported' }
|
||||||
DMG_Header_CGB = { 0x00:'No support', 0x80:'Supported', 0xC0:'Required' }
|
DMG_Header_CGB = { 0x00:'No support', 0x80:'Supported', 0xC0:'Required' }
|
||||||
|
|
||||||
|
|
@ -147,7 +147,7 @@ class Progress():
|
||||||
self.UPDATER(args)
|
self.UPDATER(args)
|
||||||
self.PROGRESS = {}
|
self.PROGRESS = {}
|
||||||
|
|
||||||
elif args["action"] in ("ERASE", "SECTOR_ERASE", "UNLOCK"):
|
elif args["action"] in ("ERASE", "SECTOR_ERASE", "UNLOCK", "UPDATE_RTC"):
|
||||||
if "time_start" in self.PROGRESS:
|
if "time_start" in self.PROGRESS:
|
||||||
args["time_elapsed"] = now - self.PROGRESS["time_start"]
|
args["time_elapsed"] = now - self.PROGRESS["time_start"]
|
||||||
elif "time_start" in args:
|
elif "time_start" in args:
|
||||||
|
|
@ -247,6 +247,32 @@ class TAMA5_REG(Enum):
|
||||||
MEM_READ_L = 0xC
|
MEM_READ_L = 0xC
|
||||||
MEM_READ_H = 0xD
|
MEM_READ_H = 0xD
|
||||||
|
|
||||||
|
def isx2bin(buffer):
|
||||||
|
data_input = io.BytesIO(buffer)
|
||||||
|
data_output = bytearray(8 * 1024 * 1024)
|
||||||
|
rom_size = 0
|
||||||
|
temp = 32 * 1024
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
type = struct.unpack('B', data_input.read(1))[0]
|
||||||
|
if type == 4:
|
||||||
|
break
|
||||||
|
elif type != 1:
|
||||||
|
print("WARNING: Unhandled ISX record type 0x{:02X} found. Converted ROM may not be working correctly.".format(type))
|
||||||
|
continue
|
||||||
|
bank = struct.unpack('B', data_input.read(1))[0]
|
||||||
|
offset = struct.unpack('<H', data_input.read(2))[0] % 0x4000
|
||||||
|
realoffset = bank * 16 * 1024 + offset
|
||||||
|
size = struct.unpack('<H', data_input.read(2))[0]
|
||||||
|
data_output[realoffset:realoffset+size] = data_input.read(size)
|
||||||
|
rom_size = max(rom_size, realoffset+size)
|
||||||
|
temp = 32 * 1024
|
||||||
|
while temp < rom_size: temp *= 2
|
||||||
|
except:
|
||||||
|
print("ERROR: Couldn’t convert ISX file correctly.")
|
||||||
|
break
|
||||||
|
return data_output[:temp]
|
||||||
|
|
||||||
def roundup(x):
|
def roundup(x):
|
||||||
# https://stackoverflow.com/questions/50405017/
|
# https://stackoverflow.com/questions/50405017/
|
||||||
d = 10 ** 2
|
d = 10 ** 2
|
||||||
|
|
@ -257,23 +283,23 @@ def roundup(x):
|
||||||
|
|
||||||
def formatFileSize(size, asInt=False, roundUp=False):
|
def formatFileSize(size, asInt=False, roundUp=False):
|
||||||
if size == 1:
|
if size == 1:
|
||||||
return "{:d} Byte".format(size)
|
return "{:d} Byte".format(size)
|
||||||
elif size < 1024:
|
elif size < 1024:
|
||||||
return "{:d} Bytes".format(size)
|
return "{:d} Bytes".format(size)
|
||||||
elif size < 1024 * 1024:
|
elif size < 1024 * 1024:
|
||||||
val = size/1024
|
val = size/1024
|
||||||
if roundUp: val = roundup(val)
|
if roundUp: val = roundup(val)
|
||||||
if asInt:
|
if asInt:
|
||||||
return "{:d} KB".format(int(val))
|
return "{:d} KB".format(int(val))
|
||||||
else:
|
else:
|
||||||
return "{:.1f} KB".format(val)
|
return "{:.1f} KB".format(val)
|
||||||
else:
|
else:
|
||||||
val = size/1024/1024
|
val = size/1024/1024
|
||||||
if roundUp: val = roundup(val)
|
if roundUp: val = roundup(val)
|
||||||
if asInt:
|
if asInt:
|
||||||
return "{:d} MB".format(int(val))
|
return "{:d} MB".format(int(val))
|
||||||
else:
|
else:
|
||||||
return "{:.2f} MB".format(val)
|
return "{:.2f} MB".format(val)
|
||||||
|
|
||||||
def formatProgressTimeShort(sec):
|
def formatProgressTimeShort(sec):
|
||||||
sec = sec % (24 * 3600)
|
sec = sec % (24 * 3600)
|
||||||
|
|
@ -285,20 +311,20 @@ def formatProgressTimeShort(sec):
|
||||||
|
|
||||||
def formatProgressTime(sec):
|
def formatProgressTime(sec):
|
||||||
if int(sec) == 1:
|
if int(sec) == 1:
|
||||||
return "{:d} second".format(int(sec))
|
return "{:d} second".format(int(sec))
|
||||||
elif sec < 60:
|
elif sec < 60:
|
||||||
return "{:d} seconds".format(int(sec))
|
return "{:d} seconds".format(int(sec))
|
||||||
elif int(sec) == 60:
|
elif int(sec) == 60:
|
||||||
return "1 minute"
|
return "1 minute"
|
||||||
else:
|
else:
|
||||||
min = int(sec / 60)
|
min = int(sec / 60)
|
||||||
sec = int(sec % 60)
|
sec = int(sec % 60)
|
||||||
s = str(min) + " "
|
s = str(min) + " "
|
||||||
if min == 1:
|
if min == 1:
|
||||||
s = s + "minute"
|
s = s + "minute"
|
||||||
else:
|
else:
|
||||||
s = s + "minutes"
|
s = s + "minutes"
|
||||||
s = s + ", " + str(sec) + " "
|
s = s + ", " + str(sec) + " "
|
||||||
if sec == 1:
|
if sec == 1:
|
||||||
s = s + "second"
|
s = s + "second"
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"Generic Flash Cartridge (0/90)"
|
"Generic Flash Cartridge (0/90)"
|
||||||
],
|
],
|
||||||
"voltage":3.3,
|
"voltage":3.3,
|
||||||
"chip_erase_timeout":120,
|
"chip_erase_timeout":300,
|
||||||
"sector_size_from_cfi":true,
|
"sector_size_from_cfi":true,
|
||||||
"command_set":"INTEL",
|
"command_set":"INTEL",
|
||||||
"commands":{
|
"commands":{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"Generic Flash Cartridge (AAA/A9)"
|
"Generic Flash Cartridge (AAA/A9)"
|
||||||
],
|
],
|
||||||
"voltage":3.3,
|
"voltage":3.3,
|
||||||
"chip_erase_timeout":120,
|
"chip_erase_timeout":300,
|
||||||
"command_set":"AMD",
|
"command_set":"AMD",
|
||||||
"commands":{
|
"commands":{
|
||||||
"reset":[
|
"reset":[
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"Generic Flash Cartridge (AAA/AA)"
|
"Generic Flash Cartridge (AAA/AA)"
|
||||||
],
|
],
|
||||||
"voltage":3.3,
|
"voltage":3.3,
|
||||||
"chip_erase_timeout":120,
|
"chip_erase_timeout":300,
|
||||||
"command_set":"AMD",
|
"command_set":"AMD",
|
||||||
"commands":{
|
"commands":{
|
||||||
"reset":[
|
"reset":[
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
{
|
{
|
||||||
"type":"AGB",
|
"type":"AGB",
|
||||||
"names":[
|
"names":[
|
||||||
"39VF512 with MSP55LV100G"
|
"AGB-E05-01 with MSP55LV100G",
|
||||||
|
"DV15 with MSP55LV100G"
|
||||||
],
|
],
|
||||||
"flash_ids":[
|
"flash_ids":[
|
||||||
|
[ 0x02, 0x01, 0x02, 0x01, 0x7D, 0x7E, 0x7D, 0x7E ],
|
||||||
[ 0x02, 0x01, 0x02, 0x01, 0x7D, 0x7E, 0x7D, 0x7E ]
|
[ 0x02, 0x01, 0x02, 0x01, 0x7D, 0x7E, 0x7D, 0x7E ]
|
||||||
],
|
],
|
||||||
"voltage":3.3,
|
"voltage":3.3,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
"AGB-E05-02 with M29W128GH",
|
"AGB-E05-02 with M29W128GH",
|
||||||
"AGB-E05-02 with M29W128FH",
|
"AGB-E05-02 with M29W128FH",
|
||||||
"2006_TSOP_64BALL_6106 with W29GL128SH9B",
|
"2006_TSOP_64BALL_6106 with W29GL128SH9B",
|
||||||
"AGB-E05-02 with JS28F128"
|
"AGB-E05-02 with JS28F128",
|
||||||
|
"AGB-E05-03H with M29W128GH"
|
||||||
],
|
],
|
||||||
"flash_ids":[
|
"flash_ids":[
|
||||||
[ 0x02, 0x00, 0x7D, 0x22 ],
|
[ 0x02, 0x00, 0x7D, 0x22 ],
|
||||||
|
|
@ -20,7 +21,8 @@
|
||||||
[ 0x20, 0x00, 0x7D, 0x22 ],
|
[ 0x20, 0x00, 0x7D, 0x22 ],
|
||||||
[ 0x20, 0x00, 0x7D, 0x22 ],
|
[ 0x20, 0x00, 0x7D, 0x22 ],
|
||||||
[ 0xEF, 0x00, 0x7D, 0x22 ],
|
[ 0xEF, 0x00, 0x7D, 0x22 ],
|
||||||
[ 0x8A, 0x00, 0x7D, 0x22 ]
|
[ 0x8A, 0x00, 0x7D, 0x22 ],
|
||||||
|
[ 0x20, 0x00, 0x7D, 0x22 ]
|
||||||
],
|
],
|
||||||
"voltage":3.3,
|
"voltage":3.3,
|
||||||
"flash_size":0x1000000,
|
"flash_size":0x1000000,
|
||||||
|
|
|
||||||
49
FlashGBX/config/fc_DMG_29LV016T.txt
Normal file
49
FlashGBX/config/fc_DMG_29LV016T.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"type":"DMG",
|
||||||
|
"names":[
|
||||||
|
"SD007_T40_64BALL_TSOP28 with 29LV016T"
|
||||||
|
],
|
||||||
|
"flash_ids":[
|
||||||
|
[ 0x04, 0xC7, 0x00, 0x00 ]
|
||||||
|
],
|
||||||
|
"voltage":5,
|
||||||
|
"flash_size":0x200000,
|
||||||
|
"start_addr":0,
|
||||||
|
"first_bank":1,
|
||||||
|
"write_pin":"WR",
|
||||||
|
"chip_erase_timeout":60,
|
||||||
|
"command_set":"AMD",
|
||||||
|
"commands":{
|
||||||
|
"reset":[
|
||||||
|
[ 0, 0xF0 ]
|
||||||
|
],
|
||||||
|
"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 ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,6 @@
|
||||||
[ 0x2AAA, 0x55 ],
|
[ 0x2AAA, 0x55 ],
|
||||||
[ 0x5555, 0x90 ]
|
[ 0x5555, 0x90 ]
|
||||||
],
|
],
|
||||||
"read_cfi":[
|
|
||||||
[ 0x5555, 0x98 ]
|
|
||||||
],
|
|
||||||
"chip_erase":[
|
"chip_erase":[
|
||||||
[ 0x5555, 0xAA ],
|
[ 0x5555, 0xAA ],
|
||||||
[ 0x2AAA, 0x55 ],
|
[ 0x2AAA, 0x55 ],
|
||||||
|
|
|
||||||
55
FlashGBX/config/fc_DMG_XploderGB.txt
Normal file
55
FlashGBX/config/fc_DMG_XploderGB.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"type":"DMG",
|
||||||
|
"names":[
|
||||||
|
"BLAZE Xploder GB"
|
||||||
|
],
|
||||||
|
"flash_ids":[
|
||||||
|
[ 0xBF, 0x10, 0xFF, 0xFF ]
|
||||||
|
],
|
||||||
|
"voltage":5,
|
||||||
|
"flash_size":0x40000,
|
||||||
|
"start_addr":0x4000,
|
||||||
|
"first_bank":1,
|
||||||
|
"flash_commands_on_bank_1":true,
|
||||||
|
"mbc":0x203,
|
||||||
|
"write_pin":"WR",
|
||||||
|
"read_identifier_at":0x4000,
|
||||||
|
"power_cycle":true,
|
||||||
|
"sector_size":0x4000,
|
||||||
|
"command_set":"BLAZE_XPLODER",
|
||||||
|
"commands":{
|
||||||
|
"reset":[
|
||||||
|
[ 0x5555, 0xAA ],
|
||||||
|
[ 0x2AAA, 0x55 ],
|
||||||
|
[ 0x5555, 0xF0 ]
|
||||||
|
],
|
||||||
|
"unlock_read":[
|
||||||
|
[ 0x0102, 1, 1 ]
|
||||||
|
],
|
||||||
|
"unlock":[
|
||||||
|
[ 0x0006, 0x01 ]
|
||||||
|
],
|
||||||
|
"bank_switch":[
|
||||||
|
[ 0x0006, "ID" ]
|
||||||
|
],
|
||||||
|
"read_identifier":[
|
||||||
|
[ 0x5555, 0xAA ],
|
||||||
|
[ 0x2AAA, 0x55 ],
|
||||||
|
[ 0x5555, 0x90 ]
|
||||||
|
],
|
||||||
|
"sector_erase":[],
|
||||||
|
"sector_erase_wait_for":[],
|
||||||
|
"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 ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
{
|
{
|
||||||
"type":"DMG",
|
"type":"DMG",
|
||||||
"names":[
|
"names":[
|
||||||
"insideGadgets 512 KB"
|
"insideGadgets 128/256/512 KB"
|
||||||
],
|
],
|
||||||
"flash_ids":[
|
"flash_ids":[
|
||||||
|
[ 0xBF, 0xB5, 0x01, 0xFF ],
|
||||||
|
[ 0xBF, 0xB6, 0x01, 0xFF ],
|
||||||
[ 0xBF, 0xB7, 0x01, 0xFF ]
|
[ 0xBF, 0xB7, 0x01, 0xFF ]
|
||||||
],
|
],
|
||||||
"voltage":5,
|
"voltage":5,
|
||||||
|
"power_cycle":true,
|
||||||
"flash_size":0x80000,
|
"flash_size":0x80000,
|
||||||
"start_addr":0,
|
"start_addr":0,
|
||||||
"first_bank":1,
|
"first_bank":1,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
APP = None
|
APP = None
|
||||||
DEVICE = None
|
DEVICE = None
|
||||||
PORT = ""
|
PORT = ""
|
||||||
|
FW_FILES = {"v1.1/v1.2":"fw_GBxCart_RW_v1_1_v1_2.zip", "v1.3":"fw_GBxCart_RW_v1_3.zip", "XMAS v1.0":"fw_GBxCart_RW_XMAS_v1_0.zip", "Mini v1.0":"fw_GBxCart_RW_Mini_v1_0.zip"}
|
||||||
|
|
||||||
def __init__(self, app, app_path, file=None, icon=None, device=None):
|
def __init__(self, app, app_path, file=None, icon=None, device=None):
|
||||||
QtWidgets.QDialog.__init__(self)
|
QtWidgets.QDialog.__init__(self)
|
||||||
|
|
@ -18,12 +19,13 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
self.APP = app
|
self.APP = app
|
||||||
self.APP_PATH = app_path
|
self.APP_PATH = app_path
|
||||||
self.DEVICE = device
|
self.DEVICE = device
|
||||||
|
self.PCB_VER = device.GetPCBVersion()
|
||||||
self.PORT = device.GetPort()
|
self.PORT = device.GetPort()
|
||||||
|
|
||||||
self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW v1.3")
|
self.setWindowTitle("FlashGBX – Firmware Updater for GBxCart RW")
|
||||||
self.setWindowFlags((self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint) & ~QtCore.Qt.WindowContextHelpButtonHint)
|
self.setWindowFlags((self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint) & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||||
|
|
||||||
with zipfile.ZipFile(self.APP_PATH + "/res/fw_GBxCart_RW_v1_3.zip") as zip:
|
with zipfile.ZipFile(self.APP_PATH + "/res/{:s}".format(self.FW_FILES[self.PCB_VER])) as zip:
|
||||||
with zip.open("fw.ini") as f: ini_file = f.read()
|
with zip.open("fw.ini") as f: ini_file = f.read()
|
||||||
ini_file = ini_file.decode(encoding="utf-8")
|
ini_file = ini_file.decode(encoding="utf-8")
|
||||||
self.INI = Util.IniSettings(ini=ini_file, main_section="Firmware")
|
self.INI = Util.IniSettings(ini=ini_file, main_section="Firmware")
|
||||||
|
|
@ -75,8 +77,8 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
self.grpAvailableFwUpdates.setMinimumWidth(400)
|
self.grpAvailableFwUpdates.setMinimumWidth(400)
|
||||||
self.grpAvailableFwUpdatesLayout = QtWidgets.QVBoxLayout()
|
self.grpAvailableFwUpdatesLayout = QtWidgets.QVBoxLayout()
|
||||||
self.grpAvailableFwUpdatesLayout.setContentsMargins(-1, 3, -1, -1)
|
self.grpAvailableFwUpdatesLayout.setContentsMargins(-1, 3, -1, -1)
|
||||||
|
|
||||||
self.optCFW = QtWidgets.QRadioButton("{:s}".format(self.CFW_VER))
|
self.optCFW = QtWidgets.QRadioButton("{:s}".format(self.CFW_VER))
|
||||||
self.optCFW.setChecked(True)
|
|
||||||
self.lblCFW_Blerb = QtWidgets.QLabel("{:s}".format(self.CFW_TEXT))
|
self.lblCFW_Blerb = QtWidgets.QLabel("{:s}".format(self.CFW_TEXT))
|
||||||
self.lblCFW_Blerb.setWordWrap(True)
|
self.lblCFW_Blerb.setWordWrap(True)
|
||||||
self.lblCFW_Blerb.mousePressEvent = lambda x: [ self.optCFW.setChecked(True) ]
|
self.lblCFW_Blerb.mousePressEvent = lambda x: [ self.optCFW.setChecked(True) ]
|
||||||
|
|
@ -85,9 +87,6 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
self.lblOFW_Blerb.setWordWrap(True)
|
self.lblOFW_Blerb.setWordWrap(True)
|
||||||
self.lblOFW_Blerb.mousePressEvent = lambda x: [ self.optOFW.setChecked(True) ]
|
self.lblOFW_Blerb.mousePressEvent = lambda x: [ self.optOFW.setChecked(True) ]
|
||||||
self.optExternal = QtWidgets.QRadioButton("External firmware file")
|
self.optExternal = QtWidgets.QRadioButton("External firmware file")
|
||||||
#self.lblExternal_Blerb = QtWidgets.QLabel("<ul><li>Please check <a href=\"https://www.gbxcart.com/\">gbxcart.com</a> for the latest official firmware version</li></ul>")
|
|
||||||
#self.lblExternal_Blerb.setWordWrap(True)
|
|
||||||
#self.lblExternal_Blerb.mousePressEvent = lambda x: [ self.optOFW.setChecked(True) ]
|
|
||||||
|
|
||||||
self.rowUpdate = QtWidgets.QHBoxLayout()
|
self.rowUpdate = QtWidgets.QHBoxLayout()
|
||||||
self.btnUpdate = QtWidgets.QPushButton("Install Firmware Update")
|
self.btnUpdate = QtWidgets.QPushButton("Install Firmware Update")
|
||||||
|
|
@ -97,20 +96,22 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
self.rowUpdate.addStretch()
|
self.rowUpdate.addStretch()
|
||||||
self.rowUpdate.addWidget(self.btnUpdate)
|
self.rowUpdate.addWidget(self.btnUpdate)
|
||||||
self.rowUpdate.addStretch()
|
self.rowUpdate.addStretch()
|
||||||
|
|
||||||
self.grpAvailableFwUpdatesLayout.addWidget(self.optCFW)
|
if self.PCB_VER == "v1.3":
|
||||||
self.grpAvailableFwUpdatesLayout.addWidget(self.lblCFW_Blerb)
|
self.grpAvailableFwUpdatesLayout.addWidget(self.optCFW)
|
||||||
|
self.grpAvailableFwUpdatesLayout.addWidget(self.lblCFW_Blerb)
|
||||||
|
self.optCFW.setChecked(True)
|
||||||
|
else:
|
||||||
|
self.optOFW.setChecked(True)
|
||||||
self.grpAvailableFwUpdatesLayout.addWidget(self.optOFW)
|
self.grpAvailableFwUpdatesLayout.addWidget(self.optOFW)
|
||||||
self.grpAvailableFwUpdatesLayout.addWidget(self.lblOFW_Blerb)
|
self.grpAvailableFwUpdatesLayout.addWidget(self.lblOFW_Blerb)
|
||||||
self.grpAvailableFwUpdatesLayout.addWidget(self.optExternal)
|
self.grpAvailableFwUpdatesLayout.addWidget(self.optExternal)
|
||||||
#self.grpAvailableFwUpdatesLayout.addWidget(self.lblExternal_Blerb)
|
|
||||||
self.grpAvailableFwUpdatesLayout.addSpacing(3)
|
self.grpAvailableFwUpdatesLayout.addSpacing(3)
|
||||||
self.grpAvailableFwUpdatesLayout.addItem(self.rowUpdate)
|
self.grpAvailableFwUpdatesLayout.addItem(self.rowUpdate)
|
||||||
#self.grpAvailableFwUpdatesLayout.addWidget(self.btnUpdate)
|
|
||||||
self.grpAvailableFwUpdates.setLayout(self.grpAvailableFwUpdatesLayout)
|
self.grpAvailableFwUpdates.setLayout(self.grpAvailableFwUpdatesLayout)
|
||||||
self.layout_device.addWidget(self.grpAvailableFwUpdates)
|
self.layout_device.addWidget(self.grpAvailableFwUpdates)
|
||||||
# ↑↑↑ Available Firmware Updates
|
# ↑↑↑ Available Firmware Updates
|
||||||
|
|
||||||
self.grpStatus = QtWidgets.QGroupBox("")
|
self.grpStatus = QtWidgets.QGroupBox("")
|
||||||
self.grpStatusLayout = QtWidgets.QGridLayout()
|
self.grpStatusLayout = QtWidgets.QGridLayout()
|
||||||
self.prgStatus = QtWidgets.QProgressBar()
|
self.prgStatus = QtWidgets.QProgressBar()
|
||||||
|
|
@ -119,7 +120,6 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
self.prgStatus.setValue(0)
|
self.prgStatus.setValue(0)
|
||||||
self.lblStatus = QtWidgets.QLabel("Status: Ready.")
|
self.lblStatus = QtWidgets.QLabel("Status: Ready.")
|
||||||
|
|
||||||
#self.grpStatusLayout.addWidget(self.btnUpdate, 0, 0, QtCore.Qt.AlignCenter)
|
|
||||||
self.grpStatusLayout.addWidget(self.prgStatus, 1, 0)
|
self.grpStatusLayout.addWidget(self.prgStatus, 1, 0)
|
||||||
self.grpStatusLayout.addWidget(self.lblStatus, 2, 0)
|
self.grpStatusLayout.addWidget(self.lblStatus, 2, 0)
|
||||||
|
|
||||||
|
|
@ -200,18 +200,18 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
fn = "ofw.hex"
|
fn = "ofw.hex"
|
||||||
else:
|
else:
|
||||||
path = self.APP.SETTINGS.value("LastDirFirmwareUpdate")
|
path = self.APP.SETTINGS.value("LastDirFirmwareUpdate")
|
||||||
path = QtWidgets.QFileDialog.getOpenFileName(self, "Choose GBxCart RW v1.3 Firmware File", path, "Firmware Update (*.hex);;All Files (*.*)")[0]
|
path = QtWidgets.QFileDialog.getOpenFileName(self, "Choose GBxCart RW Firmware File", path, "Firmware Update (*.hex);;All Files (*.*)")[0]
|
||||||
if path == "": return
|
if path == "": return
|
||||||
temp = re.search(r"^(gbxcart_rw_v1\.3_pcb_r.+\.hex)$", os.path.basename(path))
|
temp = re.search(r"^(gbx(?:cart|mas)_rw_.+_pcb_r.+\.hex)$", os.path.basename(path))
|
||||||
if temp is None:
|
if temp is None:
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The expected filename for a valid firmware file is <b>gbxcart_rw_v1.3_pcb_r**.hex</b>. Please visit <a href=\"https://www.gbxcart.com/\">gbxcart.com</a> for the latest official firmware updates.", standardButtons=QtWidgets.QMessageBox.Ok)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The expected filename for a valid firmware file is <b>gbx*_rw_*_pcb_r*.hex</b>. Please visit <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com</a> for the latest official firmware updates.", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||||
answer = msgbox.exec()
|
answer = msgbox.exec()
|
||||||
return
|
return
|
||||||
self.APP.SETTINGS.setValue("LastDirFirmwareUpdate", os.path.dirname(path))
|
self.APP.SETTINGS.setValue("LastDirFirmwareUpdate", os.path.dirname(path))
|
||||||
fw = "{:s}<br><br><b>Please double check that this is a valid firmware file for the GBxCart RW v1.3. If it is invalid or an update for a different device, it may render your device unusable.</b>".format(path)
|
fw = "{:s}<br><br><b>Please double check that this is a valid firmware file for your GBxCart RW. If it is invalid or an update for a different device, it may render your device unusable.</b>".format(path)
|
||||||
fn = None
|
fn = None
|
||||||
|
|
||||||
text = "The following firmware will now be written to your GBxCart v1.3 device:<br>- {:s}".format(fw)
|
text = "The following firmware will now be written to your GBxCart RW device:<br>- {:s}".format(fw)
|
||||||
text += "<br><br>Do you want to continue?"
|
text += "<br><br>Do you want to continue?"
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
|
msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
|
||||||
|
|
@ -223,7 +223,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
self.grpAvailableFwUpdates.setEnabled(False)
|
self.grpAvailableFwUpdates.setEnabled(False)
|
||||||
|
|
||||||
if path == "":
|
if path == "":
|
||||||
with zipfile.ZipFile(self.APP_PATH + "/res/fw_GBxCart_RW_v1_3.zip") as archive:
|
with zipfile.ZipFile(self.APP_PATH + "/res/{:s}".format(self.FW_FILES[self.PCB_VER])) as archive:
|
||||||
with archive.open(fn) as f: ihex = f.read().decode("ascii")
|
with archive.open(fn) as f: ihex = f.read().decode("ascii")
|
||||||
else:
|
else:
|
||||||
with open(path, "rb") as f: ihex = f.read().decode("ascii")
|
with open(path, "rb") as f: ihex = f.read().decode("ascii")
|
||||||
|
|
@ -298,7 +298,7 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
if self.ResetAVR(delay) is False:
|
if self.ResetAVR(delay) is False:
|
||||||
fncSetStatus(text="Status: Bootloader error.", enableUI=True)
|
fncSetStatus(text="Status: Bootloader error.", enableUI=True)
|
||||||
self.prgStatus.setValue(0)
|
self.prgStatus.setValue(0)
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW v1.3 bootloader is not responding. If it doesn’t work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW bootloader is not responding. If it doesn’t work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||||
answer = msgbox.exec()
|
answer = msgbox.exec()
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
|
@ -326,13 +326,13 @@ class FirmwareUpdaterWindow(QtWidgets.QDialog):
|
||||||
fncSetStatus("Status: Waiting for bootloader... (+{:d}ms)".format(math.ceil(delay * 1000)))
|
fncSetStatus("Status: Waiting for bootloader... (+{:d}ms)".format(math.ceil(delay * 1000)))
|
||||||
if self.ResetAVR(delay) is False:
|
if self.ResetAVR(delay) is False:
|
||||||
fncSetStatus(text="Status: Bootloader error.", enableUI=True)
|
fncSetStatus(text="Status: Bootloader error.", enableUI=True)
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW v1.3 bootloader is not responding. If it doesn’t work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW bootloader is not responding. If it doesn’t work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||||
answer = msgbox.exec()
|
answer = msgbox.exec()
|
||||||
return 2
|
return 2
|
||||||
lives -= 1
|
lives -= 1
|
||||||
if lives < 0:
|
if lives < 0:
|
||||||
fncSetStatus(text="Status: Bootloader timeout.", enableUI=True)
|
fncSetStatus(text="Status: Bootloader timeout.", enableUI=True)
|
||||||
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW v1.3 bootloader is not responding. If it doesn’t work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
|
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text="The firmware update was not successful as the GBxCart RW bootloader is not responding. If it doesn’t work even after multiple retries, please use the official firmware updater instead.", standardButtons=QtWidgets.QMessageBox.Ok)
|
||||||
answer = msgbox.exec()
|
answer = msgbox.exec()
|
||||||
return 2
|
return 2
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from . import Util
|
||||||
class GbxDevice:
|
class GbxDevice:
|
||||||
DEVICE_NAME = "GBxCart RW"
|
DEVICE_NAME = "GBxCart RW"
|
||||||
DEVICE_MIN_FW = 1
|
DEVICE_MIN_FW = 1
|
||||||
DEVICE_MAX_FW = 5
|
DEVICE_MAX_FW = 6
|
||||||
|
|
||||||
DEVICE_CMD = {
|
DEVICE_CMD = {
|
||||||
"NULL":0x30,
|
"NULL":0x30,
|
||||||
|
|
@ -49,6 +49,8 @@ class GbxDevice:
|
||||||
"DMG_MBC7_READ_EEPROM":0xB5,
|
"DMG_MBC7_READ_EEPROM":0xB5,
|
||||||
"DMG_MBC7_WRITE_EEPROM":0xB6,
|
"DMG_MBC7_WRITE_EEPROM":0xB6,
|
||||||
"DMG_MBC6_MMSA_WRITE_FLASH":0xB7,
|
"DMG_MBC6_MMSA_WRITE_FLASH":0xB7,
|
||||||
|
"DMG_SET_BANK_CHANGE_CMD":0xB8,
|
||||||
|
"DMG_EEPROM_WRITE":0xB9,
|
||||||
"AGB_CART_READ":0xC1,
|
"AGB_CART_READ":0xC1,
|
||||||
"AGB_CART_WRITE":0xC2,
|
"AGB_CART_WRITE":0xC2,
|
||||||
"AGB_CART_READ_SRAM":0xC3,
|
"AGB_CART_READ_SRAM":0xC3,
|
||||||
|
|
@ -59,7 +61,7 @@ class GbxDevice:
|
||||||
"AGB_CART_READ_3D_MEMORY":0xC8,
|
"AGB_CART_READ_3D_MEMORY":0xC8,
|
||||||
"AGB_BOOTUP_SEQUENCE":0xC9,
|
"AGB_BOOTUP_SEQUENCE":0xC9,
|
||||||
"DMG_FLASH_WRITE_BYTE":0xD1,
|
"DMG_FLASH_WRITE_BYTE":0xD1,
|
||||||
"AGB_FLASH_WRITE_BYTE":0xD2,
|
"AGB_FLASH_WRITE_SHORT":0xD2,
|
||||||
"FLASH_PROGRAM":0xD3,
|
"FLASH_PROGRAM":0xD3,
|
||||||
"CART_WRITE_FLASH_CMD":0xD4,
|
"CART_WRITE_FLASH_CMD":0xD4,
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +82,8 @@ class GbxDevice:
|
||||||
"DMG_READ_CS_PULSE":[8, 0x08],
|
"DMG_READ_CS_PULSE":[8, 0x08],
|
||||||
"DMG_WRITE_CS_PULSE":[8, 0x09],
|
"DMG_WRITE_CS_PULSE":[8, 0x09],
|
||||||
"FLASH_DOUBLE_DIE":[8, 0x0A],
|
"FLASH_DOUBLE_DIE":[8, 0x0A],
|
||||||
|
"DMG_READ_METHOD":[8, 0x0B],
|
||||||
|
"AGB_READ_METHOD":[8, 0x0C],
|
||||||
}
|
}
|
||||||
|
|
||||||
PCB_VERSIONS = {4:'v1.3', 5:'v1.4', 6:'v1.4a', 101:'Mini v1.0d'}
|
PCB_VERSIONS = {4:'v1.3', 5:'v1.4', 6:'v1.4a', 101:'Mini v1.0d'}
|
||||||
|
|
@ -88,6 +92,7 @@ class GbxDevice:
|
||||||
|
|
||||||
FW = []
|
FW = []
|
||||||
FW_UPDATE_REQ = False
|
FW_UPDATE_REQ = False
|
||||||
|
FW_VAR = {}
|
||||||
MODE = None
|
MODE = None
|
||||||
PORT = ''
|
PORT = ''
|
||||||
DEVICE = None
|
DEVICE = None
|
||||||
|
|
@ -187,7 +192,6 @@ class GbxDevice:
|
||||||
except SerialException as e:
|
except SerialException as e:
|
||||||
if "Permission" in str(e):
|
if "Permission" in str(e):
|
||||||
conn_msg.append([3, "The GBxCart RW device on port " + ports[i] + " couldn’t be accessed. Make sure your user account has permission to use it and it’s not already in use by another application."])
|
conn_msg.append([3, "The GBxCart RW device on port " + ports[i] + " couldn’t be accessed. Make sure your user account has permission to use it and it’s not already in use by another application."])
|
||||||
print(str(e))
|
|
||||||
else:
|
else:
|
||||||
conn_msg.append([3, "A critical error occured while trying to access the GBxCart RW device on port " + ports[i] + ".\n\n" + str(e)])
|
conn_msg.append([3, "A critical error occured while trying to access the GBxCart RW device on port " + ports[i] + ".\n\n" + str(e)])
|
||||||
continue
|
continue
|
||||||
|
|
@ -247,7 +251,7 @@ class GbxDevice:
|
||||||
|
|
||||||
def IsSupportedMbc(self, mbc):
|
def IsSupportedMbc(self, mbc):
|
||||||
if self.CanPowerCycleCart():
|
if self.CanPowerCycleCart():
|
||||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x06, 0x0B, 0x0D, 0x10, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x201, 0x202 )
|
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x06, 0x0B, 0x0D, 0x10, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x201, 0x202, 0x203, 0x204 )
|
||||||
else:
|
else:
|
||||||
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x06, 0x0B, 0x0D, 0x10, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x202 )
|
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x06, 0x0B, 0x0D, 0x10, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x202 )
|
||||||
|
|
||||||
|
|
@ -277,6 +281,13 @@ class GbxDevice:
|
||||||
self.DEVICE.reset_output_buffer()
|
self.DEVICE.reset_output_buffer()
|
||||||
return self.LoadFirmwareVersion()
|
return self.LoadFirmwareVersion()
|
||||||
except SerialException as e:
|
except SerialException as e:
|
||||||
|
print("Connection lost!")
|
||||||
|
try:
|
||||||
|
if e.args[0].startswith("ClearCommError failed"):
|
||||||
|
self.DEVICE.close()
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
pass
|
||||||
print(str(e))
|
print(str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -321,6 +332,9 @@ class GbxDevice:
|
||||||
else:
|
else:
|
||||||
return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.PORT)
|
return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.PORT)
|
||||||
|
|
||||||
|
def GetOfficialWebsite(self):
|
||||||
|
return "https://www.gbxcart.com/"
|
||||||
|
|
||||||
def SupportsFirmwareUpdates(self):
|
def SupportsFirmwareUpdates(self):
|
||||||
return self.FW["pcb_ver"] in (4, 5, 6)
|
return self.FW["pcb_ver"] in (4, 5, 6)
|
||||||
|
|
||||||
|
|
@ -373,8 +387,8 @@ class GbxDevice:
|
||||||
if not isinstance(data, bytearray):
|
if not isinstance(data, bytearray):
|
||||||
data = bytearray([data])
|
data = bytearray([data])
|
||||||
|
|
||||||
dstr = ' '.join(format(x, '02X') for x in data)
|
#dstr = ' '.join(format(x, '02X') for x in data)
|
||||||
dprint("[{:02X}] {:s}".format(int(len(dstr)/3) + 1, dstr[:96]))
|
#dprint("[{:02X}] {:s}".format(int(len(dstr)/3) + 1, dstr[:96]))
|
||||||
|
|
||||||
self.DEVICE.write(data)
|
self.DEVICE.write(data)
|
||||||
self.DEVICE.flush()
|
self.DEVICE.flush()
|
||||||
|
|
@ -405,6 +419,7 @@ class GbxDevice:
|
||||||
|
|
||||||
def _set_fw_variable(self, key, value):
|
def _set_fw_variable(self, key, value):
|
||||||
dprint("Setting firmware variable {:s} to 0x{:X}".format(key, value))
|
dprint("Setting firmware variable {:s} to 0x{:X}".format(key, value))
|
||||||
|
self.FW_VAR[key] = value
|
||||||
|
|
||||||
size = 0
|
size = 0
|
||||||
for (k, v) in self.DEVICE_VAR.items():
|
for (k, v) in self.DEVICE_VAR.items():
|
||||||
|
|
@ -421,14 +436,20 @@ class GbxDevice:
|
||||||
buffer.extend(struct.pack(">I", key))
|
buffer.extend(struct.pack(">I", key))
|
||||||
buffer.extend(struct.pack(">I", value))
|
buffer.extend(struct.pack(">I", value))
|
||||||
self._write(buffer)
|
self._write(buffer)
|
||||||
|
|
||||||
def _cart_read(self, address, length=0, agb_save_flash=False):
|
def _cart_read(self, address, length=0, agb_save_flash=False):
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
if length == 0:
|
if length == 0:
|
||||||
length = 1
|
length = 1
|
||||||
return struct.unpack("B", self.ReadROM(address, 1))[0]
|
if address < 0xA000:
|
||||||
|
return struct.unpack("B", self.ReadROM(address, 1))[0]
|
||||||
|
else:
|
||||||
|
return struct.unpack("B", self.ReadRAM(address - 0xA000, 1))[0]
|
||||||
else:
|
else:
|
||||||
return self.ReadROM(address, length)
|
if address < 0xA000:
|
||||||
|
return self.ReadROM(address, length)
|
||||||
|
else:
|
||||||
|
return self.ReadRAM(address - 0xA000, length)
|
||||||
elif self.MODE == "AGB":
|
elif self.MODE == "AGB":
|
||||||
if length == 0:
|
if length == 0:
|
||||||
length = 2
|
length = 2
|
||||||
|
|
@ -440,7 +461,7 @@ class GbxDevice:
|
||||||
return self.ReadROM(address, length)
|
return self.ReadROM(address, length)
|
||||||
|
|
||||||
def _cart_write(self, address, value, flashcart=False, sram=False):
|
def _cart_write(self, address, value, flashcart=False, sram=False):
|
||||||
dprint("Writing to cartridge: 0x{:X} = 0x{:X}".format(address, value & 0xFF), flashcart, sram)
|
dprint("Writing to cartridge: 0x{:X} = 0x{:X} (flashcart={:s}, sram={:s})".format(address, value & 0xFF, str(flashcart), str(sram)))
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
if flashcart:
|
if flashcart:
|
||||||
buffer = bytearray([self.DEVICE_CMD["DMG_FLASH_WRITE_BYTE"]])
|
buffer = bytearray([self.DEVICE_CMD["DMG_FLASH_WRITE_BYTE"]])
|
||||||
|
|
@ -457,7 +478,7 @@ class GbxDevice:
|
||||||
self._read(1)
|
self._read(1)
|
||||||
return
|
return
|
||||||
elif flashcart:
|
elif flashcart:
|
||||||
buffer = bytearray([self.DEVICE_CMD["AGB_FLASH_WRITE_BYTE"]])
|
buffer = bytearray([self.DEVICE_CMD["AGB_FLASH_WRITE_SHORT"]])
|
||||||
else:
|
else:
|
||||||
buffer = bytearray([self.DEVICE_CMD["AGB_CART_WRITE"]])
|
buffer = bytearray([self.DEVICE_CMD["AGB_CART_WRITE"]])
|
||||||
|
|
||||||
|
|
@ -465,19 +486,33 @@ class GbxDevice:
|
||||||
buffer.extend(struct.pack(">H", value & 0xFFFF))
|
buffer.extend(struct.pack(">H", value & 0xFFFF))
|
||||||
self._write(buffer)
|
self._write(buffer)
|
||||||
|
|
||||||
def _cart_write_flash(self, commands):
|
def _cart_write_flash(self, commands, flashcart=False):
|
||||||
|
if self.FW["fw_ver"] < 6 and not (self.MODE == "AGB" and not flashcart):
|
||||||
|
for command in commands:
|
||||||
|
self._cart_write(command[0], command[1], flashcart=flashcart)
|
||||||
|
return
|
||||||
|
|
||||||
num = len(commands)
|
num = len(commands)
|
||||||
buffer = bytearray([self.DEVICE_CMD["CART_WRITE_FLASH_CMD"]])
|
buffer = bytearray([self.DEVICE_CMD["CART_WRITE_FLASH_CMD"]])
|
||||||
|
if self.FW["fw_ver"] >= 6:
|
||||||
|
buffer.extend(struct.pack("B", 1 if flashcart else 0))
|
||||||
buffer.extend(struct.pack("B", num))
|
buffer.extend(struct.pack("B", num))
|
||||||
for i in range(0, num):
|
for i in range(0, num):
|
||||||
#dprint("Writing to cartridge: 0x{:X} = 0x{:X}".format(commands[i][0], commands[i][1] & 0xFF))
|
dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d})".format(commands[i][0], commands[i][1], i+1, num))
|
||||||
buffer.extend(struct.pack(">I", commands[i][0]))
|
if self.MODE == "AGB" and flashcart:
|
||||||
buffer.extend(struct.pack("B", commands[i][1]))
|
buffer.extend(struct.pack(">I", commands[i][0] >> 1))
|
||||||
|
else:
|
||||||
|
buffer.extend(struct.pack(">I", commands[i][0]))
|
||||||
|
|
||||||
|
if self.FW["fw_ver"] < 6:
|
||||||
|
buffer.extend(struct.pack("B", commands[i][1]))
|
||||||
|
else:
|
||||||
|
buffer.extend(struct.pack(">H", commands[i][1]))
|
||||||
|
|
||||||
self._write(buffer)
|
self._write(buffer)
|
||||||
|
ret = self._read(1)
|
||||||
if self._read(1) != 0x01:
|
if ret != 0x01:
|
||||||
print("Error!")
|
print("Error in _cart_write_flash():", ret)
|
||||||
|
|
||||||
def _clk_toggle(self, num):
|
def _clk_toggle(self, num):
|
||||||
if self.FW["pcb_ver"] not in (5, 6, 101): return False
|
if self.FW["pcb_ver"] not in (5, 6, 101): return False
|
||||||
|
|
@ -495,8 +530,6 @@ class GbxDevice:
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
||||||
elif self.MODE == "AGB":
|
elif self.MODE == "AGB":
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
||||||
else:
|
|
||||||
print("{:s}NOTE: Cartridge power cycling is not supported by this device.{:s}".format(ANSI.YELLOW, ANSI.RESET))
|
|
||||||
|
|
||||||
def CartPowerOff(self, delay=0.1):
|
def CartPowerOff(self, delay=0.1):
|
||||||
if self.FW["pcb_ver"] in (5, 6):
|
if self.FW["pcb_ver"] in (5, 6):
|
||||||
|
|
@ -519,10 +552,14 @@ class GbxDevice:
|
||||||
if mode == "DMG":
|
if mode == "DMG":
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
||||||
|
self._set_fw_variable("DMG_READ_METHOD", 1)
|
||||||
|
self._set_fw_variable("CART_MODE", 1)
|
||||||
self.MODE = "DMG"
|
self.MODE = "DMG"
|
||||||
elif mode == "AGB":
|
elif mode == "AGB":
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
||||||
|
self._set_fw_variable("AGB_READ_METHOD", 0)
|
||||||
|
self._set_fw_variable("CART_MODE", 2)
|
||||||
self.MODE = "AGB"
|
self.MODE = "AGB"
|
||||||
self._set_fw_variable(key="ADDRESS", value=0)
|
self._set_fw_variable(key="ADDRESS", value=0)
|
||||||
self.CartPowerOn()
|
self.CartPowerOn()
|
||||||
|
|
@ -534,7 +571,7 @@ class GbxDevice:
|
||||||
return (list(self.SUPPORTED_CARTS['AGB'].keys()), list(self.SUPPORTED_CARTS['AGB'].values()))
|
return (list(self.SUPPORTED_CARTS['AGB'].keys()), list(self.SUPPORTED_CARTS['AGB'].values()))
|
||||||
|
|
||||||
def SetProgress(self, args):
|
def SetProgress(self, args):
|
||||||
if self.CANCEL and args["action"] != "ABORT": return
|
if self.CANCEL and args["action"] not in ("ABORT", "FINISHED"): return
|
||||||
if args["action"] == "UPDATE_POS":
|
if args["action"] == "UPDATE_POS":
|
||||||
self.POS = args["pos"]
|
self.POS = args["pos"]
|
||||||
self.INFO["transferred"] = args["pos"]
|
self.INFO["transferred"] = args["pos"]
|
||||||
|
|
@ -543,7 +580,9 @@ class GbxDevice:
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
if self.SIGNAL is not None:
|
if self.SIGNAL is not None:
|
||||||
self.SIGNAL(args)
|
self.SIGNAL(args)
|
||||||
if args["action"] == "FINISHED": self.SIGNAL = None
|
|
||||||
|
if args["action"] == "FINISHED":
|
||||||
|
self.SIGNAL = None
|
||||||
|
|
||||||
def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
|
def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
|
||||||
if not self.IsConnected(): raise Exception("Couldn’t access the the device.")
|
if not self.IsConnected(): raise Exception("Couldn’t access the the device.")
|
||||||
|
|
@ -555,7 +594,7 @@ class GbxDevice:
|
||||||
self._write(self.DEVICE_CMD["OFW_CART_MODE"]) # Reset LEDs
|
self._write(self.DEVICE_CMD["OFW_CART_MODE"]) # Reset LEDs
|
||||||
self._read(1)
|
self._read(1)
|
||||||
self.CartPowerOn()
|
self.CartPowerOn()
|
||||||
|
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
||||||
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
||||||
|
|
@ -566,28 +605,30 @@ class GbxDevice:
|
||||||
self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=True)
|
self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=True)
|
||||||
|
|
||||||
header = self.ReadROM(0, 0x180)
|
header = self.ReadROM(0, 0x180)
|
||||||
|
if Util.DEBUG:
|
||||||
|
with open("debug_header.bin", "wb") as f: f.write(header)
|
||||||
if header is False or len(header) != 0x180:
|
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))
|
print("{:s}\n{:s}Couldn’t read the cartridge information. Please try again.{:s}".format(str(header), ANSI.RED, ANSI.RESET))
|
||||||
return False
|
return False
|
||||||
if Util.DEBUG:
|
|
||||||
with open("debug_header.bin", "wb") as f: f.write(header)
|
|
||||||
|
|
||||||
# Unlock DACS carts on older firmware
|
|
||||||
if self.MODE == "AGB" and (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] == 1):
|
|
||||||
if header[0x04:0x04+0x9C] == bytearray([0x00] * 0x9C):
|
|
||||||
self.ReadROM(0x1FFFFE0, 20)
|
|
||||||
header = self.ReadROM(0, 0x180)
|
|
||||||
|
|
||||||
# Parse ROM header
|
# Parse ROM header
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
data = RomFileDMG(header).GetHeader()
|
data = RomFileDMG(header).GetHeader()
|
||||||
if data["logo_correct"] is False: # try to fix weird bootlegs
|
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
|
||||||
|
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
|
||||||
self._cart_write(0, 0xFF)
|
self._cart_write(0, 0xFF)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
header = self.ReadROM(0, 0x180)
|
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
|
||||||
|
self._cart_write(0x0006, 0)
|
||||||
|
header[0:0x10] = self.ReadROM(0x4000, 0x10)
|
||||||
|
header[0xD0:0xE0] = self.ReadROM(0x40D0, 0x10)
|
||||||
data = RomFileDMG(header).GetHeader()
|
data = RomFileDMG(header).GetHeader()
|
||||||
|
|
||||||
_mbc = DMG_MBC().GetInstance(args={"mbc":data["features_raw"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
_mbc = DMG_MBC().GetInstance(args={"mbc":data["mapper_raw"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
||||||
if checkRtc:
|
if checkRtc:
|
||||||
data["has_rtc"] = _mbc.HasRTC() is True
|
data["has_rtc"] = _mbc.HasRTC() is True
|
||||||
if data["has_rtc"] is True:
|
if data["has_rtc"] is True:
|
||||||
|
|
@ -597,26 +638,35 @@ class GbxDevice:
|
||||||
data["has_rtc"] = False
|
data["has_rtc"] = False
|
||||||
|
|
||||||
elif self.MODE == "AGB":
|
elif self.MODE == "AGB":
|
||||||
|
# Unlock DACS carts on older firmware
|
||||||
|
if self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] == 1:
|
||||||
|
if header[0x04:0x04+0x9C] == bytearray([0x00] * 0x9C):
|
||||||
|
self.ReadROM(0x1FFFFE0, 20)
|
||||||
|
header = self.ReadROM(0, 0x180)
|
||||||
|
|
||||||
data = RomFileAGB(header).GetHeader()
|
data = RomFileAGB(header).GetHeader()
|
||||||
if data["logo_correct"] is False: # workaround for weird bootlegs
|
if data["logo_correct"] is False: # workaround for strange bootlegs
|
||||||
self._cart_write(0, 0xFF)
|
self._cart_write(0, 0xFF)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
header = self.ReadROM(0, 0x180)
|
header = self.ReadROM(0, 0x180)
|
||||||
data = RomFileAGB(header).GetHeader()
|
data = RomFileAGB(header).GetHeader()
|
||||||
|
|
||||||
# Check where the ROM data repeats
|
if data["empty"] or data["empty_nocart"]:
|
||||||
size_check = header[0xA0:0xA0+16]
|
data["rom_size"] = 0x2000000
|
||||||
currAddr = 0x400000
|
elif (data["3d_memory"] == True):
|
||||||
while currAddr < 0x2000000:
|
|
||||||
buffer = self.ReadROM(currAddr + 0xA0, 64)[:16]
|
|
||||||
if buffer == size_check: break
|
|
||||||
currAddr += 0x400000
|
|
||||||
data["rom_size"] = currAddr
|
|
||||||
|
|
||||||
if (data["3d_memory"] == True):
|
|
||||||
data["rom_size"] = 0x4000000
|
data["rom_size"] = 0x4000000
|
||||||
elif (self.ReadROM(0x1FFE000, 0x0C) == b"AGBFLASHDACS"):
|
else:
|
||||||
data["dacs_8m"] = True
|
# Check where the ROM data repeats (for unlicensed carts)
|
||||||
|
size_check = header[0xA0:0xA0+16]
|
||||||
|
currAddr = 0x400000
|
||||||
|
while currAddr < 0x2000000:
|
||||||
|
buffer = self.ReadROM(currAddr + 0xA0, 64)[:16]
|
||||||
|
if buffer == size_check: break
|
||||||
|
currAddr += 0x400000
|
||||||
|
data["rom_size"] = currAddr
|
||||||
|
|
||||||
|
if (self.ReadROM(0x1FFE000, 0x0C) == b"AGBFLASHDACS"):
|
||||||
|
data["dacs_8m"] = True
|
||||||
|
|
||||||
if checkRtc and data["logo_correct"] is True and header[0xC5] == 0 and header[0xC7] == 0 and header[0xC9] == 0:
|
if checkRtc and data["logo_correct"] is True and header[0xC5] == 0 and header[0xC7] == 0 and header[0xC9] == 0:
|
||||||
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
||||||
|
|
@ -653,7 +703,7 @@ class GbxDevice:
|
||||||
self.INFO["has_rtc"] = has_rtc
|
self.INFO["has_rtc"] = has_rtc
|
||||||
|
|
||||||
if self.MODE == "DMG" and mbc is None:
|
if self.MODE == "DMG" and mbc is None:
|
||||||
mbc = info["features_raw"]
|
mbc = info["mapper_raw"]
|
||||||
if mbc > 0x200: checkSaveType = False
|
if mbc > 0x200: checkSaveType = False
|
||||||
|
|
||||||
(cart_types, cart_type_id, flash_id, cfi_s, cfi) = self.AutoDetectFlash(limitVoltage=limitVoltage)
|
(cart_types, cart_type_id, flash_id, cfi_s, cfi) = self.AutoDetectFlash(limitVoltage=limitVoltage)
|
||||||
|
|
@ -1124,7 +1174,7 @@ class GbxDevice:
|
||||||
for i in range(0, num):
|
for i in range(0, num):
|
||||||
self._set_fw_variable("TRANSFER_SIZE", length)
|
self._set_fw_variable("TRANSFER_SIZE", length)
|
||||||
self._set_fw_variable("ADDRESS", address)
|
self._set_fw_variable("ADDRESS", address)
|
||||||
#dprint("Now in iteration {:d}".format(i))
|
dprint("Now in iteration {:d}".format(i))
|
||||||
|
|
||||||
if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
|
if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
|
||||||
skip_write = True
|
skip_write = True
|
||||||
|
|
@ -1248,14 +1298,22 @@ class GbxDevice:
|
||||||
[ address, length - 1 ],
|
[ address, length - 1 ],
|
||||||
[ address, 0x00 ],
|
[ address, 0x00 ],
|
||||||
])
|
])
|
||||||
while True:
|
lives = 100
|
||||||
|
while lives > 0:
|
||||||
self._cart_write(0x4000, 0x70)
|
self._cart_write(0x4000, 0x70)
|
||||||
sr = self._cart_read(address + length - 1)
|
sr = self._cart_read(address + length - 1)
|
||||||
dprint("sr=0x{:X}".format(sr))
|
dprint("sr=0x{:X}".format(sr))
|
||||||
if sr & 0x80 == 0x80: break
|
if sr & 0x80 == 0x80: break
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
|
lives -= 1
|
||||||
self._cart_write(0x4000, 0xFF)
|
self._cart_write(0x4000, 0xFF)
|
||||||
|
|
||||||
|
if lives == 0:
|
||||||
|
self.CANCEL_ARGS = {"info_type":"msgbox_critical", "info_msg":"Flash write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(sr), i, length)}
|
||||||
|
self.CANCEL = True
|
||||||
|
self.ERROR = True
|
||||||
|
return False
|
||||||
|
|
||||||
address += length
|
address += length
|
||||||
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
||||||
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
||||||
|
|
@ -1263,11 +1321,48 @@ class GbxDevice:
|
||||||
self._cart_write(address - 1, 0xFF)
|
self._cart_write(address - 1, 0xFF)
|
||||||
self.SKIPPING = skip_write
|
self.SKIPPING = skip_write
|
||||||
|
|
||||||
|
def WriteROM_DMG_EEPROM(self, address, buffer, bank, eeprom_buffer_size=0x80):
|
||||||
|
length = len(buffer)
|
||||||
|
if self.FW["pcb_ver"] not in (5, 6, 101):
|
||||||
|
max_length = 256
|
||||||
|
else:
|
||||||
|
max_length = 1024
|
||||||
|
num = math.ceil(length / max_length)
|
||||||
|
if length > max_length: length = max_length
|
||||||
|
dprint("Writing 0x{:X} bytes to EEPROM in {:d} iteration(s)".format(length, num))
|
||||||
|
|
||||||
|
for i in range(0, num):
|
||||||
|
self._set_fw_variable("BUFFER_SIZE", eeprom_buffer_size)
|
||||||
|
self._set_fw_variable("TRANSFER_SIZE", length)
|
||||||
|
self._set_fw_variable("ADDRESS", address)
|
||||||
|
dprint("Now in iteration {:d}".format(i))
|
||||||
|
|
||||||
|
self._write(self.DEVICE_CMD["DMG_EEPROM_WRITE"])
|
||||||
|
self._write(buffer[i*length:i*length+length])
|
||||||
|
ret = self._read(1)
|
||||||
|
if ret not in (0x01, 0x03):
|
||||||
|
self.CANCEL_ARGS = {"info_type":"msgbox_critical", "info_msg":"EEPROM write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)}
|
||||||
|
self.CANCEL = True
|
||||||
|
self.ERROR = True
|
||||||
|
return False
|
||||||
|
|
||||||
|
address += length
|
||||||
|
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
||||||
|
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
||||||
|
|
||||||
|
self._cart_write_flash([
|
||||||
|
[0x0006, 0x01],
|
||||||
|
[0x5555, 0xAA],
|
||||||
|
[0x2AAA, 0x55],
|
||||||
|
[0x5555, 0xF0],
|
||||||
|
[0x0006, bank]
|
||||||
|
])
|
||||||
|
|
||||||
def CheckROMStable(self):
|
def CheckROMStable(self):
|
||||||
if not self.IsConnected(): raise Exception("Couldn’t access the the device.")
|
if not self.IsConnected(): raise Exception("Couldn’t access the the device.")
|
||||||
buffer1 = self.ReadROM(0x100, 0x40)
|
buffer1 = self.ReadROM(0x80, 0x40)
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
buffer2 = self.ReadROM(0x100, 0x40)
|
buffer2 = self.ReadROM(0x80, 0x40)
|
||||||
return buffer1 == buffer2
|
return buffer1 == buffer2
|
||||||
|
|
||||||
def AutoDetectFlash(self, limitVoltage=False):
|
def AutoDetectFlash(self, limitVoltage=False):
|
||||||
|
|
@ -1277,12 +1372,13 @@ class GbxDevice:
|
||||||
flash_id_found = False
|
flash_id_found = False
|
||||||
|
|
||||||
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
||||||
|
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
if limitVoltage:
|
if limitVoltage:
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
||||||
else:
|
else:
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
||||||
time.sleep(0.25)
|
time.sleep(0.1)
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
||||||
elif self.MODE == "DMG":
|
elif self.MODE == "DMG":
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
||||||
|
|
@ -1295,21 +1391,21 @@ class GbxDevice:
|
||||||
dprint("*** Now checking: {:s}\n".format(flashcart_meta["names"][0]))
|
dprint("*** Now checking: {:s}\n".format(flashcart_meta["names"][0]))
|
||||||
|
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
if flashcart_meta["write_pin"] == "WR":
|
we = flashcart_meta["write_pin"]
|
||||||
we = 0x01 # FLASH_WE_PIN_WR
|
if we == "WR":
|
||||||
elif flashcart_meta["write_pin"] in ("AUDIO", "VIN"):
|
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
|
||||||
we = 0x02 # FLASH_WE_PIN_AUDIO
|
elif we in ("AUDIO", "VIN"):
|
||||||
elif flashcart_meta["write_pin"] == "WR+RESET":
|
self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
|
||||||
we = 0x03 # FLASH_WE_PIN_WR_RESET
|
elif we == "WR+RESET":
|
||||||
self._set_fw_variable("FLASH_WE_PIN", we)
|
self._set_fw_variable("FLASH_WE_PIN", 0x03) # FLASH_WE_PIN_WR_RESET
|
||||||
|
|
||||||
flashcart = Flashcart(config=flashcart_meta, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle)
|
flashcart = Flashcart(config=flashcart_meta, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle)
|
||||||
flashcart.Reset(full_reset=False)
|
flashcart.Reset(full_reset=False)
|
||||||
flashcart.Unlock()
|
flashcart.Unlock()
|
||||||
if "flash_ids" in flashcart_meta and len(flashcart_meta["flash_ids"]) > 0:
|
if "flash_ids" in flashcart_meta and len(flashcart_meta["flash_ids"]) > 0:
|
||||||
vfid = flashcart.VerifyFlashID()
|
vfid = flashcart.VerifyFlashID()
|
||||||
if vfid is not False:
|
if vfid is not False:
|
||||||
(verified, cart_flash_id) = flashcart.VerifyFlashID()
|
(verified, cart_flash_id) = vfid
|
||||||
if verified and cart_flash_id in flashcart_meta["flash_ids"]:
|
if verified and cart_flash_id in flashcart_meta["flash_ids"]:
|
||||||
flash_id = cart_flash_id
|
flash_id = cart_flash_id
|
||||||
flash_id_found = True
|
flash_id_found = True
|
||||||
|
|
@ -1349,10 +1445,20 @@ class GbxDevice:
|
||||||
|
|
||||||
if self.MODE == "DMG" and not flash_id_found:
|
if self.MODE == "DMG" and not flash_id_found:
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
||||||
time.sleep(0.25)
|
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)
|
||||||
|
|
||||||
def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most horribly written function
|
def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most horribly written function
|
||||||
|
if cart_type is not None:
|
||||||
|
if self.MODE == "DMG":
|
||||||
|
if cart_type["write_pin"] == "WR":
|
||||||
|
we = 0x01 # FLASH_WE_PIN_WR
|
||||||
|
elif cart_type["write_pin"] in ("AUDIO", "VIN"):
|
||||||
|
we = 0x02 # FLASH_WE_PIN_AUDIO
|
||||||
|
elif cart_type["write_pin"] == "WR+RESET":
|
||||||
|
we = 0x03 # FLASH_WE_PIN_WR_RESET
|
||||||
|
self._set_fw_variable("FLASH_WE_PIN", we)
|
||||||
|
|
||||||
if self.FW["pcb_ver"] in (5, 6, 101):
|
if self.FW["pcb_ver"] in (5, 6, 101):
|
||||||
self._write(self.DEVICE_CMD["OFW_CART_MODE"])
|
self._write(self.DEVICE_CMD["OFW_CART_MODE"])
|
||||||
self._read(1)
|
self._read(1)
|
||||||
|
|
@ -1375,6 +1481,13 @@ class GbxDevice:
|
||||||
#{ 'read_cfi':[[0x4000, 0x98]], 'read_identifier':[[ 0x4000, 0x90 ]], 'reset':[[ 0x4000, 0xFF ]] },
|
#{ 'read_cfi':[[0x4000, 0x98]], 'read_identifier':[[ 0x4000, 0x90 ]], 'reset':[[ 0x4000, 0xFF ]] },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if self.MODE == "DMG":
|
||||||
|
if limitVoltage:
|
||||||
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
||||||
|
else:
|
||||||
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
check_buffer = self.ReadROM(0, 0x400)
|
check_buffer = self.ReadROM(0, 0x400)
|
||||||
d_swap = None
|
d_swap = None
|
||||||
cfi_info = ""
|
cfi_info = ""
|
||||||
|
|
@ -1385,11 +1498,6 @@ class GbxDevice:
|
||||||
cfi = {'raw':b''}
|
cfi = {'raw':b''}
|
||||||
|
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
if limitVoltage:
|
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
||||||
else:
|
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
||||||
time.sleep(0.25)
|
|
||||||
rom_string = "[ ROM ] " + rom_string
|
rom_string = "[ ROM ] " + rom_string
|
||||||
we_pins = [ "WR", "AUDIO" ]
|
we_pins = [ "WR", "AUDIO" ]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1414,7 +1522,8 @@ class GbxDevice:
|
||||||
buffer = self.ReadROM(0, 0x400)
|
buffer = self.ReadROM(0, 0x400)
|
||||||
for i in range(0, len(method['reset'])):
|
for i in range(0, len(method['reset'])):
|
||||||
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
||||||
if buffer == check_buffer: continue
|
#if buffer == check_buffer: continue
|
||||||
|
if buffer == bytearray([0x00] * len(buffer)): continue
|
||||||
|
|
||||||
magic = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
|
magic = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
|
||||||
if magic == "QRY": # nothing swapped
|
if magic == "QRY": # nothing swapped
|
||||||
|
|
@ -1482,6 +1591,7 @@ class GbxDevice:
|
||||||
self._cart_write(method['read_identifier'][i][0], method["read_identifier"][i][1], flashcart=True)
|
self._cart_write(method['read_identifier'][i][0], method["read_identifier"][i][1], flashcart=True)
|
||||||
flash_id = self.ReadROM(0, 8)
|
flash_id = self.ReadROM(0, 8)
|
||||||
if flash_id == check_buffer[:len(flash_id)]: continue
|
if flash_id == check_buffer[:len(flash_id)]: continue
|
||||||
|
if flash_id == bytearray([0x00] * len(flash_id)): continue
|
||||||
|
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
method_string = "[" + we.ljust(5) + "/{:4X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
method_string = "[" + we.ljust(5) + "/{:4X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
||||||
|
|
@ -1495,7 +1605,7 @@ class GbxDevice:
|
||||||
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
||||||
|
|
||||||
if cart_type is not None: # reset cartridge if method is known
|
if cart_type is not None: # reset cartridge if method is known
|
||||||
flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=None)
|
flashcart = Flashcart(config=cart_type, 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=None)
|
||||||
flashcart.Reset(full_reset=False)
|
flashcart.Reset(full_reset=False)
|
||||||
|
|
||||||
if "method" in cfi:
|
if "method" in cfi:
|
||||||
|
|
@ -1543,7 +1653,8 @@ class GbxDevice:
|
||||||
flash_id = self.ReadROM(method["start_addr"], 8)
|
flash_id = self.ReadROM(method["start_addr"], 8)
|
||||||
else:
|
else:
|
||||||
flash_id = self.ReadROM(0, 8)
|
flash_id = self.ReadROM(0, 8)
|
||||||
|
if flash_id == bytearray([0x00] * len(flash_id)): continue
|
||||||
|
|
||||||
for i in range(0, len(method['reset'])):
|
for i in range(0, len(method['reset'])):
|
||||||
self._cart_write(method['reset'][i][0], method["reset"][i][1], flashcart=True)
|
self._cart_write(method['reset'][i][0], method["reset"][i][1], flashcart=True)
|
||||||
|
|
||||||
|
|
@ -1561,10 +1672,10 @@ class GbxDevice:
|
||||||
|
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
||||||
time.sleep(0.25)
|
time.sleep(0.2)
|
||||||
|
|
||||||
if cart_type is not None: # reset cartridge if method is known
|
if cart_type is not None: # reset cartridge if method is known
|
||||||
flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=None)
|
flashcart = Flashcart(config=cart_type, 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=None)
|
||||||
flashcart.Reset(full_reset=True)
|
flashcart.Reset(full_reset=True)
|
||||||
|
|
||||||
flash_id = ""
|
flash_id = ""
|
||||||
|
|
@ -1626,13 +1737,18 @@ class GbxDevice:
|
||||||
if i == args["cart_type"]:
|
if i == args["cart_type"]:
|
||||||
try:
|
try:
|
||||||
cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i])
|
cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i])
|
||||||
flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress)
|
flashcart = Flashcart(config=cart_type, 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)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
buffer_len = 0x4000
|
buffer_len = 0x4000
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
self.INFO["features_raw"] = args["mbc"]
|
self.INFO["mapper_raw"] = args["mbc"]
|
||||||
|
if not self.IsSupportedMbc(args["mbc"]):
|
||||||
|
msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device. An updated hardware revision is required.".format(Util.APPNAME, self.GetFullName())
|
||||||
|
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
|
||||||
|
return False
|
||||||
|
|
||||||
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
||||||
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
||||||
|
|
||||||
|
|
@ -1643,6 +1759,9 @@ class GbxDevice:
|
||||||
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
||||||
_mbc.EnableMapper()
|
_mbc.EnableMapper()
|
||||||
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
||||||
|
elif _mbc.GetName() == "Sachen":
|
||||||
|
start_bank = int(args["rom_size"] / 0x4000)
|
||||||
|
_mbc.SetStartBank(start_bank)
|
||||||
else:
|
else:
|
||||||
_mbc.EnableMapper()
|
_mbc.EnableMapper()
|
||||||
|
|
||||||
|
|
@ -1701,6 +1820,7 @@ class GbxDevice:
|
||||||
cancel_args = {"action":"ABORT", "abortable":False}
|
cancel_args = {"action":"ABORT", "abortable":False}
|
||||||
cancel_args.update(self.CANCEL_ARGS)
|
cancel_args.update(self.CANCEL_ARGS)
|
||||||
self.CANCEL_ARGS = {}
|
self.CANCEL_ARGS = {}
|
||||||
|
self.ERROR_ARGS = {}
|
||||||
self.SetProgress(cancel_args)
|
self.SetProgress(cancel_args)
|
||||||
try:
|
try:
|
||||||
if file is not None: file.close()
|
if file is not None: file.close()
|
||||||
|
|
@ -1770,14 +1890,26 @@ class GbxDevice:
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
# Calculate Global Checksum
|
# Calculate Global Checksum
|
||||||
|
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
|
||||||
|
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
|
||||||
|
#self.INFO["file_sha256"] = hashlib.sha256(buffer).hexdigest()
|
||||||
|
#self.INFO["file_md5"] = hashlib.md5(buffer).hexdigest()
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
chk = _mbc.CalcChecksum(buffer)
|
chk = _mbc.CalcChecksum(buffer)
|
||||||
elif self.MODE == "AGB":
|
elif self.MODE == "AGB":
|
||||||
chk = zlib.crc32(buffer) & 0xFFFFFFFF
|
chk = self.INFO["file_crc32"]
|
||||||
|
|
||||||
self.INFO["rom_checksum_calc"] = chk
|
self.INFO["rom_checksum_calc"] = chk
|
||||||
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
|
|
||||||
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
|
# Check for ROM loops
|
||||||
|
self.INFO["loop_detected"] = False
|
||||||
|
temp = min(0x2000000, len(buffer))
|
||||||
|
while temp > 0x4000:
|
||||||
|
temp = temp >> 1
|
||||||
|
if (buffer[0:0x4000] == buffer[temp:temp+0x4000]):
|
||||||
|
if buffer[0:temp] == buffer[temp:temp*2]:
|
||||||
|
self.INFO["loop_detected"] = temp
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
# ↓↓↓ Switch to first ROM bank
|
# ↓↓↓ Switch to first ROM bank
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
|
|
@ -1807,6 +1939,10 @@ class GbxDevice:
|
||||||
extra_size = 0
|
extra_size = 0
|
||||||
audio_low = False
|
audio_low = False
|
||||||
if self.MODE == "DMG":
|
if self.MODE == "DMG":
|
||||||
|
if not self.IsSupportedMbc(args["mbc"]):
|
||||||
|
msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device. An updated hardware revision is required.".format(Util.APPNAME, self.GetFullName())
|
||||||
|
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
|
||||||
|
return False
|
||||||
save_size = args["save_type"]
|
save_size = args["save_type"]
|
||||||
ram_banks = _mbc.GetRAMBanks(save_size)
|
ram_banks = _mbc.GetRAMBanks(save_size)
|
||||||
buffer_len = min(0x200, _mbc.GetRAMBankSize())
|
buffer_len = min(0x200, _mbc.GetRAMBankSize())
|
||||||
|
|
@ -1814,6 +1950,7 @@ class GbxDevice:
|
||||||
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
||||||
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
||||||
|
|
||||||
|
# Enable mappers
|
||||||
if _mbc.GetName() == "TAMA5":
|
if _mbc.GetName() == "TAMA5":
|
||||||
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
||||||
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
||||||
|
|
@ -1828,6 +1965,32 @@ class GbxDevice:
|
||||||
self._set_fw_variable("FLASH_METHOD", 0x04) # FLASH_METHOD_DMG_MBC6
|
self._set_fw_variable("FLASH_METHOD", 0x04) # FLASH_METHOD_DMG_MBC6
|
||||||
self._set_fw_variable("FLASH_WE_PIN", 0x01) # WR
|
self._set_fw_variable("FLASH_WE_PIN", 0x01) # WR
|
||||||
_mbc.EnableFlash(enable=True, enable_write=True if (args["mode"] == 3) else False)
|
_mbc.EnableFlash(enable=True, enable_write=True if (args["mode"] == 3) else False)
|
||||||
|
elif _mbc.GetName() == "Xploder GB":
|
||||||
|
empty_data_byte = 0xFF
|
||||||
|
self._set_fw_variable("FLASH_PULSE_RESET", 0)
|
||||||
|
self._set_fw_variable("FLASH_DOUBLE_DIE", 0)
|
||||||
|
self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
|
||||||
|
self._write(0x00) # FLASH_COMMAND_SET_NONE
|
||||||
|
self._write(0x01) # FLASH_METHOD_UNBUFFERED
|
||||||
|
self._write(0x01) # FLASH_WE_PIN_WR
|
||||||
|
commands = [
|
||||||
|
[ 0x5555, 0xAA ],
|
||||||
|
[ 0x2AAA, 0x55 ],
|
||||||
|
[ 0x5555, 0xA0 ],
|
||||||
|
]
|
||||||
|
for i in range(0, 6):
|
||||||
|
if i >= len(commands):
|
||||||
|
self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
|
||||||
|
else:
|
||||||
|
self._write(bytearray(struct.pack(">I", commands[i][0])) + bytearray(struct.pack(">H", commands[i][1])))
|
||||||
|
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
||||||
|
self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
|
||||||
|
self._write(1) # number of commands
|
||||||
|
self._write(bytearray(struct.pack(">I", 0x0006))) # address/value
|
||||||
|
self._write(0) # type = address
|
||||||
|
ret = self._read(1)
|
||||||
|
if ret != 0x01:
|
||||||
|
print("Error in DMG_SET_BANK_CHANGE_CMD:", ret)
|
||||||
else:
|
else:
|
||||||
_mbc.EnableMapper()
|
_mbc.EnableMapper()
|
||||||
|
|
||||||
|
|
@ -1918,7 +2081,7 @@ class GbxDevice:
|
||||||
command = commands[args["save_type"]][args["mode"] - 2]
|
command = commands[args["save_type"]][args["mode"] - 2]
|
||||||
if args["rtc"] is True:
|
if args["rtc"] is True:
|
||||||
extra_size = 0x10
|
extra_size = 0x10
|
||||||
|
|
||||||
if args["mode"] == 2: # Backup
|
if args["mode"] == 2: # Backup
|
||||||
action = "SAVE_READ"
|
action = "SAVE_READ"
|
||||||
buffer = bytearray()
|
buffer = bytearray()
|
||||||
|
|
@ -1927,6 +2090,8 @@ class GbxDevice:
|
||||||
self.INFO["save_erase"] = args["erase"]
|
self.INFO["save_erase"] = args["erase"]
|
||||||
if args["erase"] == True:
|
if args["erase"] == True:
|
||||||
buffer = bytearray([ empty_data_byte ] * save_size)
|
buffer = bytearray([ empty_data_byte ] * save_size)
|
||||||
|
if self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
|
||||||
|
buffer[0] = 0x00
|
||||||
else:
|
else:
|
||||||
if args["path"] is None:
|
if args["path"] is None:
|
||||||
buffer = self.INFO["data"]
|
buffer = self.INFO["data"]
|
||||||
|
|
@ -1956,6 +2121,10 @@ class GbxDevice:
|
||||||
if ((buffer_offset - 0x8000) % 0x20000) == 0:
|
if ((buffer_offset - 0x8000) % 0x20000) == 0:
|
||||||
dprint("Erasing flash sector at position 0x{:X}".format(buffer_offset))
|
dprint("Erasing flash sector at position 0x{:X}".format(buffer_offset))
|
||||||
_mbc.EraseFlashSector()
|
_mbc.EraseFlashSector()
|
||||||
|
elif _mbc.GetName() == "Xploder GB":
|
||||||
|
self._set_fw_variable("DMG_ROM_BANK", bank + 8)
|
||||||
|
(start_address, bank_size) = _mbc.SelectBankRAM(bank)
|
||||||
|
end_address = min(save_size, start_address + bank_size)
|
||||||
else:
|
else:
|
||||||
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1 if _mbc.WriteWithCSPulse() else 0)
|
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1 if _mbc.WriteWithCSPulse() else 0)
|
||||||
(start_address, bank_size) = _mbc.SelectBankRAM(bank)
|
(start_address, bank_size) = _mbc.SelectBankRAM(bank)
|
||||||
|
|
@ -1994,6 +2163,7 @@ class GbxDevice:
|
||||||
cancel_args = {"action":"ABORT", "abortable":False}
|
cancel_args = {"action":"ABORT", "abortable":False}
|
||||||
cancel_args.update(self.CANCEL_ARGS)
|
cancel_args.update(self.CANCEL_ARGS)
|
||||||
self.CANCEL_ARGS = {}
|
self.CANCEL_ARGS = {}
|
||||||
|
self.ERROR_ARGS = {}
|
||||||
self.SetProgress(cancel_args)
|
self.SetProgress(cancel_args)
|
||||||
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
||||||
return
|
return
|
||||||
|
|
@ -2005,6 +2175,8 @@ class GbxDevice:
|
||||||
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=False, max_length=max_length)
|
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=False, max_length=max_length)
|
||||||
elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
|
elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
|
||||||
temp = self.ReadRAM_TAMA5()
|
temp = self.ReadRAM_TAMA5()
|
||||||
|
elif self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
|
||||||
|
temp = self.ReadROM(address=0x20000+pos, length=buffer_len, skip_init=False, max_length=max_length)
|
||||||
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
|
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
|
||||||
temp = self.ReadRAM(address=int(pos/8), length=buffer_len, command=command, max_length=max_length)
|
temp = self.ReadRAM(address=int(pos/8), length=buffer_len, command=command, max_length=max_length)
|
||||||
elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
|
elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
|
||||||
|
|
@ -2037,6 +2209,8 @@ class GbxDevice:
|
||||||
self.WriteFlash_MBC6(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], mapper=_mbc)
|
self.WriteFlash_MBC6(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], mapper=_mbc)
|
||||||
elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
|
elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
|
||||||
self.WriteRAM_TAMA5(buffer=buffer[buffer_offset:buffer_offset+buffer_len])
|
self.WriteRAM_TAMA5(buffer=buffer[buffer_offset:buffer_offset+buffer_len])
|
||||||
|
elif self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
|
||||||
|
self.WriteROM_DMG_EEPROM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], bank=bank+8)
|
||||||
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
|
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
|
||||||
self.WriteRAM(address=int(pos/8), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
self.WriteRAM(address=int(pos/8), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
||||||
elif self.MODE == "AGB" and args["save_type"] in (4, 5): # FLASH
|
elif self.MODE == "AGB" and args["save_type"] in (4, 5): # FLASH
|
||||||
|
|
@ -2121,17 +2295,18 @@ class GbxDevice:
|
||||||
file.close()
|
file.close()
|
||||||
else:
|
else:
|
||||||
self.INFO["data"] = buffer
|
self.INFO["data"] = buffer
|
||||||
|
|
||||||
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
|
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
|
||||||
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
|
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
|
||||||
|
|
||||||
if "verify_write" in args and args["verify_write"] not in (None, False):
|
if "verify_write" in args and args["verify_write"] not in (None, False):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif args["mode"] == 3: # Restore
|
elif args["mode"] == 3: # Restore
|
||||||
self.INFO["transferred"] = len(buffer)
|
self.INFO["transferred"] = len(buffer)
|
||||||
if args["rtc"] is True:
|
if args["rtc"] is True:
|
||||||
advance = args["rtc_advance"]
|
advance = args["rtc_advance"]
|
||||||
dprint("rtc_advance:", advance)
|
self.SetProgress({"action":"UPDATE_RTC", "method":"write"})
|
||||||
if self.MODE == "DMG" and args["rtc"] is True:
|
if self.MODE == "DMG" and args["rtc"] is True:
|
||||||
_mbc.WriteRTC(buffer[-_mbc.GetRTCBufferSize():], advance=advance)
|
_mbc.WriteRTC(buffer[-_mbc.GetRTCBufferSize():], advance=advance)
|
||||||
elif self.MODE == "AGB":
|
elif self.MODE == "AGB":
|
||||||
|
|
@ -2174,7 +2349,7 @@ class GbxDevice:
|
||||||
_mbc.EnableRAM(enable=False)
|
_mbc.EnableRAM(enable=False)
|
||||||
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
||||||
if audio_low: self._set_fw_variable("FLASH_WE_PIN", 0x02)
|
if audio_low: self._set_fw_variable("FLASH_WE_PIN", 0x02)
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
self.INFO["last_action"] = self.INFO["action"]
|
self.INFO["last_action"] = self.INFO["action"]
|
||||||
self.INFO["action"] = None
|
self.INFO["action"] = None
|
||||||
|
|
@ -2239,7 +2414,7 @@ class GbxDevice:
|
||||||
# Firmware check L2
|
# Firmware check L2
|
||||||
# Firmware check L5
|
# Firmware check L5
|
||||||
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True):
|
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True):
|
||||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires firmware version L5 or higher on the GBxCart RW v1.4 hardware revision. It may also work with older hardware revisions using the official firmware and insideGadgets flasher software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>.", "abortable":False})
|
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L5 on the GBxCart RW v1.4 hardware revision or newer. It may also work with older hardware revisions using the official firmware and insideGadgets flasher software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>.", "abortable":False})
|
||||||
return False
|
return False
|
||||||
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("double_die" in cart_type and cart_type["double_die"] is True):
|
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("double_die" in cart_type and cart_type["double_die"] is True):
|
||||||
if self.FW["pcb_ver"] in (5, 6):
|
if self.FW["pcb_ver"] in (5, 6):
|
||||||
|
|
@ -2256,7 +2431,7 @@ class GbxDevice:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if cart_type["command_set"] == "GBMEMORY":
|
if cart_type["command_set"] == "GBMEMORY":
|
||||||
flashcart = Flashcart_DMG_MMSA(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, progress_fncptr=self.SetProgress)
|
flashcart = Flashcart_DMG_MMSA(config=cart_type, cart_write_fncptr=self._cart_write, cart_write_fast_fncptr=self._cart_write_flash, cart_read_fncptr=self.ReadROM, progress_fncptr=self.SetProgress)
|
||||||
if "buffer_map" not in args:
|
if "buffer_map" not in args:
|
||||||
if os.path.exists(os.path.splitext(args["path"])[0] + ".map"):
|
if os.path.exists(os.path.splitext(args["path"])[0] + ".map"):
|
||||||
with open(os.path.splitext(args["path"])[0] + ".map", "rb") as file: args["buffer_map"] = file.read()
|
with open(os.path.splitext(args["path"])[0] + ".map", "rb") as file: args["buffer_map"] = file.read()
|
||||||
|
|
@ -2280,7 +2455,7 @@ class GbxDevice:
|
||||||
data_map_import = bytearray(data_map_import)
|
data_map_import = bytearray(data_map_import)
|
||||||
dprint("Hidden sector data loaded")
|
dprint("Hidden sector data loaded")
|
||||||
else:
|
else:
|
||||||
flashcart = Flashcart(config=cart_type, cart_write_fncptr=self._cart_write, cart_read_fncptr=self.ReadROM, cart_powercycle_fncptr=self.CartPowerCycle, progress_fncptr=self.SetProgress)
|
flashcart = Flashcart(config=cart_type, 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)
|
||||||
|
|
||||||
rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
|
rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
|
||||||
|
|
||||||
|
|
@ -2317,6 +2492,12 @@ class GbxDevice:
|
||||||
args["mbc"] = mbc
|
args["mbc"] = mbc
|
||||||
else:
|
else:
|
||||||
args["mbc"] = 0x1B # MBC5+SRAM+BATTERY
|
args["mbc"] = 0x1B # MBC5+SRAM+BATTERY
|
||||||
|
|
||||||
|
if not self.IsSupportedMbc(args["mbc"]):
|
||||||
|
msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device. An updated hardware revision is required.".format(Util.APPNAME, self.GetFullName())
|
||||||
|
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
|
||||||
|
return False
|
||||||
|
|
||||||
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
||||||
|
|
||||||
self._set_fw_variable("FLASH_PULSE_RESET", 1 if flashcart.PulseResetAfterWrite() else 0)
|
self._set_fw_variable("FLASH_PULSE_RESET", 1 if flashcart.PulseResetAfterWrite() else 0)
|
||||||
|
|
@ -2356,6 +2537,8 @@ class GbxDevice:
|
||||||
elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
|
elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
|
||||||
temp = 0x00
|
temp = 0x00
|
||||||
dprint("Using GB Memory command set")
|
dprint("Using GB Memory command set")
|
||||||
|
elif command_set_type == "BLAZE_XPLODER":
|
||||||
|
temp = 0x00
|
||||||
else:
|
else:
|
||||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM flashing.", "abortable":False})
|
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM flashing.", "abortable":False})
|
||||||
return False
|
return False
|
||||||
|
|
@ -2427,10 +2610,36 @@ class GbxDevice:
|
||||||
dprint("Setting command #{:d} to 0x{:X}=0x{:X}".format(i, address, value))
|
dprint("Setting command #{:d} to 0x{:X}=0x{:X}".format(i, address, value))
|
||||||
self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value)))
|
self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value)))
|
||||||
|
|
||||||
if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
if self.FW["fw_ver"] >= 6:
|
||||||
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
||||||
|
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
||||||
|
self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
|
||||||
|
if "bank_switch" in cart_type["commands"]:
|
||||||
|
self._write(len(cart_type["commands"]["bank_switch"])) # number of commands
|
||||||
|
for command in cart_type["commands"]["bank_switch"]:
|
||||||
|
address = command[0]
|
||||||
|
value = command[1]
|
||||||
|
if value == "ID":
|
||||||
|
self._write(bytearray(struct.pack(">I", address))) # address
|
||||||
|
self._write(0) # type = address
|
||||||
|
else:
|
||||||
|
self._write(bytearray(struct.pack(">I", value))) # value
|
||||||
|
self._write(1) # type = value
|
||||||
|
ret = self._read(1)
|
||||||
|
if ret != 0x01:
|
||||||
|
print("Error in DMG_SET_BANK_CHANGE_CMD:", ret)
|
||||||
|
else:
|
||||||
|
self._write(0, wait=True)
|
||||||
|
else:
|
||||||
|
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
|
||||||
|
self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
|
||||||
|
self._write(0, wait=True)
|
||||||
else:
|
else:
|
||||||
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
|
if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
||||||
|
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
||||||
|
else:
|
||||||
|
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
|
||||||
|
|
||||||
# ↑↑↑ Load commands into firmware
|
# ↑↑↑ Load commands into firmware
|
||||||
|
|
||||||
# ↓↓↓ Unlock cartridge
|
# ↓↓↓ Unlock cartridge
|
||||||
|
|
@ -2531,6 +2740,7 @@ class GbxDevice:
|
||||||
cancel_args = {"action":"ABORT", "abortable":False}
|
cancel_args = {"action":"ABORT", "abortable":False}
|
||||||
cancel_args.update(self.CANCEL_ARGS)
|
cancel_args.update(self.CANCEL_ARGS)
|
||||||
self.CANCEL_ARGS = {}
|
self.CANCEL_ARGS = {}
|
||||||
|
self.ERROR_ARGS = {}
|
||||||
self.SetProgress(cancel_args)
|
self.SetProgress(cancel_args)
|
||||||
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
||||||
return
|
return
|
||||||
|
|
@ -2560,6 +2770,8 @@ class GbxDevice:
|
||||||
self._cart_write(pos + buffer_len - 1, 0xF0)
|
self._cart_write(pos + buffer_len - 1, 0xF0)
|
||||||
elif command_set_type == "DMG-MBC5-32M-FLASH":
|
elif command_set_type == "DMG-MBC5-32M-FLASH":
|
||||||
status = self.WriteROM_DMG_MBC5_32M_FLASH(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
status = self.WriteROM_DMG_MBC5_32M_FLASH(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
||||||
|
elif command_set_type == "BLAZE_XPLODER":
|
||||||
|
status = self.WriteROM_DMG_EEPROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
||||||
else:
|
else:
|
||||||
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble)
|
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble)
|
||||||
if status is False:
|
if status is False:
|
||||||
|
|
@ -2606,8 +2818,14 @@ class GbxDevice:
|
||||||
verify_args.update({"verify_write":data_import, "rom_size":len(data_import), "path":"", "rtc_area":flashcart.HasRTC()})
|
verify_args.update({"verify_write":data_import, "rom_size":len(data_import), "path":"", "rtc_area":flashcart.HasRTC()})
|
||||||
self.ReadROM(0, 4) # dummy read
|
self.ReadROM(0, 4) # dummy read
|
||||||
verified_size = self._BackupROM(verify_args)
|
verified_size = self._BackupROM(verify_args)
|
||||||
if self.CANCEL is True:
|
if self.CANCEL:
|
||||||
pass
|
cancel_args = {"action":"ABORT", "abortable":False}
|
||||||
|
cancel_args.update(self.CANCEL_ARGS)
|
||||||
|
self.CANCEL_ARGS = {}
|
||||||
|
self.ERROR_ARGS = {}
|
||||||
|
self.SetProgress(cancel_args)
|
||||||
|
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
||||||
|
return
|
||||||
elif (verified_size is not True) and (len(data_import) != verified_size):
|
elif (verified_size is not True) and (len(data_import) != verified_size):
|
||||||
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The ROM was written completely, but verification of written data failed at address 0x{:X}.\n\nPlease make sure that the correct cart type was selected, that your cartridge actually does support ROMs that are {:s} in size and that the cartridge contacts are clean, then re-connect the device and try again from the beginning.".format(verified_size, Util.formatFileSize(len(data_import))), "abortable":False})
|
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The ROM was written completely, but verification of written data failed at address 0x{:X}.\n\nPlease make sure that the correct cart type was selected, that your cartridge actually does support ROMs that are {:s} in size and that the cartridge contacts are clean, then re-connect the device and try again from the beginning.".format(verified_size, Util.formatFileSize(len(data_import))), "abortable":False})
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class GbxDevice:
|
||||||
DEVICE_NAME = "GBxCart RW"
|
DEVICE_NAME = "GBxCart RW"
|
||||||
DEVICE_MIN_FW = 26
|
DEVICE_MIN_FW = 26
|
||||||
DEVICE_MAX_FW = 30
|
DEVICE_MAX_FW = 30
|
||||||
|
|
||||||
DEVICE_CMD = {
|
DEVICE_CMD = {
|
||||||
"CART_MODE":'C',
|
"CART_MODE":'C',
|
||||||
"GB_MODE":1,
|
"GB_MODE":1,
|
||||||
|
|
@ -119,7 +119,7 @@ class GbxDevice:
|
||||||
"AUDIO_HIGH":'8',
|
"AUDIO_HIGH":'8',
|
||||||
"AUDIO_LOW":'9',
|
"AUDIO_LOW":'9',
|
||||||
}
|
}
|
||||||
PCB_VERSIONS = {1:'v1.0', 2:'v1.1/v1.2', 4:'v1.3', 90:'XMAS', 100:'Mini v1.0', 101:'Mini v1.0d'}
|
PCB_VERSIONS = {2:'v1.1/v1.2', 4:'v1.3', 90:'XMAS v1.0', 100:'Mini v1.0', 101:'Mini v1.0d'}
|
||||||
SUPPORTED_CARTS = {}
|
SUPPORTED_CARTS = {}
|
||||||
|
|
||||||
FW = []
|
FW = []
|
||||||
|
|
@ -185,15 +185,14 @@ class GbxDevice:
|
||||||
self.DEVICE = None
|
self.DEVICE = None
|
||||||
conn_msg.append([0, "Couldn’t communicate with the GBxCart RW device on port " + ports[i] + ". Please disconnect and reconnect the device, then try again."])
|
conn_msg.append([0, "Couldn’t communicate with the GBxCart RW device on port " + ports[i] + ". Please disconnect and reconnect the device, then try again."])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.FW[0] == 0:
|
if self.FW[0] == 0:
|
||||||
dev.close()
|
dev.close()
|
||||||
self.DEVICE = None
|
self.DEVICE = None
|
||||||
return False
|
return False
|
||||||
elif (self.FW[1] == 4 and self.FW[0] < self.DEVICE_MAX_FW) or (self.FW[0] < self.DEVICE_MIN_FW):
|
elif (self.FW[1] == 100 and self.FW[0] < 26):
|
||||||
#dev.close()
|
self.FW_UPDATE_REQ = True
|
||||||
#self.DEVICE = None
|
elif (self.FW[1] in (2, 4, 90) and self.FW[0] < self.DEVICE_MAX_FW) or (self.FW[0] < self.DEVICE_MIN_FW):
|
||||||
#conn_msg.append([3, "The GBxCart RW device on port " + ports[i] + " requires a firmware update to work with this software. Please try again after updating it to version R" + str(self.DEVICE_MIN_FW) + " or higher.<br><br>Firmware updates are available at <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>."])
|
|
||||||
#continue
|
|
||||||
self.FW_UPDATE_REQ = True
|
self.FW_UPDATE_REQ = True
|
||||||
elif self.FW[0] < self.DEVICE_MAX_FW:
|
elif self.FW[0] < self.DEVICE_MAX_FW:
|
||||||
#conn_msg.append([1, "The GBxCart RW device on port " + ports[i] + " is running an older firmware version. Please consider updating to version R" + str(self.DEVICE_MAX_FW) + " to make use of the latest features.<br><br>Firmware updates are available at <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>."])
|
#conn_msg.append([1, "The GBxCart RW device on port " + ports[i] + " is running an older firmware version. Please consider updating to version R" + str(self.DEVICE_MAX_FW) + " to make use of the latest features.<br><br>Firmware updates are available at <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>."])
|
||||||
|
|
@ -214,7 +213,7 @@ class GbxDevice:
|
||||||
|
|
||||||
conn_msg.append([0, "For help please visit the insideGadgets Discord: https://gbxcart.com/discord"])
|
conn_msg.append([0, "For help please visit the insideGadgets Discord: https://gbxcart.com/discord"])
|
||||||
if (self.FW[1] not in (4, 5)):
|
if (self.FW[1] not in (4, 5)):
|
||||||
conn_msg.append([0, "\nNow running in Legacy Mode.\nThis version of FlashGBX was developed to be used with GBxCart RW v1.3 and v1.4.\nOther revisions are untested and may not be fully compatible.\n"])
|
conn_msg.append([0, "\nNow running in Legacy Mode.\nThis version of FlashGBX has been tested with GBxCart RW v1.3 and v1.4.\nOther revisions may not be fully compatible.\n"])
|
||||||
elif self.FW[1] == 4:
|
elif self.FW[1] == 4:
|
||||||
conn_msg.append([0, "{:s}Now running in Legacy Mode. You can install the optimized firmware version L1 in GUI mode from the “Tools” menu.{:s}".format(ANSI.YELLOW, ANSI.RESET)])
|
conn_msg.append([0, "{:s}Now running in Legacy Mode. You can install the optimized firmware version L1 in GUI mode from the “Tools” menu.{:s}".format(ANSI.YELLOW, ANSI.RESET)])
|
||||||
|
|
||||||
|
|
@ -230,10 +229,9 @@ class GbxDevice:
|
||||||
except SerialException as e:
|
except SerialException as e:
|
||||||
if "Permission" in str(e):
|
if "Permission" in str(e):
|
||||||
conn_msg.append([3, "The GBxCart RW device on port " + ports[i] + " couldn’t be accessed. Make sure your user account has permission to use it and it’s not already in use by another application."])
|
conn_msg.append([3, "The GBxCart RW device on port " + ports[i] + " couldn’t be accessed. Make sure your user account has permission to use it and it’s not already in use by another application."])
|
||||||
print(str(e))
|
|
||||||
else:
|
else:
|
||||||
conn_msg.append([3, "A critical error occured while trying to access the GBxCart RW device on port " + ports[i] + ".\n\n" + str(e)])
|
conn_msg.append([3, "A critical error occured while trying to access the GBxCart RW device on port " + ports[i] + ".\n\n" + str(e)])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return conn_msg
|
return conn_msg
|
||||||
|
|
||||||
|
|
@ -290,18 +288,24 @@ class GbxDevice:
|
||||||
def GetFullNameExtended(self, more=False):
|
def GetFullNameExtended(self, more=False):
|
||||||
return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), str(self.PORT))
|
return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), str(self.PORT))
|
||||||
|
|
||||||
|
def GetOfficialWebsite(self):
|
||||||
|
return "https://www.gbxcart.com/"
|
||||||
|
|
||||||
def SupportsFirmwareUpdates(self):
|
def SupportsFirmwareUpdates(self):
|
||||||
_, pcb = self.FW
|
fw, pcb = self.FW
|
||||||
if pcb != 4: return False
|
#return pcb == 4
|
||||||
return True
|
return (pcb in (2, 4, 90, 100) and fw > 3)
|
||||||
|
|
||||||
def FirmwareUpdateAvailable(self):
|
def FirmwareUpdateAvailable(self):
|
||||||
fw, pcb = self.FW
|
fw, pcb = self.FW
|
||||||
if pcb != 4: return False
|
#if pcb != 4: return False
|
||||||
|
if pcb not in (2, 4, 90, 100) and fw <= 3: return False
|
||||||
|
if (self.FW[1] == 100 and self.FW[0] >= 26): return False
|
||||||
return fw < self.DEVICE_MAX_FW
|
return fw < self.DEVICE_MAX_FW
|
||||||
|
|
||||||
def GetFirmwareUpdaterClass(self):
|
def GetFirmwareUpdaterClass(self):
|
||||||
if self.FW[1] == 4: # v1.3
|
_, pcb = self.FW
|
||||||
|
if pcb in (2, 4, 90, 100):
|
||||||
try:
|
try:
|
||||||
from . import fw_GBxCartRW_v1_3
|
from . import fw_GBxCartRW_v1_3
|
||||||
return (None, fw_GBxCartRW_v1_3.FirmwareUpdaterWindow)
|
return (None, fw_GBxCartRW_v1_3.FirmwareUpdaterWindow)
|
||||||
|
|
@ -1449,6 +1453,20 @@ class GbxDevice:
|
||||||
|
|
||||||
self.INFO["file_crc32"] = zlib.crc32(data_dump) & 0xFFFFFFFF
|
self.INFO["file_crc32"] = zlib.crc32(data_dump) & 0xFFFFFFFF
|
||||||
self.INFO["file_sha1"] = hashlib.sha1(data_dump).hexdigest()
|
self.INFO["file_sha1"] = hashlib.sha1(data_dump).hexdigest()
|
||||||
|
#self.INFO["file_sha256"] = hashlib.sha256(buffer).hexdigest()
|
||||||
|
#self.INFO["file_md5"] = hashlib.md5(buffer).hexdigest()
|
||||||
|
|
||||||
|
# Check for ROM loops
|
||||||
|
self.INFO["loop_detected"] = False
|
||||||
|
temp = min(0x2000000, len(data_dump))
|
||||||
|
while temp > 0x4000:
|
||||||
|
temp = temp >> 1
|
||||||
|
if (data_dump[0:0x4000] == data_dump[temp:temp+0x4000]):
|
||||||
|
if data_dump[0:temp] == data_dump[temp:temp*2]:
|
||||||
|
self.INFO["loop_detected"] = temp
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
self.SetProgress({"action":"FINISHED"})
|
self.SetProgress({"action":"FINISHED"})
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
FlashGBX/res/fw_GBxCart_RW_Mini_v1_0.zip
Normal file
BIN
FlashGBX/res/fw_GBxCart_RW_Mini_v1_0.zip
Normal file
Binary file not shown.
BIN
FlashGBX/res/fw_GBxCart_RW_XMAS_v1_0.zip
Normal file
BIN
FlashGBX/res/fw_GBxCart_RW_XMAS_v1_0.zip
Normal file
Binary file not shown.
BIN
FlashGBX/res/fw_GBxCart_RW_v1_1_v1_2.zip
Normal file
BIN
FlashGBX/res/fw_GBxCart_RW_v1_1_v1_2.zip
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
19
README.md
19
README.md
|
|
@ -53,7 +53,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
*To run FlashGBX in portable mode without installing, you can also download the source code archive and call `python3 run.py` after installing the prerequisites yourself.*
|
*To run FlashGBX in portable mode without installing, you can also download the source code archive and call `python3 run.py` after installing the prerequisites yourself.*
|
||||||
|
|
||||||
## Cartridge Compatibility
|
## Cartridge Compatibility
|
||||||
### Supported official cartridge memory mappers
|
### Supported cartridge memory mappers
|
||||||
- Game Boy
|
- Game Boy
|
||||||
- All cartridges without memory mapping
|
- All cartridges without memory mapping
|
||||||
- MBC1
|
- MBC1
|
||||||
|
|
@ -71,17 +71,23 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
- HuC-1
|
- HuC-1
|
||||||
- HuC-3
|
- HuC-3
|
||||||
- TAMA5
|
- TAMA5
|
||||||
|
- Unlicensed 256M Mapper
|
||||||
|
- Unlicesned Wisdom Tree Mapper
|
||||||
|
- Unlicensed Xploder GB Mapper
|
||||||
|
- Unlicensed Sachen Mapper
|
||||||
|
|
||||||
- Game Boy Advance
|
- Game Boy Advance
|
||||||
- All cartridges without memory mapping
|
- All cartridges without memory mapping
|
||||||
- 8M FLASH DACS
|
- 8M FLASH DACS
|
||||||
- 3D Memory (GBA Video)
|
- 3D Memory (GBA Video)
|
||||||
|
- Unlicensed 2048M Mapper
|
||||||
|
|
||||||
### Currently supported flash cartridges
|
### Currently supported flash cartridges
|
||||||
|
|
||||||
- Game Boy
|
- Game Boy
|
||||||
|
|
||||||
- 29LV Series Flash BOY with 29LV160DB
|
- 29LV Series Flash BOY with 29LV160DB
|
||||||
|
- BLAZE Xploder GB
|
||||||
- BUNG Doctor GB Card 64M
|
- BUNG Doctor GB Card 64M
|
||||||
- DIY cart with AM29F010
|
- DIY cart with AM29F010
|
||||||
- DIY cart with AM29F016
|
- DIY cart with AM29F016
|
||||||
|
|
@ -97,6 +103,8 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
- GB Smart 32M
|
- GB Smart 32M
|
||||||
- HDR Game Boy Camera Flashcart
|
- HDR Game Boy Camera Flashcart
|
||||||
- insideGadgets 32 KB
|
- insideGadgets 32 KB
|
||||||
|
- insideGadgets 128 KB
|
||||||
|
- insideGadgets 256 KB
|
||||||
- insideGadgets 512 KB
|
- insideGadgets 512 KB
|
||||||
- insideGadgets 1 MB, 128 KB SRAM
|
- insideGadgets 1 MB, 128 KB SRAM
|
||||||
- insideGadgets 2 MB, 128 KB SRAM/32 KB FRAM
|
- insideGadgets 2 MB, 128 KB SRAM/32 KB FRAM
|
||||||
|
|
@ -153,6 +161,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
- SD007_BV5_V3 with HY29LV160BT-70
|
- SD007_BV5_V3 with HY29LV160BT-70
|
||||||
- SD007_BV5_V3 with AM29LV160MB
|
- SD007_BV5_V3 with AM29LV160MB
|
||||||
- SD007_K8D3216_32M with MX29LV160CT
|
- SD007_K8D3216_32M with MX29LV160CT
|
||||||
|
- SD007_T40_64BALL_TSOP28 with 29LV016T
|
||||||
- SD007_T40_64BALL_TSOP28 with TC58FVB016FT-85¹
|
- SD007_T40_64BALL_TSOP28 with TC58FVB016FT-85¹
|
||||||
- SD007_TSOP_29LV017D with L017D70VC
|
- SD007_TSOP_29LV017D with L017D70VC
|
||||||
- SD007_TSOP_29LV017D with S29GL032M90T
|
- SD007_TSOP_29LV017D with S29GL032M90T
|
||||||
|
|
@ -197,6 +206,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
- 4455_4400_4000_4350_36L0R_V3 with M36L0R7050T
|
- 4455_4400_4000_4350_36L0R_V3 with M36L0R7050T
|
||||||
- AA1030_TSOP88BALL with M36W0R603
|
- AA1030_TSOP88BALL with M36W0R603
|
||||||
- AGB-E05-01 with GL128S
|
- AGB-E05-01 with GL128S
|
||||||
|
- AGB-E05-01 with MSP55LV100G
|
||||||
- AGB-E05-01 with MSP55LV128M
|
- AGB-E05-01 with MSP55LV128M
|
||||||
- AGB-E05-01 with MX29GL128FHT2I-90G
|
- AGB-E05-01 with MX29GL128FHT2I-90G
|
||||||
- AGB-E05-01 with S29GL064
|
- AGB-E05-01 with S29GL064
|
||||||
|
|
@ -204,6 +214,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
- AGB-E05-02 with M29W128FH
|
- AGB-E05-02 with M29W128FH
|
||||||
- AGB-E05-02 with M29W128GH
|
- AGB-E05-02 with M29W128GH
|
||||||
- AGB-E05-02 with S29GL032
|
- AGB-E05-02 with S29GL032
|
||||||
|
- AGB-E05-03H with M29W128GH
|
||||||
- AGB-E05-06L with 29LV128DBT2C-90Q
|
- AGB-E05-06L with 29LV128DBT2C-90Q
|
||||||
- AGB-E08-09 with 29LV128DTMC-90Q
|
- AGB-E08-09 with 29LV128DTMC-90Q
|
||||||
- AGB-E20-30 with M29W128GH
|
- AGB-E20-30 with M29W128GH
|
||||||
|
|
@ -218,6 +229,7 @@ Use this command in a Terminal or Command Prompt window to launch the installed
|
||||||
- BX2006_TSOP_64BALL with GL256S
|
- BX2006_TSOP_64BALL with GL256S
|
||||||
- BX2006_TSOPBGA_0106 with M29W640GB6AZA6
|
- BX2006_TSOPBGA_0106 with M29W640GB6AZA6
|
||||||
- BX2006_TSOPBGA_0106 with K8D6316UTM-PI07
|
- BX2006_TSOPBGA_0106 with K8D6316UTM-PI07
|
||||||
|
- DV15 with MSP55LV100G
|
||||||
- GA-07 with unlabeled flash chip
|
- GA-07 with unlabeled flash chip
|
||||||
- GE28F128W30 with 128W30B0
|
- GE28F128W30 with 128W30B0
|
||||||
- M5M29G130AN (no PCB text)
|
- M5M29G130AN (no PCB text)
|
||||||
|
|
@ -282,7 +294,7 @@ The author would like to thank the following very kind people for their help and
|
||||||
- iamevn (flash chip info)
|
- iamevn (flash chip info)
|
||||||
- Icesythe7 (feature suggestions, testing, bug reports)
|
- Icesythe7 (feature suggestions, testing, bug reports)
|
||||||
- Jayro (flash chip info)
|
- Jayro (flash chip info)
|
||||||
- JFox (help with properly packaging the app for pip, Linux help)
|
- JFox (help with properly packaging the app for pip, Linux help, bug reports)
|
||||||
- joyrider3774 (flash chip info)
|
- joyrider3774 (flash chip info)
|
||||||
- JS7457 (flash chip info)
|
- JS7457 (flash chip info)
|
||||||
- julgr (macOS help, testing)
|
- julgr (macOS help, testing)
|
||||||
|
|
@ -308,14 +320,17 @@ The author would like to thank the following very kind people for their help and
|
||||||
- skite2001 (flash chip info)
|
- skite2001 (flash chip info)
|
||||||
- Smelly-Ghost (testing)
|
- Smelly-Ghost (testing)
|
||||||
- Super Maker (flash chip info, testing)
|
- Super Maker (flash chip info, testing)
|
||||||
|
- Tauwasser (research)
|
||||||
- t5b6_de (flash chip info)
|
- t5b6_de (flash chip info)
|
||||||
- Timville (flash chip info)
|
- Timville (flash chip info)
|
||||||
- twitnic (flash chip info)
|
- twitnic (flash chip info)
|
||||||
- Veund (flash chip info)
|
- Veund (flash chip info)
|
||||||
- Voultar (bug reports, feature suggestions)
|
- Voultar (bug reports, feature suggestions)
|
||||||
|
- Wkr (flash chip info)
|
||||||
- x7l7j8cc (flash chip info)
|
- x7l7j8cc (flash chip info)
|
||||||
- Zeii (flash chip info)
|
- Zeii (flash chip info)
|
||||||
- Zelante (flash chip info)
|
- Zelante (flash chip info)
|
||||||
|
- zvxr (flash chip info)
|
||||||
|
|
||||||
## DISCLAIMER
|
## DISCLAIMER
|
||||||
|
|
||||||
|
|
|
||||||
2
setup.py
2
setup.py
|
|
@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="FlashGBX",
|
name="FlashGBX",
|
||||||
version="3.7",
|
version="3.8",
|
||||||
author="Lesserkuma",
|
author="Lesserkuma",
|
||||||
description="Reads and writes Game Boy and Game Boy Advance cartridge data. Supported hardware: GBxCart RW v1.3 and v1.4 by insideGadgets.",
|
description="Reads and writes Game Boy and Game Boy Advance cartridge data. Supported hardware: GBxCart RW v1.3 and v1.4 by insideGadgets.",
|
||||||
url="https://github.com/lesserkuma/FlashGBX",
|
url="https://github.com/lesserkuma/FlashGBX",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user