FlashGBX 4.2__py3-none-any.whl → 4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
FlashGBX/LK_Device.py CHANGED
@@ -31,7 +31,7 @@ class LK_Device(ABC):
31
31
  "OFW_FW_VER":0x56,
32
32
  "OFW_PCB_VER":0x68,
33
33
  "OFW_USART_1_0M_SPEED":0x3C,
34
- "OFW_USART_1_7M_SPEED":0x3E,
34
+ "OFW_USART_1_5M_SPEED":0x3E,
35
35
  "OFW_CART_PWR_ON":0x2F,
36
36
  "OFW_CART_PWR_OFF":0x2E,
37
37
  "OFW_QUERY_CART_PWR":0x5D,
@@ -114,9 +114,10 @@ class LK_Device(ABC):
114
114
  "PULLUPS_ENABLED":[8, 0x0E],
115
115
  "AUTO_POWEROFF_ENABLED":[8, 0x0F],
116
116
  "AGB_IRQ_ENABLED":[8, 0x10],
117
+ "DMG_AUDIO_ENABLED":[8, 0x11],
117
118
  }
118
119
 
119
- ACTIONS = {"ROM_READ":1, "SAVE_READ":2, "SAVE_WRITE":3, "ROM_WRITE":4, "ROM_WRITE_VERIFY":4, "SAVE_WRITE_VERIFY":3, "RTC_WRITE":5}
120
+ ACTIONS = {"ROM_READ":1, "SAVE_READ":2, "SAVE_WRITE":3, "ROM_WRITE":4, "ROM_WRITE_VERIFY":4, "SAVE_WRITE_VERIFY":3, "RTC_WRITE":5, "DETECT_CART":6}
120
121
  SUPPORTED_CARTS = {}
121
122
 
122
123
  FW = {}
@@ -229,8 +230,14 @@ class LK_Device(ABC):
229
230
  #################################################################
230
231
 
231
232
  def IsSupportedMbc(self, mbc):
232
- return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x201, 0x202, 0x203, 0x204, 0x205 )
233
+ return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206 )
233
234
 
235
+ def IsUnregistered(self):
236
+ if "unregistered" in self.FW:
237
+ return self.FW["unregistered"]
238
+ else:
239
+ return False
240
+
234
241
  def TryConnect(self, port, baudrate):
235
242
  dprint("Trying to connect to {:s} at baud rate {:d} ({:s})".format(port, baudrate, type(self).__module__))
236
243
  try:
@@ -299,7 +306,7 @@ class LK_Device(ABC):
299
306
  for mode in flashcarts.keys():
300
307
  for key in sorted(flashcarts[mode].keys(), key=str.casefold):
301
308
  self.SUPPORTED_CARTS[mode][key] = flashcarts[mode][key]
302
-
309
+
303
310
  def IsConnected(self):
304
311
  if self.DEVICE is None: return False
305
312
  if not self.DEVICE.isOpen(): return False
@@ -635,11 +642,11 @@ class LK_Device(ABC):
635
642
  ret = self._read(1)
636
643
  if ret != 0x01:
637
644
  if ret is False:
638
- msg = "Error: No response while trying to write to the device."
645
+ msg = "Error: No response while trying to communicate with the device."
639
646
  time.sleep(0.5)
640
647
  self.DEVICE.reset_input_buffer()
641
648
  else:
642
- msg = "Error: Bad response “{:s}” while trying to write to the device.".format(str(ret))
649
+ msg = "Error: Bad response “{:s}” while trying to communicate with the device.".format(str(ret))
643
650
  print(f"{ANSI.RED}{msg}{ANSI.RESET}")
644
651
  dprint(msg)
645
652
  #self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A critical communication error occured during a write. Please avoid passive USB hubs, try different USB ports/cables and re-connect the device."})
@@ -673,6 +680,12 @@ class LK_Device(ABC):
673
680
  self.CartPowerCycle(delay=delay)
674
681
  return
675
682
 
683
+ if self.FW["fw_ver"] < 12:
684
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L12."})
685
+ self.CANCEL = True
686
+ self.USER_ANSWER = None
687
+ return False
688
+
676
689
  mode = self.GetMode()
677
690
  var_state = self.GetVarState()
678
691
 
@@ -741,7 +754,7 @@ class LK_Device(ABC):
741
754
  if self.DEVICE.in_waiting == 0:
742
755
  dprint("Waiting for ACK...")
743
756
  hp -= 1
744
- time.sleep(0.05)
757
+ time.sleep(0.1)
745
758
  continue
746
759
  temp = self.DEVICE.read(self.DEVICE.in_waiting)
747
760
  if len(temp) >= 1: temp = temp[len(temp) - 1]
@@ -751,6 +764,13 @@ class LK_Device(ABC):
751
764
  self._write(self.DEVICE_CMD["QUERY_CART_PWR"])
752
765
  time.sleep(0.05)
753
766
  hp -= 1
767
+
768
+ if hp == 0:
769
+ self.DEVICE.close()
770
+ self.DEVICE = None
771
+ self.ERROR = True
772
+ raise BrokenPipeError("Couldn’t power on the cartridge.")
773
+
754
774
  self.DEVICE.timeout = self.DEVICE_TIMEOUT
755
775
 
756
776
  self._write(self.DEVICE_CMD["QUERY_CART_PWR"])
@@ -823,6 +843,7 @@ class LK_Device(ABC):
823
843
  self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
824
844
  self._set_fw_variable("DMG_READ_METHOD", self.DMG_READ_METHOD)
825
845
  self._set_fw_variable("CART_MODE", 1)
846
+ #if self.FW["fw_ver"] >= 14: self._set_fw_variable("DMG_AUDIO_ENABLED", 0)
826
847
  self.MODE = "DMG"
827
848
  elif mode == "AGB":
828
849
  self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
@@ -854,31 +875,31 @@ class LK_Device(ABC):
854
875
  def GetSupportedCartridgesAGB(self):
855
876
  return (list(self.SUPPORTED_CARTS['AGB'].keys()), list(self.SUPPORTED_CARTS['AGB'].values()))
856
877
 
857
- def SetProgress(self, args):
878
+ def SetProgress(self, args, signal=None):
858
879
  if self.CANCEL and args["action"] not in ("ABORT", "FINISHED", "ERROR"): return
859
880
  if "pos" in args: self.POS = args["pos"]
860
881
  if args["action"] == "UPDATE_POS": self.INFO["transferred"] = args["pos"]
882
+ if signal is None:
883
+ signal = self.SIGNAL
884
+
861
885
  try:
862
- self.SIGNAL.emit(args)
886
+ signal.emit(args)
863
887
  except AttributeError:
864
- if self.SIGNAL is not None:
865
- self.SIGNAL(args)
888
+ if signal is not None:
889
+ signal(args)
866
890
 
867
891
  if args["action"] == "INITIALIZE":
868
892
  if self.CanPowerCycleCart(): self.CartPowerOn() # Ensure cart is powered
869
893
  self.POS = 0
870
894
  elif args["action"] == "FINISHED":
871
895
  self.POS = 0
896
+ signal = None
872
897
  self.SIGNAL = None
873
898
 
874
899
  def Debug(self):
875
- # for i in range(0, 0x100000):
876
- # print(hex(i), self._set_fw_variable("ADDRESS", i), end="\r", flush=True)
877
900
  return
878
901
 
879
902
  def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
880
- self.Debug()
881
-
882
903
  if not self.IsConnected(): raise ConnectionError("Couldn’t access the the device.")
883
904
  data = {}
884
905
  self.SIGNAL = None
@@ -905,7 +926,7 @@ class LK_Device(ABC):
905
926
  header = self.ReadROM(0, 0x180)
906
927
 
907
928
  if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
908
- with open("debug_header.bin", "wb") as f: f.write(header)
929
+ with open(Util.CONFIG_PATH + "/debug_header.bin", "wb") as f: f.write(header)
909
930
 
910
931
  # Parse ROM header
911
932
  if self.MODE == "DMG":
@@ -942,6 +963,7 @@ class LK_Device(ABC):
942
963
  data["rtc_string"] = _mbc.GetRTCString()
943
964
  except:
944
965
  data["rtc_string"] = "Invalid data"
966
+
945
967
  if _mbc.GetName() == "G-MMC1":
946
968
  try:
947
969
  temp = bytearray([0] * 0x100000)
@@ -959,6 +981,21 @@ class LK_Device(ABC):
959
981
  print(traceback.format_exc())
960
982
  print("{:s}An error occured while trying to read the hidden sector data of the NP GB-Memory cartridge.{:s}".format(ANSI.RED, ANSI.RESET))
961
983
 
984
+ elif _mbc.GetName() == "MAC-GBD":
985
+ dprint("Reading Game Boy Camera calibration data...")
986
+ _mbc.EnableRAM(True)
987
+ _mbc.SelectBankRAM(2)
988
+ temp = self.ReadRAM(address=0xFF2, length=0xE)
989
+ if temp != bytearray(temp[0] * len(temp)):
990
+ data["gbcamera_calibration1"] = temp
991
+ dprint("Game Boy Camera calibration data 1:", ''.join(format(x, '02X') for x in temp))
992
+ _mbc.SelectBankRAM(8)
993
+ temp = self.ReadRAM(address=0x1FF2, length=0xE)
994
+ if temp != bytearray(temp[0] * len(temp)):
995
+ data["gbcamera_calibration2"] = temp
996
+ dprint("Game Boy Camera calibration data 2:", ''.join(format(x, '02X') for x in temp))
997
+ _mbc.EnableRAM(False)
998
+
962
999
  elif self.MODE == "AGB":
963
1000
  # Unlock DACS carts on older firmware
964
1001
  if not self.CanPowerCycleCart() or self.FW["fw_ver"] == 1:
@@ -1067,9 +1104,22 @@ class LK_Device(ABC):
1067
1104
  if self.MODE == "DMG": #and setPinsAsInputs:
1068
1105
  self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"], wait=self.FW["fw_ver"] >= 12)
1069
1106
 
1107
+ self.Debug()
1070
1108
  return data
1071
1109
 
1072
- def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True):
1110
+ def _DetectCartridge(self, args): # Wrapper for thread call
1111
+ self.SetProgress({"action":"INITIALIZE", "abortable":False, "method":"DETECT_CART"})
1112
+ signal = self.SIGNAL
1113
+ self.SIGNAL = None
1114
+ ret = self.DoDetectCartridge(mbc=None, limitVoltage=args["limitVoltage"], checkSaveType=args["checkSaveType"], signal=signal)
1115
+ self.INFO["detect_cart"] = ret
1116
+ self.INFO["last_action"] = self.ACTIONS["DETECT_CART"]
1117
+ self.INFO["action"] = None
1118
+ self.SIGNAL = signal
1119
+ self.SetProgress({"action":"FINISHED"})
1120
+ return True
1121
+
1122
+ def DoDetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True, signal=None):
1073
1123
  self.SIGNAL = None
1074
1124
  self.CANCEL = False
1075
1125
  self.ERROR = False
@@ -1079,8 +1129,10 @@ class LK_Device(ABC):
1079
1129
  sram_unstable = None
1080
1130
  save_size = None
1081
1131
  checkBatterylessSRAM = False
1132
+ _apot = 0
1082
1133
 
1083
1134
  # Header
1135
+ if signal is not None: self.SetProgress({"action":"UPDATE_INFO", "text":"Detecting ROM..."}, signal=signal)
1084
1136
  info = self.ReadInfo(checkRtc=True)
1085
1137
  if self.MODE == "DMG" and mbc is None:
1086
1138
  mbc = info["mapper_raw"]
@@ -1088,23 +1140,27 @@ class LK_Device(ABC):
1088
1140
 
1089
1141
  # Disable Auto Power Off
1090
1142
  _apoe = False
1091
- if self.CanPowerCycleCart():
1092
- self.CartPowerCycle()
1093
- if self.FW["fw_ver"] >= 12:
1143
+ if self.FW["fw_ver"] >= 12:
1144
+ if self.CanPowerCycleCart():
1145
+ self.CartPowerCycle()
1094
1146
  _apoe = self._get_fw_variable("AUTO_POWEROFF_ENABLED") == 1
1095
1147
  if _apoe is True:
1096
1148
  _apot = self._get_fw_variable("AUTO_POWEROFF_TIME")
1097
1149
  self._set_fw_variable("AUTO_POWEROFF_TIME", 5000)
1098
1150
 
1099
1151
  # Detect Flash Cart
1152
+ if signal is not None: self.SetProgress({"action":"UPDATE_INFO", "text":"Detecting Flash..."}, signal=signal)
1100
1153
  ret = self.DetectFlash(limitVoltage=limitVoltage)
1101
1154
  if ret is False: return False
1102
1155
  (cart_types, cart_type_id, flash_id, cfi_s, cfi, detected_size) = ret
1103
1156
  supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
1104
1157
  cart_type = supported_carts[cart_type_id]
1105
1158
 
1159
+ # Skip DMG save type detection
1160
+ if self.MODE == "DMG" and cart_type_id == 0: checkSaveType = False
1161
+
1106
1162
  # Preparations
1107
- if "command_set" in cart_type and cart_type["command_set"] in ("DMG-MBC5-32M-FLASH", "GBAMP"):
1163
+ if ("command_set" in cart_type and cart_type["command_set"] in ("DMG-MBC5-32M-FLASH", "GBAMP")) or ("dmg-mbc5-32m-flash" in cart_type):
1108
1164
  checkSaveType = False
1109
1165
  elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
1110
1166
  save_size = 65536
@@ -1113,6 +1169,7 @@ class LK_Device(ABC):
1113
1169
  elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 1:
1114
1170
  checkSaveType = False
1115
1171
  elif self.MODE == "DMG" and "mbc" in cart_type and cart_type["mbc"] == 0x105: # G-MMC1
1172
+ if signal is not None: self.SetProgress({"action":"UPDATE_INFO", "text":"Detecting GB-Memory..."}, signal=signal)
1116
1173
  header = self.ReadROM(0, 0x180)
1117
1174
  data = RomFileDMG(header).GetHeader()
1118
1175
  _mbc = DMG_MBC().GetInstance(args={"mbc":cart_type["mbc"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
@@ -1131,6 +1188,7 @@ class LK_Device(ABC):
1131
1188
 
1132
1189
  # Save Type and Size
1133
1190
  if checkSaveType:
1191
+ if signal is not None: self.SetProgress({"action":"UPDATE_INFO", "text":"Detecting save type..."}, signal=signal)
1134
1192
  if self.MODE == "DMG":
1135
1193
  save_size = 131072
1136
1194
  save_type = 0x04
@@ -1148,6 +1206,8 @@ class LK_Device(ABC):
1148
1206
  args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':save_type, 'rtc':False, 'detect':True }
1149
1207
  elif self.MODE == "AGB":
1150
1208
  args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':8, 'rtc':False, 'detect':True }
1209
+ else:
1210
+ return
1151
1211
 
1152
1212
  ret = self._BackupRestoreRAM(args=args)
1153
1213
 
@@ -1175,6 +1235,10 @@ class LK_Device(ABC):
1175
1235
  if self.INFO["data"][i:i+3] != bytearray([self.INFO["data"][i]] * 3):
1176
1236
  check = False
1177
1237
  break
1238
+
1239
+ if self.INFO["data"][0:0x8000] == self.INFO["data"][0x8000:0x10000]: # MBCX
1240
+ check = True
1241
+
1178
1242
  if check:
1179
1243
  save_size = 32768
1180
1244
  save_type = 0x03
@@ -1201,13 +1265,21 @@ class LK_Device(ABC):
1201
1265
  if flash_save_id != 0 and flash_save_id in Util.AGB_Flash_Save_Chips:
1202
1266
  save_size = Util.AGB_Flash_Save_Chips_Sizes[list(Util.AGB_Flash_Save_Chips).index(flash_save_id)]
1203
1267
  save_chip = Util.AGB_Flash_Save_Chips[flash_save_id]
1204
- if save_size == 131072:
1268
+
1269
+ if flash_save_id in (0xBF5B, 0xFFFF): # Bootlegs
1270
+ if self.INFO["data"][0:0x20000] == bytearray([0xFF] * 0x20000):
1271
+ save_type = 5
1272
+ elif self.INFO["data"][0:0x10000] == self.INFO["data"][0x10000:0x20000]:
1273
+ save_type = 4
1274
+ else:
1275
+ save_type = 5
1276
+ elif save_size == 131072:
1205
1277
  save_type = 5
1206
1278
  elif save_size == 65536:
1207
1279
  save_type = 4
1208
1280
  except:
1209
1281
  pass
1210
-
1282
+
1211
1283
  if save_type is None:
1212
1284
  checkBatterylessSRAM = True
1213
1285
  if info["dacs_8m"] is True:
@@ -1238,8 +1310,8 @@ class LK_Device(ABC):
1238
1310
  save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
1239
1311
  eeprom_64k = self.INFO["data"]
1240
1312
  if eeprom_64k in (bytearray([0xFF] * len(eeprom_64k)), bytearray([0] * len(eeprom_64k))):
1241
- save_type = None
1242
1313
  save_size = 0
1314
+ save_type = 0
1243
1315
  elif (eeprom_4k == eeprom_64k[:len(eeprom_4k)]):
1244
1316
  save_type = 2
1245
1317
  save_size = 8192
@@ -1332,6 +1404,7 @@ class LK_Device(ABC):
1332
1404
 
1333
1405
  def ReadFlashSaveID(self):
1334
1406
  # Check if actually SRAM/FRAM
1407
+ dprint("Checking Flash ID of Save Memory")
1335
1408
  test1 = self._cart_read(0, 0x10, agb_save_flash=True)[4]
1336
1409
  self._cart_write_flash([[ 0x0004, test1 ^ 0xFF ]])
1337
1410
  test2 = self._cart_read(0, 0x10, agb_save_flash=True)[4]
@@ -1395,6 +1468,8 @@ class LK_Device(ABC):
1395
1468
  command = "DMG_CART_READ"
1396
1469
  elif self.MODE == "AGB":
1397
1470
  command = "AGB_CART_READ"
1471
+ else:
1472
+ raise NotImplementedError
1398
1473
 
1399
1474
  for n in range(0, num):
1400
1475
  self._write(self.DEVICE_CMD[command])
@@ -1687,7 +1762,6 @@ class LK_Device(ABC):
1687
1762
  self._set_fw_variable("ADDRESS", address)
1688
1763
  elif self.MODE == "AGB":
1689
1764
  self._set_fw_variable("ADDRESS", address >> 1)
1690
- # print("==>", hex(self._get_fw_variable("ADDRESS")), hex(self._get_fw_variable("TRANSFER_SIZE")), hex(self._get_fw_variable("BUFFER_SIZE")))
1691
1765
 
1692
1766
  skip_init = True
1693
1767
 
@@ -1791,9 +1865,6 @@ class LK_Device(ABC):
1791
1865
  self._write(buffer[i*length:i*length+length])
1792
1866
  ret = self._read(1)
1793
1867
  if ret not in (0x01, 0x03):
1794
- #self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Flash write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
1795
- #self.CANCEL = True
1796
- #self.ERROR = True
1797
1868
  return False
1798
1869
 
1799
1870
  self._cart_write(address + length - 1, 0xFF)
@@ -1936,7 +2007,7 @@ class LK_Device(ABC):
1936
2007
  buffer2 = self.ReadROM(0x80, 0x40)
1937
2008
  return buffer1 == buffer2
1938
2009
 
1939
- def CompareCRC32(self, buffer, offset, length, address, flashcart=None, max_length=0x20000, reset=False):
2010
+ def CompareCRC32(self, buffer, offset, length, address, flashcart=None, max_length=0x20000, reset=False, mbc=None, bank=0):
1940
2011
  left = length
1941
2012
  chunk_pos = 0
1942
2013
  verified = False
@@ -1944,7 +2015,7 @@ class LK_Device(ABC):
1944
2015
  chunk_len = max_length if left > max_length else left
1945
2016
  chunk_from = offset + chunk_pos
1946
2017
  chunk_to = offset + chunk_pos + chunk_len
1947
- dprint(f"Running CRC32 verification, comparing between source 0x{chunk_from:x}~0x{chunk_to:x} and target 0x{address+chunk_pos:x}~0x{address+chunk_pos+chunk_len:x}")
2018
+ dprint(f"Running CRC32 verification, comparing between source 0x{chunk_from:X}~0x{chunk_to:X} and target 0x{address+chunk_pos:X}~0x{address+chunk_pos+chunk_len:X}")
1948
2019
  crc32_expected = zlib.crc32(buffer[chunk_from:chunk_to])
1949
2020
 
1950
2021
  for i in range(0, 2 if (reset is True and flashcart is not None) else 1): # for retrying with reset
@@ -1962,6 +2033,8 @@ class LK_Device(ABC):
1962
2033
  if crc32_expected != crc32_calculated:
1963
2034
  if i == 0 and flashcart is not None:
1964
2035
  flashcart.Reset(full_reset=True)
2036
+ if mbc is not None:
2037
+ mbc.SelectBankROM(bank)
1965
2038
  verified = (crc32_expected, crc32_calculated)
1966
2039
  continue
1967
2040
  else:
@@ -2008,17 +2081,33 @@ class LK_Device(ABC):
2008
2081
  self.SetAGBReadMethod(0)
2009
2082
  self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
2010
2083
 
2084
+ else:
2085
+ raise NotImplementedError
2086
+
2011
2087
  rom = self._cart_read(0, 8)
2012
2088
 
2013
2089
  dprint("Resetting and unlocking all cart types")
2014
2090
  cmds = []
2015
2091
  cmds_reset = []
2016
2092
  cmds_unlock_read = []
2017
- for f in range(1, len(supported_carts)):
2018
- cart_type = supported_carts[f]
2093
+
2094
+ for cart_type in sorted(supported_carts, key=lambda c: 'm29w640' not in c): # m29w640 first because of corruption risk
2095
+ f = supported_carts.index(cart_type)
2019
2096
  if "command_set" not in cart_type: continue
2020
2097
  if "manual_select" in cart_type and cart_type["manual_select"] is True: continue
2021
- if self.MODE == "DMG" and cart_type["command_set"] == "BLAZE_XPLODER":
2098
+ if self.MODE == "DMG" and "m29w640" in cart_type:
2099
+ self._cart_write_flash(cart_type["commands"]["reset"])
2100
+ rom1 = self._cart_read(0, 8)
2101
+ self._cart_write_flash(cart_type["commands"]["read_identifier"])
2102
+ rom2 = self._cart_read(0, 8)
2103
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2104
+ found = True
2105
+ flash_types.append(f)
2106
+ dprint("Found a M29W640 cartridge")
2107
+ self._cart_write_flash(cart_type["commands"]["reset"])
2108
+ break
2109
+ continue
2110
+ elif self.MODE == "DMG" and cart_type["command_set"] == "BLAZE_XPLODER":
2022
2111
  self._cart_read(0x102, 1)
2023
2112
  self._cart_write(6, 1)
2024
2113
  self._cart_write_flash(cart_type["commands"]["reset"])
@@ -2059,7 +2148,7 @@ class LK_Device(ABC):
2059
2148
  self._cart_write_flash(cart_type["commands"]["reset"])
2060
2149
  break
2061
2150
  continue
2062
- elif self.MODE == "DMG" and cart_type["command_set"] == "DMG-MBC5-32M-FLASH":
2151
+ elif self.MODE == "DMG" and "dmg-mbc5-32m-flash" in cart_type:
2063
2152
  self._set_we_pin_audio()
2064
2153
  self._cart_write_flash(cart_type["commands"]["unlock"], flashcart=False)
2065
2154
  self._cart_write_flash(cart_type["commands"]["reset"])
@@ -2149,6 +2238,7 @@ class LK_Device(ABC):
2149
2238
  self._cart_write(cmd[0], cmd[1], flashcart=True)
2150
2239
 
2151
2240
  flash_id_cmds = sorted(cmds, key=lambda x: x['read_identifier'][0][0])
2241
+ read_cfi_cmds = []
2152
2242
 
2153
2243
  rom_s = " ".join(format(x, '02X') for x in rom)
2154
2244
  if self.MODE == "DMG":
@@ -2174,41 +2264,13 @@ class LK_Device(ABC):
2174
2264
  cmp = self._cart_read(0, 8)
2175
2265
  self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2176
2266
  if rom != cmp: # ROM data changed
2177
- if "read_cfi" not in flash_id_cmds[i]:
2178
- flash_id_cmds[i]["read_cfi"] = [[ flash_id_cmds[i]["read_identifier"][0][0], 0x98 ]]
2179
- self._cart_write_flash(flash_id_cmds[i]["read_cfi"], flashcart=True)
2180
- cfi_buffer = self._cart_read(0, 0x400)
2181
- self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2182
2267
  flash_id_methods.append([we - 1, i, list(cmp), cfi_buffer, flash_id_cmds[i]["read_identifier"]])
2183
-
2184
- if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2185
- with open("debug_cfi.bin", "wb") as f: f.write(cfi_buffer)
2186
- try:
2187
- magic = "{:s}{:s}{:s}".format(chr(cfi_buffer[0x20]), chr(cfi_buffer[0x22]), chr(cfi_buffer[0x24]))
2188
- d_swap = (0, 0)
2189
- if magic == "QRY": # D0D1 not swapped
2190
- pass
2191
- elif magic == "RQZ": # D0D1 swapped
2192
- d_swap = [(0, 1)]
2193
- for j2 in range(0, len(d_swap)):
2194
- for j in range(0, len(cfi_buffer)):
2195
- cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2196
- elif magic == "\x92\x91\x9A": # D0D1+D6D7 swapped
2197
- d_swap = [( 0, 1 ), ( 6, 7 )]
2198
- for j2 in range(0, len(d_swap)):
2199
- for j in range(0, len(cfi_buffer)):
2200
- cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2201
- if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2202
- with open("debug_cfi_d0d1+d6d7.bin", "wb") as f: f.write(cfi_buffer)
2203
- else:
2204
- cfi_buffer = None
2205
- except:
2206
- cfi_buffer = None
2207
-
2208
2268
  if self.MODE == "DMG":
2209
2269
  flash_id_s += "[{:s}/{:4X}/{:2X}] {:s}\n".format(we_pins[we-1].ljust(5), flash_id_cmds[i]["read_identifier"][0][0], flash_id_cmds[i]["read_identifier"][0][1], ' '.join(format(x, '02X') for x in cmp))
2210
2270
  else:
2211
2271
  flash_id_s += "[{:6X}/{:4X}] {:s}\n".format(flash_id_cmds[i]["read_identifier"][0][0], flash_id_cmds[i]["read_identifier"][0][1], ' '.join(format(x, '02X') for x in cmp))
2272
+ flash_id_cmds[i]["read_cfi"] = [ flash_id_cmds[i]["read_identifier"][0][0], 0x98 ]
2273
+ read_cfi_cmds.append(flash_id_cmds[i]["read_cfi"])
2212
2274
 
2213
2275
  dprint(f"Found {len(flash_id_methods):d} result(s)")
2214
2276
  self._cart_write_flash([[0, 0xFF]], True)
@@ -2242,10 +2304,55 @@ class LK_Device(ABC):
2242
2304
 
2243
2305
  dprint("Compatible flash types:", [(index, supported_carts[index]["names"][0]) for index in flash_types])
2244
2306
 
2307
+ try:
2308
+ if len(flash_types) > 0 and "read_cfi" in supported_carts[flash_types[0]]["commands"]:
2309
+ read_cfi_cmd = supported_carts[flash_types[0]]["commands"]["read_cfi"]
2310
+ reset_cmd = supported_carts[flash_types[0]]["commands"]["reset"]
2311
+ else:
2312
+ if len(read_cfi_cmds) == 0:
2313
+ read_cfi_cmd = [ [0, 0x98] ]
2314
+ else:
2315
+ read_cfi_cmd = [ read_cfi_cmds[0] ]
2316
+ reset_cmd = flash_id_cmds[0]["reset"]
2317
+ self._cart_write_flash(read_cfi_cmd, flashcart=(self.MODE == "AGB"))
2318
+ cfi_buffer = self._cart_read(0, 0x400)
2319
+ self._cart_write_flash(reset_cmd, flashcart=(self.MODE == "AGB"))
2320
+
2321
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2322
+ with open(Util.CONFIG_PATH + "/debug_cfi.bin", "wb") as f: f.write(cfi_buffer)
2323
+
2324
+ found = False
2325
+ for o in ((0x20, 2), (0x10, 1)):
2326
+ magic = "{:s}{:s}{:s}".format(chr(cfi_buffer[o[0]]), chr(cfi_buffer[o[0] + (1 * o[1])]), chr(cfi_buffer[o[0] + (2 * o[1])]))
2327
+ dprint("CFI magic:", hex(o[0]), hex(o[0] + (1 * o[1])), hex(o[0] + (2 * o[1])), "=", str(magic))
2328
+ d_swap = (0, 0)
2329
+ if magic == "QRY": # D0D1 not swapped
2330
+ found = True
2331
+ elif magic == "RQZ": # D0D1 swapped
2332
+ d_swap = [(0, 1)]
2333
+ for j2 in range(0, len(d_swap)):
2334
+ for j in range(0, len(cfi_buffer)):
2335
+ cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2336
+ found = True
2337
+ elif magic == "\x92\x91\x9A": # D0D1+D6D7 swapped
2338
+ d_swap = [( 0, 1 ), ( 6, 7 )]
2339
+ for j2 in range(0, len(d_swap)):
2340
+ for j in range(0, len(cfi_buffer)):
2341
+ cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2342
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2343
+ with open(Util.CONFIG_PATH + "/debug_cfi_d0d1+d6d7.bin", "wb") as f: f.write(cfi_buffer)
2344
+ found = True
2345
+ if found: break
2346
+ if found is False:
2347
+ cfi_buffer = None
2348
+ except:
2349
+ cfi_buffer = None
2350
+
2245
2351
  if cfi_buffer is None or len(cfi_buffer) < 0x400:
2246
2352
  cfi = False
2247
2353
  else:
2248
2354
  cfi = ParseCFI(cfi_buffer)
2355
+
2249
2356
  if cfi is not False:
2250
2357
  cfi["raw"] = cfi_buffer
2251
2358
  s = ""
@@ -2281,6 +2388,8 @@ class LK_Device(ABC):
2281
2388
  supp_flash_types = self.GetSupportedCartridgesDMG()
2282
2389
  elif self.MODE == "AGB":
2283
2390
  supp_flash_types = self.GetSupportedCartridgesAGB()
2391
+ else:
2392
+ raise NotImplementedError
2284
2393
 
2285
2394
  if "flash_size" in supp_flash_types[1][flash_types[0]]:
2286
2395
  size = supp_flash_types[1][flash_types[0]]["flash_size"]
@@ -2381,6 +2490,9 @@ class LK_Device(ABC):
2381
2490
  def FlashROM(self, fncSetProgress=None, args=None):
2382
2491
  self.DoTransfer(4, fncSetProgress, args)
2383
2492
 
2493
+ def DetectCartridge(self, fncSetProgress=None, args=None):
2494
+ self.DoTransfer(5, fncSetProgress, args)
2495
+
2384
2496
  #################################################################
2385
2497
 
2386
2498
  def _BackupROM(self, args):
@@ -2416,8 +2528,8 @@ class LK_Device(ABC):
2416
2528
 
2417
2529
  # Firmware check L8
2418
2530
  if self.FW["fw_ver"] < 8 and flashcart and "enable_pullups" in cart_type:
2419
- self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8.", "abortable":False})
2420
- return False
2531
+ print("{:s}Note: This cartridge may not be fully compatible with your {:s} device running an old or legacy firmware version.{:s}".format(ANSI.YELLOW, self.GetName(), ANSI.RESET))
2532
+ del(cart_type["enable_pullups"])
2421
2533
  # Firmware check L8
2422
2534
 
2423
2535
  buffer_len = 0x4000
@@ -2477,9 +2589,6 @@ class LK_Device(ABC):
2477
2589
  self.INFO["dump_info"]["agb_read_method"] = "3D Memory"
2478
2590
 
2479
2591
  elif flashcart and "command_set" in cart_type and cart_type["command_set"] == "GBAMP":
2480
- # if not self.CanPowerCycleCart():
2481
- # self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is not compatible with your " + self.DEVICE_NAME + " hardware revision due to missing cartridge power cycling support.", "abortable":False})
2482
- # return False
2483
2592
  self.INFO["dump_info"]["agb_read_method"] = "GBA Movie Player"
2484
2593
  if not "verify_write" in args: self.CartPowerCycleOrAskReconnect()
2485
2594
  flashcart.Unlock()
@@ -2550,9 +2659,6 @@ class LK_Device(ABC):
2550
2659
  else:
2551
2660
  rom_banks = 1
2552
2661
  rom_bank_size = 0x2000000
2553
-
2554
- #if "header" in self.INFO["dump_info"] and "dacs_8m" in self.INFO["dump_info"]["header"] and self.INFO["dump_info"]["header"]["dacs_8m"] is True:
2555
- # self.SetAGBReadMethod(0)
2556
2662
 
2557
2663
  if self.ERROR: return
2558
2664
 
@@ -2577,9 +2683,12 @@ class LK_Device(ABC):
2577
2683
  else:
2578
2684
  self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
2579
2685
  dprint("Pullups disabled")
2686
+
2580
2687
  if self.FW["fw_ver"] >= 12:
2581
- if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
2582
- self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
2688
+ if self.MODE == "DMG":
2689
+ # Joey Jr bug workaround
2690
+ enable_pullup_wr = 2 if (("enable_pullup_wr" in cart_type and cart_type["enable_pullup_wr"] is True) or ("force_wr_pullup" in args and args["force_wr_pullup"] is True)) else 0
2691
+ self._set_fw_variable("PULLUPS_ENABLED", enable_pullup_wr)
2583
2692
 
2584
2693
  buffer = bytearray(size)
2585
2694
  max_length = self.MAX_BUFFER_READ
@@ -2603,6 +2712,8 @@ class LK_Device(ABC):
2603
2712
  end_bank = math.ceil((buffer_pos + args["verify_len"]) / rom_bank_size)
2604
2713
  rom_banks = end_bank
2605
2714
  elif "bl_offset" in args:
2715
+ if "bl_layout" in args and args["bl_layout"] in (1, 2):
2716
+ args["bl_size"] <<= 1
2606
2717
  buffer_pos = args["bl_offset"]
2607
2718
  start_address = buffer_pos
2608
2719
  end_address = args["bl_offset"] + args["bl_size"]
@@ -2707,17 +2818,22 @@ class LK_Device(ABC):
2707
2818
  elif lives < 20:
2708
2819
  lives = 20
2709
2820
 
2710
- if file is not None: file.write(temp)
2821
+ if file is not None:
2822
+ if "bl_layout" in args and args["bl_layout"] == 1:
2823
+ file.write(temp[0x0000:0x2000])
2824
+ elif "bl_layout" in args and args["bl_layout"] == 2:
2825
+ file.write(temp[0x2000:0x4000])
2826
+ else:
2827
+ file.write(temp)
2711
2828
  buffer[pos_total:pos_total+len(temp)] = temp
2712
2829
  pos_total += len(temp)
2713
2830
 
2714
2831
  if "verify_write" in args:
2715
- #if pos_total >= len(args["verify_write"]): break
2716
2832
  check = args["verify_write"][pos_total-len(temp):pos_total]
2717
2833
  if temp[:len(check)] != check:
2718
2834
  if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2719
2835
  dprint("Writing 0x{:X} bytes to debug_verify_0x{:X}.bin".format(len(temp), pos_total-len(temp)))
2720
- with open("debug_verify_0x{:X}.bin".format(pos_total-len(temp)), "ab") as f: f.write(temp)
2836
+ with open(Util.CONFIG_PATH + "/debug_verify_0x{:X}.bin".format(pos_total-len(temp)), "ab") as f: f.write(temp)
2721
2837
 
2722
2838
  for i in range(0, pos_total):
2723
2839
  if (i < len(args["verify_write"]) - 1) and (i < pos_total - 1) and args["verify_write"][i] != buffer[i]:
@@ -2818,7 +2934,7 @@ class LK_Device(ABC):
2818
2934
 
2819
2935
  # Check for ROM loops
2820
2936
  self.INFO["loop_detected"] = False
2821
- temp = min(0x2000000, len(buffer))
2937
+ temp = len(buffer)
2822
2938
  while temp > 0x4000:
2823
2939
  temp = temp >> 1
2824
2940
  if (buffer[0:0x4000] == buffer[temp:temp+0x4000]):
@@ -2861,6 +2977,8 @@ class LK_Device(ABC):
2861
2977
  _agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
2862
2978
  if _agb_gpio.HasRTC() is not True: return False
2863
2979
  ret = _agb_gpio.WriteRTCDict(args["rtc_dict"])
2980
+ else:
2981
+ raise NotImplementedError
2864
2982
  return ret
2865
2983
 
2866
2984
  def _BackupRestoreRAM(self, args):
@@ -2872,14 +2990,23 @@ class LK_Device(ABC):
2872
2990
  empty_data_byte = 0x00
2873
2991
  extra_size = 0
2874
2992
  audio_low = False
2993
+
2994
+ # Initialization
2995
+ ram_banks = 0
2996
+ buffer_len = 0
2997
+ sram_5 = 0
2998
+ temp = None
2875
2999
 
2876
3000
  cart_type = None
2877
3001
  if "cart_type" in args and args["cart_type"] >= 0:
2878
3002
  supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
2879
3003
  cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
3004
+
2880
3005
  if self.FW["fw_ver"] >= 12:
2881
- if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
2882
- self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
3006
+ if self.MODE == "DMG":
3007
+ # Joey Jr bug workaround
3008
+ enable_pullup_wr = 2 if (("enable_pullup_wr" in cart_type and cart_type["enable_pullup_wr"] is True) or ("force_wr_pullup" in args and args["force_wr_pullup"] is True)) else 0
3009
+ self._set_fw_variable("PULLUPS_ENABLED", enable_pullup_wr)
2883
3010
 
2884
3011
  self._set_fw_variable("STATUS_REGISTER_MASK", 0x80)
2885
3012
  self._set_fw_variable("STATUS_REGISTER_VALUE", 0x80)
@@ -2895,7 +3022,9 @@ class LK_Device(ABC):
2895
3022
  save_size = args["save_size"]
2896
3023
  else:
2897
3024
  save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(args["save_type"])]
3025
+
2898
3026
  ram_banks = _mbc.GetRAMBanks(save_size)
3027
+
2899
3028
  buffer_len = min(0x200, _mbc.GetRAMBankSize())
2900
3029
  self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
2901
3030
  self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
@@ -3052,9 +3181,13 @@ class LK_Device(ABC):
3052
3181
  if args["rtc"] is True:
3053
3182
  extra_size = 0x10
3054
3183
 
3184
+ action = None
3055
3185
  if args["mode"] == 2: # Backup
3056
3186
  action = "SAVE_READ"
3057
3187
  buffer = bytearray()
3188
+ if self.MODE == "DMG" and args["save_type"] == 0x204: # Unlicensed PHOTO!
3189
+ ram_banks = 16
3190
+
3058
3191
  elif args["mode"] == 3: # Restore
3059
3192
  action = "SAVE_WRITE"
3060
3193
  self.INFO["save_erase"] = args["erase"]
@@ -3062,6 +3195,9 @@ class LK_Device(ABC):
3062
3195
  buffer = bytearray([ empty_data_byte ] * save_size)
3063
3196
  if self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
3064
3197
  buffer[0] = 0x00
3198
+ elif self.MODE == "DMG" and _mbc.GetName() == "MAC-GBD":
3199
+ buffer[0x11B2:0x11D7] = bytearray.fromhex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D616769631115")
3200
+ buffer[0x11D7:0x11FC] = buffer[0x11B2:0x11D7]
3065
3201
  else:
3066
3202
  if args["path"] is None:
3067
3203
  if "buffer" in args:
@@ -3072,16 +3208,18 @@ class LK_Device(ABC):
3072
3208
  with open(args["path"], "rb") as f:
3073
3209
  buffer = bytearray(f.read())
3074
3210
 
3211
+ if self.MODE == "DMG" and args["save_type"] == 0x204: # Unlicensed PHOTO!
3212
+ ram_banks = 16
3213
+ if len(buffer) <= 0x20000:
3214
+ save_size = len(buffer)
3215
+ args["save_type"] = 0x04
3216
+
3075
3217
  # Fill too small file
3076
3218
  if not (self.MODE == "AGB" and args["save_type"] == 6): # Not DACS
3077
3219
  if args["mode"] == 3:
3078
3220
  while len(buffer) < save_size:
3079
3221
  buffer += bytearray(buffer)
3080
3222
 
3081
- #if self.MODE == "AGB" and "ereader" in self.INFO and self.INFO["ereader"] is True: # e-Reader
3082
- # buffer[0xFF80:0x10000] = bytearray([0] * 0x80)
3083
- # buffer[0x1FF80:0x20000] = bytearray([0] * 0x80)
3084
-
3085
3223
  # Main loop
3086
3224
  if not (args["mode"] == 2 and "verify_write" in args and args["verify_write"]):
3087
3225
  self.INFO["action"] = self.ACTIONS[action]
@@ -3132,10 +3270,6 @@ class LK_Device(ABC):
3132
3270
  else:
3133
3271
  dprint("Unknown bank switching method")
3134
3272
  time.sleep(0.05)
3135
-
3136
- # if "detect" in args and args["detect"] is True:
3137
- # start_address = end_address - 64
3138
- # buffer_len = 64
3139
3273
 
3140
3274
  max_length = 64
3141
3275
  dprint("start_address=0x{:X}, end_address=0x{:X}, buffer_len=0x{:X}, buffer_offset=0x{:X}".format(start_address, end_address, buffer_len, buffer_offset))
@@ -3193,7 +3327,6 @@ class LK_Device(ABC):
3193
3327
  continue
3194
3328
 
3195
3329
  if xe == 2 and in_temp[0] != in_temp[1]:
3196
- # print(in_temp[0], in_temp[1], sep="\n")
3197
3330
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Failed to read save data consistently. Please ensure that the cartridge contacts are clean.", "abortable":False})
3198
3331
  return False
3199
3332
 
@@ -3315,11 +3448,6 @@ class LK_Device(ABC):
3315
3448
 
3316
3449
  pos += buffer_len
3317
3450
  buffer_offset += buffer_len
3318
-
3319
- # if "detect" in args and args["detect"] is True:
3320
- # buffer += bytearray(end_address - 64)
3321
- # pos += end_address - 64
3322
- # buffer_offset += end_address - 64
3323
3451
 
3324
3452
  verified = False
3325
3453
  if args["mode"] == 2: # Backup
@@ -3355,6 +3483,13 @@ class LK_Device(ABC):
3355
3483
  if self.MODE == "AGB" and cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
3356
3484
  buffer[5] = sram_5
3357
3485
 
3486
+ # PHOTO! rolls
3487
+ if self.MODE == "DMG" and args["save_type"] == 0x204:
3488
+ for i in range(8, 64):
3489
+ _mbc.SelectBankROM(i)
3490
+ buffer += self.ReadROM(0x4000, 0x4000)
3491
+ self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)})
3492
+
3358
3493
  if args["path"] is not None:
3359
3494
  if self.MODE == "DMG" and _mbc.GetName() == "MBC2":
3360
3495
  for i in range(0, len(buffer)):
@@ -3381,11 +3516,21 @@ class LK_Device(ABC):
3381
3516
  advance = "rtc_advance" in args and args["rtc_advance"]
3382
3517
  self.SetProgress({"action":"UPDATE_RTC", "method":"write"})
3383
3518
  if self.MODE == "DMG" and args["rtc"] is True:
3519
+ if "erase" in args and args["erase"] is True:
3520
+ buffer += bytearray([0] * _mbc.GetRTCBufferSize())
3384
3521
  _mbc.WriteRTC(buffer[-_mbc.GetRTCBufferSize():], advance=advance)
3385
3522
  elif self.MODE == "AGB":
3523
+ if "erase" in args and args["erase"] is True:
3524
+ buffer += bytearray([0xFF] * 0x10)
3386
3525
  _agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
3387
3526
  _agb_gpio.WriteRTC(buffer[-0x10:], advance=advance)
3388
3527
 
3528
+ # PHOTO! rolls
3529
+ if self.MODE == "DMG" and args["save_type"] == 0x204 and "cart_type" in args and args["cart_type"] != -1:
3530
+ if len(buffer) > 0x20000:
3531
+ self._FlashROM({'buffer': buffer[0x20000:0x100000], 'path':"", 'cart_type': args["cart_type"], 'override_voltage': False, 'prefer_chip_erase': False, 'fast_read_mode': True, 'verify_write': False, 'fix_header': False, 'fix_bootlogo': False, 'mbc': 252, 'bl_offset': 0x20000, 'bl_size': 0xE0000, 'bl_layout': 0, 'bl_save': True, 'flash_offset': 0x20000, 'flash_size': 0xE0000, 'mode': 4, 'photo_mode': True})
3532
+ args["verify_write"] = False
3533
+
3389
3534
  self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer), "force_update":True})
3390
3535
 
3391
3536
  # ↓↓↓ Write verify
@@ -3467,7 +3612,15 @@ class LK_Device(ABC):
3467
3612
  return True
3468
3613
 
3469
3614
  def _FlashROM(self, args):
3615
+ # Initialization
3470
3616
  self.FAST_READ = True
3617
+ temp = None
3618
+ rom_bank_size = 0
3619
+ end_bank = 0
3620
+ pos_from = 0
3621
+ verify_len = 0
3622
+ enable_pullup_wr = False
3623
+
3471
3624
  if "buffer" in args:
3472
3625
  data_import = args["buffer"]
3473
3626
  else:
@@ -3479,6 +3632,20 @@ class LK_Device(ABC):
3479
3632
  if "start_addr" in args and args["start_addr"] > 0:
3480
3633
  data_import = bytearray(b'\xFF' * args["start_addr"]) + data_import
3481
3634
 
3635
+ # Batteryless SRAM
3636
+ if "bl_layout" in args and args["bl_layout"] in (1, 2):
3637
+ args["bl_size"] <<= 1
3638
+ args["flash_size"] <<= 1
3639
+ bl_data_import = bytearray(b'\xFF' * args["bl_size"])
3640
+
3641
+ if args["bl_layout"] == 1:
3642
+ for i in range(0, args["bl_size"] // 0x4000):
3643
+ bl_data_import[i*0x4000:i*0x4000+0x2000] = data_import[i*0x2000:i*0x2000+0x2000]
3644
+ elif args["bl_layout"] == 2:
3645
+ for i in range(0, args["bl_size"] // 0x4000):
3646
+ bl_data_import[i*0x4000+0x2000:i*0x4000+0x2000+0x2000] = data_import[i*0x2000:i*0x2000+0x2000]
3647
+ data_import = bl_data_import
3648
+
3482
3649
  # Pad data
3483
3650
  if len(data_import) > 0:
3484
3651
  if len(data_import) < 0x400:
@@ -3521,24 +3688,23 @@ class LK_Device(ABC):
3521
3688
 
3522
3689
  supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
3523
3690
  cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
3691
+ try:
3692
+ cart_name = cart_type["names"][0]
3693
+ except:
3694
+ cart_name = "Unknown"
3695
+
3524
3696
  if cart_type == "RETAIL": return False # Generic ROM Cartridge is not flashable
3525
3697
 
3526
- # Special carts
3527
- # if "power_cycle" in cart_type and cart_type["power_cycle"] is True and not self.CanPowerCycleCart():
3528
- # self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is not flashable using FlashGBX and your " + self.DEVICE_NAME + " hardware revision due to missing cartridge power cycling support.", "abortable":False})
3529
- # return False
3530
- # Special carts
3531
- # Firmware check L1
3698
+ # Firmware check L2
3532
3699
  if (cart_type["type"] == "DMG" and "write_pin" in cart_type and cart_type["write_pin"] == "WR+RESET" and self.FW["fw_ver"] < 2) or (self.FW["fw_ver"] < 2 and ("pulse_reset_after_write" in cart_type and cart_type["pulse_reset_after_write"] is True)):
3533
- #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is not supported by FlashGBX using your " + self.DEVICE_NAME + " hardware revision and/or firmware version.", "abortable":False})
3534
3700
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L2.", "abortable":False})
3535
3701
  return False
3536
- # Firmware check L1
3537
3702
  # Firmware check L2
3703
+ # Firmware check L3
3538
3704
  if (self.FW["fw_ver"] < 3 and ("command_set" in cart_type and cart_type["command_set"] == "SHARP") and ("buffer_write" in cart_type["commands"])):
3539
3705
  print("{:s}Note: Update your {:s} firmware to version L3 or higher for a better transfer rate with this cartridge.{:s}".format(ANSI.YELLOW, self.DEVICE_NAME, ANSI.RESET))
3540
3706
  del(cart_type["commands"]["buffer_write"])
3541
- # Firmware check L2
3707
+ # Firmware check L3
3542
3708
  # Firmware check L5
3543
3709
  if (self.FW["fw_ver"] < 5 and ("flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True)):
3544
3710
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L5.", "abortable":False})
@@ -3549,17 +3715,25 @@ class LK_Device(ABC):
3549
3715
  # Firmware check L5
3550
3716
  # Firmware check L8
3551
3717
  if (self.FW["fw_ver"] < 8 and "enable_pullups" in cart_type and cart_type["enable_pullups"] is True):
3552
- self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8.", "abortable":False})
3553
- return False
3718
+ print("{:s}Note: This cartridge may not be fully compatible with your {:s} device running an old or legacy firmware version.{:s}".format(ANSI.YELLOW, self.GetName(), ANSI.RESET))
3719
+ del(cart_type["enable_pullups"])
3554
3720
  # Firmware check L8
3555
3721
  # Firmware check L12
3556
3722
  if (self.FW["fw_ver"] < 12 and "set_irq_high" in cart_type and cart_type["set_irq_high"] is True):
3557
- self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L12.", "abortable":False})
3558
- return False
3723
+ print("{:s}Note: This cartridge may not be fully compatible with your {:s} device until updated to a newer firmware version.{:s}".format(ANSI.YELLOW, self.GetName(), ANSI.RESET))
3724
+ del(cart_type["set_irq_high"])
3559
3725
  if (self.FW["fw_ver"] < 12 and "status_register_mask" in cart_type):
3560
- self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L12.", "abortable":False})
3726
+ if self.FW["pcb_name"] in ("GBxCart RW", ""):
3727
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires a different firmware version. Please update your GBxCart RW firmware using the older FlashGBX v4.3 and try again.", "abortable":False})
3728
+ else:
3729
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L12.", "abortable":False})
3561
3730
  return False
3562
3731
  # Firmware check L12
3732
+ # Firmware check L14
3733
+ if (self.FW["fw_ver"] < 14 and "set_audio_high" in cart_type and cart_type["set_audio_high"] is True):
3734
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L14.", "abortable":False})
3735
+ return False
3736
+ # Firmware check L14
3563
3737
 
3564
3738
  # Ensure cart is powered
3565
3739
  if self.CanPowerCycleCart(): self.CartPowerOn()
@@ -3591,7 +3765,7 @@ class LK_Device(ABC):
3591
3765
  flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
3592
3766
 
3593
3767
  rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
3594
-
3768
+
3595
3769
  # ↓↓↓ Set Voltage
3596
3770
  if args["override_voltage"] is not False:
3597
3771
  if args["override_voltage"] == 5:
@@ -3603,20 +3777,22 @@ class LK_Device(ABC):
3603
3777
  elif flashcart.GetVoltage() == 5:
3604
3778
  self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
3605
3779
  # ↑↑↑ Set Voltage
3606
-
3780
+
3607
3781
  # ↓↓↓ Pad data for full chip erase on sector erase mode
3608
3782
  if not flashcart.SupportsChipErase() and flashcart.SupportsSectorErase() and args["prefer_chip_erase"]:
3609
3783
  print("{:s}Note: Chip erase mode is not supported for this flash cartridge type. Sector erase mode will be used.{:s}\n".format(ANSI.YELLOW, ANSI.RESET))
3610
- flash_size = flashcart.GetFlashSize()
3611
- if flash_size is not False and len(data_import) < flash_size:
3612
- # Pad with FF till the end (with MemoryError fix)
3613
- pad_len = flash_size - len(data_import)
3614
- while pad_len > 0x2000000:
3615
- data_import += bytearray([0xFF] * 0x2000000)
3616
- pad_len -= 0x2000000
3617
- data_import += bytearray([0xFF] * (flash_size - len(data_import)))
3784
+ # Pad data if the user wants to erase the entire cartridge
3785
+ if data_import == bytearray([0xFF] * len(data_import)):
3786
+ flash_size = flashcart.GetFlashSize()
3787
+ if flash_size is not False and len(data_import) < flash_size:
3788
+ # Pad with FF till the end (with MemoryError fix)
3789
+ pad_len = flash_size - len(data_import)
3790
+ while pad_len > 0x2000000:
3791
+ data_import += bytearray([0xFF] * 0x2000000)
3792
+ pad_len -= 0x2000000
3793
+ data_import += bytearray([0xFF] * (flash_size - len(data_import)))
3618
3794
  # ↑↑↑ Pad data for full chip erase on sector erase mode
3619
-
3795
+
3620
3796
  # ↓↓↓ Flashcart configuration
3621
3797
  if self.FW["fw_ver"] >= 8:
3622
3798
  if "enable_pullups" in cart_type:
@@ -3627,9 +3803,11 @@ class LK_Device(ABC):
3627
3803
  self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
3628
3804
  dprint("Pullups disabled")
3629
3805
  if self.FW["fw_ver"] >= 12:
3630
- if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
3631
- self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
3632
- if self.MODE == "AGB":
3806
+ if self.MODE == "DMG":
3807
+ # Joey Jr bug workaround
3808
+ enable_pullup_wr = 2 if (("enable_pullup_wr" in cart_type and cart_type["enable_pullup_wr"] is True) or ("force_wr_pullup" in args and args["force_wr_pullup"] is True)) else 0
3809
+ self._set_fw_variable("PULLUPS_ENABLED", enable_pullup_wr)
3810
+ elif self.MODE == "AGB":
3633
3811
  self._set_fw_variable("AGB_IRQ_ENABLED", 1 if "set_irq_high" in cart_type else 0)
3634
3812
 
3635
3813
  _mbc = None
@@ -3704,7 +3882,7 @@ class LK_Device(ABC):
3704
3882
  else:
3705
3883
  dprint("Hidden sector data:", args["buffer_map"])
3706
3884
  if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
3707
- with open("debug_mmsa_map.bin", "wb") as f: f.write(args["buffer_map"])
3885
+ with open(Util.CONFIG_PATH + "/debug_mmsa_map.bin", "wb") as f: f.write(args["buffer_map"])
3708
3886
  data_map_import = copy.copy(args["buffer_map"])
3709
3887
  data_map_import = bytearray(data_map_import)
3710
3888
  dprint("Hidden sector data loaded")
@@ -3716,17 +3894,13 @@ class LK_Device(ABC):
3716
3894
  temp = 0
3717
3895
  if command_set_type == "AMD":
3718
3896
  temp = 0x01
3719
- #self._write(0x01) # FLASH_COMMAND_SET_AMD
3720
- #self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
3721
3897
  dprint("Using AMD command set")
3722
3898
  elif command_set_type == "INTEL":
3723
3899
  temp = 0x02
3724
- #self._write(0x02) # FLASH_COMMAND_SET_INTEL
3725
3900
  self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
3726
3901
  dprint("Using Intel command set")
3727
3902
  elif command_set_type == "SHARP":
3728
3903
  temp = 0x02
3729
- #self._write(0x02) # FLASH_COMMAND_SET_INTEL
3730
3904
  self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 1)
3731
3905
  dprint("Using Sharp/Intel command set")
3732
3906
  elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
@@ -3867,6 +4041,8 @@ class LK_Device(ABC):
3867
4041
  _mbc.SelectBankROM(1)
3868
4042
  if we != 0x00:
3869
4043
  self._set_fw_variable("FLASH_WE_PIN", we)
4044
+ if self.FW["fw_ver"] >= 14 and "set_audio_high" in cart_type:
4045
+ self._set_fw_variable("DMG_AUDIO_ENABLED", 1 if "set_audio_high" in cart_type else 0)
3870
4046
  # ↑↑↑ Preparations
3871
4047
 
3872
4048
  # ↓↓↓ Read Flash ID
@@ -3893,8 +4069,6 @@ class LK_Device(ABC):
3893
4069
  if len(sector_offsets) > 0:
3894
4070
  flash_capacity = sector_offsets[-1][0] + sector_offsets[-1][1]
3895
4071
  if flash_capacity < len(data_import) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]):
3896
- #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"There are not enough flash sectors available to write this ROM. The maximum capacity is {:s}.".format(Util.formatFileSize(size=flash_capacity, asInt=False)), "abortable":False})
3897
- #return False
3898
4072
  sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size)
3899
4073
 
3900
4074
  sector_offsets_hash = base64.urlsafe_b64encode(hashlib.sha1(str(sector_offsets).encode("UTF-8")).digest()).decode("ASCII", "ignore")[:4]
@@ -3949,7 +4123,6 @@ class LK_Device(ABC):
3949
4123
  bl_sectors = []
3950
4124
  for sector in sector_offsets:
3951
4125
  if flash_offset > sector[0]: continue
3952
- #if len(bl_sectors) > 0 and (flash_offset + flash_size) < (sector[0] + sector[1]): break
3953
4126
  if (flash_offset + flash_size) < sector[0]: break
3954
4127
  bl_sectors.append(sector)
3955
4128
  write_sectors = bl_sectors
@@ -3963,7 +4136,7 @@ class LK_Device(ABC):
3963
4136
 
3964
4137
  dprint("Sectors to update:", write_sectors)
3965
4138
  # ↑↑↑ Read Sector Map
3966
-
4139
+
3967
4140
  # ↓↓↓ Chip erase
3968
4141
  chip_erase = False
3969
4142
  if flashcart.SupportsChipErase() and not flash_offset > 0:
@@ -3978,7 +4151,7 @@ class LK_Device(ABC):
3978
4151
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No erase method available.", "abortable":False})
3979
4152
  return False
3980
4153
  # ↑↑↑ Chip erase
3981
-
4154
+
3982
4155
  # ↓↓↓ Flash Write
3983
4156
  if chip_erase:
3984
4157
  write_sectors = [[ 0, len(data_import) ]]
@@ -3987,9 +4160,10 @@ class LK_Device(ABC):
3987
4160
  elif len(write_sectors) == 0:
3988
4161
  write_sectors = sector_offsets
3989
4162
 
3990
- self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE", "size":len(data_import), "flash_offset":flash_offset, "sector_count":len(write_sectors)})
4163
+ if not "photo_mode" in args:
4164
+ self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE", "size":len(data_import), "flash_offset":flash_offset, "sector_count":len(write_sectors)})
4165
+ self.INFO["action"] = self.ACTIONS["ROM_WRITE"]
3991
4166
  self.SetProgress({"action":"UPDATE_POS", "pos":flash_offset})
3992
- self.INFO["action"] = self.ACTIONS["ROM_WRITE"]
3993
4167
 
3994
4168
  if smallest_sector_size is not False:
3995
4169
  buffer_len = smallest_sector_size
@@ -4030,9 +4204,6 @@ class LK_Device(ABC):
4030
4204
  sector_pos = sector_offsets.index(sector[:2])
4031
4205
  start_bank = math.floor(buffer_pos / rom_bank_size)
4032
4206
  end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
4033
- # print(hex(start_address), hex(end_address), start_bank, end_bank)
4034
- # print(hex(sector_offsets[sector_pos][0]), sector_pos)
4035
- # print("")
4036
4207
  elif self.MODE == "DMG":
4037
4208
  dprint("Writing sector:", hex(sector[0]), hex(sector[1]))
4038
4209
  buffer_pos = sector[0]
@@ -4043,12 +4214,85 @@ class LK_Device(ABC):
4043
4214
  sector_pos = sector_offsets.index(sector[:2])
4044
4215
  start_bank = math.floor(buffer_pos / rom_bank_size)
4045
4216
  end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
4046
- # print(hex(start_address), hex(end_address), start_bank, end_bank)
4047
- # print(hex(sector_offsets[sector_pos][0]), sector_pos)
4048
- # print("")
4049
4217
 
4050
- #for bank in range(start_bank, end_bank):
4051
4218
  bank = start_bank
4219
+
4220
+ # ↓↓↓ Check if data matches already
4221
+ if not chip_erase and self.FW["fw_ver"] >= 12 and "compare_sectors" in args and args["compare_sectors"] is True and not (flashcart and cart_type["command_set"] == "GBAMP"):
4222
+ buffer_pos_matchcheck = buffer_pos
4223
+ verified = False
4224
+ ts_se_start = time.time()
4225
+ while bank < end_bank:
4226
+ status = None
4227
+ # ↓↓↓ Switch ROM bank
4228
+ if self.MODE == "DMG":
4229
+ if _mbc.ResetBeforeBankChange(bank) is True:
4230
+ dprint("Resetting the MBC")
4231
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4232
+ (start_address, bank_size) = _mbc.SelectBankROM(bank)
4233
+ if flashcart.PulseResetAfterWrite():
4234
+ if bank == 0:
4235
+ if self.FW["fw_ver"] < 2:
4236
+ self._write(self.DEVICE_CMD["OFW_GB_CART_MODE"])
4237
+ else:
4238
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4239
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4240
+ self._set_fw_variable("DMG_ROM_BANK", bank)
4241
+
4242
+ buffer_len = min(buffer_len, bank_size)
4243
+ if "start_addr" in flashcart.CONFIG and bank == 0: start_address = flashcart.CONFIG["start_addr"]
4244
+ end_address = start_address + bank_size
4245
+ start_address += (buffer_pos % rom_bank_size)
4246
+ if end_address > start_address + sector[1]:
4247
+ end_address = start_address + sector[1]
4248
+
4249
+ elif self.MODE == "AGB":
4250
+ if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
4251
+ if bank != current_bank:
4252
+ flashcart.Reset(full_reset=True)
4253
+ flashcart.SelectBankROM(bank)
4254
+ temp = end_address - start_address
4255
+ start_address %= cart_type["flash_bank_size"]
4256
+ end_address = min(cart_type["flash_bank_size"], start_address + temp)
4257
+ current_bank = bank
4258
+ # ↑↑↑ Switch ROM bank
4259
+
4260
+ pos = start_address
4261
+ #dprint("pos=0x{:X}, buffer_pos=0x{:X}, start_address=0x{:X}, end_address=0x{:X}".format(pos, buffer_pos_matchcheck, start_address, end_address))
4262
+ verified = self.CompareCRC32(buffer=data_import, offset=buffer_pos_matchcheck, length=end_address - pos, address=pos, flashcart=flashcart, reset=True, mbc=_mbc, bank=bank)
4263
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos_matchcheck})
4264
+ if verified is not True:
4265
+ break
4266
+ buffer_pos_matchcheck += (end_address - pos)
4267
+ bank += 1
4268
+
4269
+ if verified is True:
4270
+ dprint("Skipping sector #{:d}, because the CRC32 matched".format(sector_pos))
4271
+ if flashcart.FlashCommandsOnBank1(): _mbc.SelectBankROM(bank)
4272
+ self.NO_PROG_UPDATE = True
4273
+ se_ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos, skip=verified)
4274
+ self.NO_PROG_UPDATE = False
4275
+
4276
+ if self.CANCEL:
4277
+ cancel_args = {"action":"ABORT", "abortable":False}
4278
+ cancel_args.update(self.CANCEL_ARGS)
4279
+ self.CANCEL_ARGS = {}
4280
+ self.ERROR_ARGS = {}
4281
+ self.SetProgress(cancel_args)
4282
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
4283
+ return
4284
+
4285
+ ts_se_elapsed = time.time() - ts_se_start
4286
+ if se_ret:
4287
+ sector_size = se_ret
4288
+ dprint("Next sector size: 0x{:X}".format(sector_size))
4289
+ buffer_pos += sector_size
4290
+ continue
4291
+ else:
4292
+ verified = False
4293
+ bank = start_bank
4294
+ # ↑↑↑ Check if data matches already
4295
+
4052
4296
  while bank < end_bank:
4053
4297
  if self.CANCEL:
4054
4298
  cancel_args = {"action":"ABORT", "abortable":False}
@@ -4060,7 +4304,6 @@ class LK_Device(ABC):
4060
4304
  return
4061
4305
 
4062
4306
  status = None
4063
- #print("Bank:", bank, "...")
4064
4307
  # ↓↓↓ Switch ROM bank
4065
4308
  if self.MODE == "DMG":
4066
4309
  if _mbc.ResetBeforeBankChange(bank) is True:
@@ -4074,8 +4317,6 @@ class LK_Device(ABC):
4074
4317
  else:
4075
4318
  self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4076
4319
  self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4077
- # else:
4078
- # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4079
4320
  self._set_fw_variable("DMG_ROM_BANK", bank)
4080
4321
 
4081
4322
  buffer_len = min(buffer_len, bank_size)
@@ -4116,44 +4357,20 @@ class LK_Device(ABC):
4116
4357
  se_ret = None
4117
4358
  if chip_erase is False:
4118
4359
  if sector_pos < len(sector_offsets) and buffer_pos == sector_offsets[sector_pos][0]:
4119
- len_rest = end_address - pos
4120
- skip_se = False
4121
4360
  ts_se_start = time.time()
4122
- if self.FW["fw_ver"] >= 12 and "compare_sectors" in args and args["compare_sectors"] is True and sector[1] <= len_rest and not (flashcart and cart_type["command_set"] == "GBAMP"):
4123
- verified = self.CompareCRC32(buffer=data_import, offset=sector[0], length=sector[1], address=start_address, flashcart=flashcart, reset=True)
4124
- if verified is True:
4125
- skip_se = True
4126
- elif verified is not True and len(verified) == 2:
4127
- dprint("Writing sector #{:d}, because the CRC32 didn’t match: 0x{:X} ≠ 0x{:X}".format(sector_pos, verified[0], verified[1]))
4128
- verified = False
4129
- skip_se = False
4130
-
4131
- if self.CANCEL:
4132
- cancel_args = {"action":"ABORT", "abortable":False}
4133
- cancel_args.update(self.CANCEL_ARGS)
4134
- self.CANCEL_ARGS = {}
4135
- self.ERROR_ARGS = {}
4136
- self.SetProgress(cancel_args)
4137
- if self.CanPowerCycleCart(): self.CartPowerCycle()
4138
- return
4139
-
4140
- if skip_se is True:
4141
- dprint("Skipping sector #{:d}, because the CRC32 matched".format(sector_pos))
4142
- self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "sector_pos":sector_pos, "force_update":True})
4143
- else:
4144
- if sector not in verify_sectors:
4145
- verify_sectors.append(sector)
4146
- dprint("Erasing sector #{:d} at position 0x{:X} (0x{:X})".format(sector_pos, buffer_pos, pos))
4147
- self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "force_update":True})
4148
- if self.CanPowerCycleCart(): self.CartPowerOn()
4361
+ dprint("Erasing sector #{:d} at position 0x{:X} (0x{:X})".format(sector_pos, buffer_pos, pos))
4362
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "force_update":True})
4363
+ if self.CanPowerCycleCart(): self.CartPowerOn()
4149
4364
 
4150
4365
  sector_pos += 1
4151
4366
  if flashcart.FlashCommandsOnBank1(): _mbc.SelectBankROM(bank)
4152
4367
  self.NO_PROG_UPDATE = True
4153
- se_ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos, skip=skip_se)
4368
+ se_ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos, skip=False)
4154
4369
  self.NO_PROG_UPDATE = False
4155
4370
  if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
4156
4371
  continue
4372
+ if sector not in verify_sectors:
4373
+ verify_sectors.append(sector)
4157
4374
 
4158
4375
  ts_se_elapsed = time.time() - ts_se_start
4159
4376
  if se_ret:
@@ -4161,11 +4378,6 @@ class LK_Device(ABC):
4161
4378
  sector_size = se_ret
4162
4379
  dprint("Next sector size: 0x{:X}".format(sector_size))
4163
4380
  skip_init = False
4164
-
4165
- if skip_se:
4166
- buffer_pos += sector_size
4167
- pos += sector_size
4168
- continue
4169
4381
  # ↑↑↑ Sector erase
4170
4382
 
4171
4383
  if se_ret is not False:
@@ -4175,8 +4387,6 @@ class LK_Device(ABC):
4175
4387
  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))
4176
4388
  elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] < 12:
4177
4389
  status = self.WriteROM_DMG_MBC5_32M_FLASH(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
4178
- #elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] >= 12:
4179
- # 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))
4180
4390
  elif command_set_type == "EEPROM":
4181
4391
  status = self.WriteROM_DMG_EEPROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank, eeprom_buffer_size=256)
4182
4392
  elif command_set_type == "BLAZE_XPLODER":
@@ -4227,21 +4437,22 @@ class LK_Device(ABC):
4227
4437
  if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
4228
4438
  break
4229
4439
  elif not self.DEVICE.is_open or self.DEVICE is None:
4230
- self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}\n\nStatus Register: {:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), errmsg_mbc_selection, sr)})
4440
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Check soldering if it’s a DIY cartridge\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}\n\nStatus Register: {:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), errmsg_mbc_selection, sr)})
4231
4441
  break
4232
4442
  else:
4233
4443
  if chip_erase: retry_hp = 0
4234
4444
  if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
4235
4445
  retry_hp -= 5
4236
4446
  if retry_hp <= 0:
4237
- self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Unstable connection detected while writing 0x{:X} bytes in iteration {:d} at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n\nStatus Register: {:s}".format(buffer_len, self.ERROR_ARGS["iteration"], buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), sr)})
4447
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Unstable connection detected while writing 0x{:X} bytes in iteration {:d} at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Check soldering if it’s a DIY cartridge\n- Avoid passive USB hubs and try different USB ports/cables\n\nStatus Register: {:s}".format(buffer_len, self.ERROR_ARGS["iteration"], buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), sr)})
4238
4448
  continue
4239
4449
  else:
4240
4450
  retry_hp -= 10
4241
4451
  if retry_hp <= 0:
4242
- self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}\n- Your {:s} may also be incompatible with this cartridge\n\nStatus Register: {:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(size=len(data_import), asInt=False), errmsg_mbc_selection, self.DEVICE_NAME, sr), "abortable":False})
4452
+ enable_pullup_wr_str = " (+ WR pullup)" if enable_pullup_wr == 2 else ""
4453
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Check soldering if it’s a DIY cartridge\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}\n- Check cartridge profile used: {:s}{:s}\n- The cartridge may also be incompatible with your “{:s}” device\n\nStatus Register: {:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(size=len(data_import), asInt=False), errmsg_mbc_selection, cart_name, enable_pullup_wr_str, self.GetFullNameLabel(), sr), "abortable":False})
4243
4454
  continue
4244
-
4455
+
4245
4456
  rev_buffer_pos = sector_offsets[sector_pos - 1][0]
4246
4457
  buffer_pos = rev_buffer_pos
4247
4458
  bank = start_bank
@@ -4275,10 +4486,6 @@ class LK_Device(ABC):
4275
4486
  self._cart_write(pos, 0xFF)
4276
4487
  if flashcart.Unlock() is False: return False
4277
4488
  continue
4278
-
4279
- self.CANCEL = True
4280
- self.ERROR = True
4281
- continue
4282
4489
 
4283
4490
  skip_init = True
4284
4491
 
@@ -4304,6 +4511,8 @@ class LK_Device(ABC):
4304
4511
 
4305
4512
  # ↓↓↓ Reset flash
4306
4513
  flashcart.Reset(full_reset=True)
4514
+ if self.FW["fw_ver"] >= 14 and "set_audio_high" in cart_type:
4515
+ self._set_fw_variable("DMG_AUDIO_ENABLED", 0)
4307
4516
  # ↑↑↑ Reset flash
4308
4517
 
4309
4518
  # ↓↓↓ Flash verify
@@ -4313,7 +4522,7 @@ class LK_Device(ABC):
4313
4522
  if "verify_write" in args and args["verify_write"] is True:
4314
4523
  self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE_VERIFY", "size":len(data_import), "flash_offset":flash_offset})
4315
4524
  if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
4316
- with open("debug_verify.bin", "wb") as f: pass
4525
+ with open(Util.CONFIG_PATH + "/debug_verify.bin", "wb") as f: pass
4317
4526
 
4318
4527
  current_bank = None
4319
4528
  broken_sectors = []
@@ -4369,8 +4578,6 @@ class LK_Device(ABC):
4369
4578
  else:
4370
4579
  self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4371
4580
  self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4372
- # else:
4373
- # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4374
4581
  self._set_fw_variable("DMG_ROM_BANK", bank)
4375
4582
 
4376
4583
  buffer_len = min(buffer_len, bank_size)
@@ -4380,7 +4587,6 @@ class LK_Device(ABC):
4380
4587
  if end_address > start_address + sector[1]:
4381
4588
  end_address = start_address + sector[1]
4382
4589
  pos_from = (bank * start_address)
4383
- # pos_to = (bank * start_address) + verify_len
4384
4590
 
4385
4591
  elif self.MODE == "AGB":
4386
4592
  if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
@@ -4393,14 +4599,13 @@ class LK_Device(ABC):
4393
4599
  current_bank = bank
4394
4600
  verify_len = sector[1]
4395
4601
  pos_from = sector[0]
4396
- # pos_to = pos_from + verify_len
4397
4602
  # ↑↑↑ Switch ROM bank
4398
4603
 
4399
4604
  dprint(f"Verifying ROM bank #{bank} at 0x{pos_from:x} (physical 0x{start_address:X}, 0x{verify_len:X} bytes)")
4400
4605
 
4401
4606
  verified = False
4402
4607
  if self.FW["fw_ver"] >= 12 and sector[1] >= verify_len and crc32_errors < 5:
4403
- verified = self.CompareCRC32(buffer=data_import, offset=pos_from, length=verify_len, address=start_address, flashcart=flashcart, reset=False)
4608
+ verified = self.CompareCRC32(buffer=data_import, offset=pos_from, length=verify_len, address=start_address, flashcart=flashcart, reset=False, mbc=_mbc, bank=bank)
4404
4609
  if verified is True:
4405
4610
  dprint("CRC32 verification successful between 0x{:X} and 0x{:X}".format(pos_from, verify_len))
4406
4611
  self.SetProgress({"action":"UPDATE_POS", "pos":pos_from + verify_len})
@@ -4489,9 +4694,10 @@ class LK_Device(ABC):
4489
4694
 
4490
4695
  self.SetMode(self.MODE)
4491
4696
 
4492
- self.INFO["last_action"] = self.INFO["action"]
4493
- self.INFO["action"] = None
4494
- self.SetProgress({"action":"FINISHED", "verified":verified})
4697
+ if not "photo_mode" in args:
4698
+ self.INFO["last_action"] = self.INFO["action"]
4699
+ self.INFO["action"] = None
4700
+ self.SetProgress({"action":"FINISHED", "verified":verified})
4495
4701
  return True
4496
4702
 
4497
4703
  #################################################################
@@ -4502,6 +4708,8 @@ class LK_Device(ABC):
4502
4708
  self.CANCEL_ARGS = {}
4503
4709
  self.READ_ERRORS = 0
4504
4710
  self.WRITE_ERRORS = 0
4711
+ _apot = 0
4712
+
4505
4713
  if self.IsConnected():
4506
4714
  _apoe = False
4507
4715
  if self.CanPowerCycleCart():
@@ -4516,7 +4724,7 @@ class LK_Device(ABC):
4516
4724
  self.SIGNAL = signal
4517
4725
  try:
4518
4726
  temp = copy.copy(args)
4519
- if "buffer" in temp: temp["buffer"] = "(data)"
4727
+ if "buffer" in temp: temp["buffer"] = "(0x{:x} bytes of data)".format(len(temp["buffer"]))
4520
4728
  dprint("args:", temp)
4521
4729
  del(temp)
4522
4730
  self.NO_PROG_UPDATE = False
@@ -4524,6 +4732,7 @@ class LK_Device(ABC):
4524
4732
  elif args['mode'] == 2: ret = self._BackupRestoreRAM(args)
4525
4733
  elif args['mode'] == 3: ret = self._BackupRestoreRAM(args)
4526
4734
  elif args['mode'] == 4: ret = self._FlashROM(args)
4735
+ elif args['mode'] == 5: ret = self._DetectCartridge(args)
4527
4736
  elif args['mode'] == 0xFF: self.Debug()
4528
4737
  if self.FW is None: return False
4529
4738
  if self.FW["fw_ver"] >= 2 and self.FW["pcb_name"] == "GBxCart RW":