FlashGBX 4.3__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,6 +114,7 @@ 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
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}
@@ -305,7 +306,7 @@ class LK_Device(ABC):
305
306
  for mode in flashcarts.keys():
306
307
  for key in sorted(flashcarts[mode].keys(), key=str.casefold):
307
308
  self.SUPPORTED_CARTS[mode][key] = flashcarts[mode][key]
308
-
309
+
309
310
  def IsConnected(self):
310
311
  if self.DEVICE is None: return False
311
312
  if not self.DEVICE.isOpen(): return False
@@ -679,6 +680,12 @@ class LK_Device(ABC):
679
680
  self.CartPowerCycle(delay=delay)
680
681
  return
681
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
+
682
689
  mode = self.GetMode()
683
690
  var_state = self.GetVarState()
684
691
 
@@ -836,6 +843,7 @@ class LK_Device(ABC):
836
843
  self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
837
844
  self._set_fw_variable("DMG_READ_METHOD", self.DMG_READ_METHOD)
838
845
  self._set_fw_variable("CART_MODE", 1)
846
+ #if self.FW["fw_ver"] >= 14: self._set_fw_variable("DMG_AUDIO_ENABLED", 0)
839
847
  self.MODE = "DMG"
840
848
  elif mode == "AGB":
841
849
  self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
@@ -889,13 +897,9 @@ class LK_Device(ABC):
889
897
  self.SIGNAL = None
890
898
 
891
899
  def Debug(self):
892
- # for i in range(0, 0x100000):
893
- # print(hex(i), self._set_fw_variable("ADDRESS", i), end="\r", flush=True)
894
900
  return
895
901
 
896
902
  def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
897
- self.Debug()
898
-
899
903
  if not self.IsConnected(): raise ConnectionError("Couldn’t access the the device.")
900
904
  data = {}
901
905
  self.SIGNAL = None
@@ -959,6 +963,7 @@ class LK_Device(ABC):
959
963
  data["rtc_string"] = _mbc.GetRTCString()
960
964
  except:
961
965
  data["rtc_string"] = "Invalid data"
966
+
962
967
  if _mbc.GetName() == "G-MMC1":
963
968
  try:
964
969
  temp = bytearray([0] * 0x100000)
@@ -976,6 +981,21 @@ class LK_Device(ABC):
976
981
  print(traceback.format_exc())
977
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))
978
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
+
979
999
  elif self.MODE == "AGB":
980
1000
  # Unlock DACS carts on older firmware
981
1001
  if not self.CanPowerCycleCart() or self.FW["fw_ver"] == 1:
@@ -1084,6 +1104,7 @@ class LK_Device(ABC):
1084
1104
  if self.MODE == "DMG": #and setPinsAsInputs:
1085
1105
  self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"], wait=self.FW["fw_ver"] >= 12)
1086
1106
 
1107
+ self.Debug()
1087
1108
  return data
1088
1109
 
1089
1110
  def _DetectCartridge(self, args): # Wrapper for thread call
@@ -1119,9 +1140,9 @@ class LK_Device(ABC):
1119
1140
 
1120
1141
  # Disable Auto Power Off
1121
1142
  _apoe = False
1122
- if self.CanPowerCycleCart():
1123
- self.CartPowerCycle()
1124
- if self.FW["fw_ver"] >= 12:
1143
+ if self.FW["fw_ver"] >= 12:
1144
+ if self.CanPowerCycleCart():
1145
+ self.CartPowerCycle()
1125
1146
  _apoe = self._get_fw_variable("AUTO_POWEROFF_ENABLED") == 1
1126
1147
  if _apoe is True:
1127
1148
  _apot = self._get_fw_variable("AUTO_POWEROFF_TIME")
@@ -1135,8 +1156,11 @@ class LK_Device(ABC):
1135
1156
  supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
1136
1157
  cart_type = supported_carts[cart_type_id]
1137
1158
 
1159
+ # Skip DMG save type detection
1160
+ if self.MODE == "DMG" and cart_type_id == 0: checkSaveType = False
1161
+
1138
1162
  # Preparations
1139
- 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):
1140
1164
  checkSaveType = False
1141
1165
  elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
1142
1166
  save_size = 65536
@@ -1243,7 +1267,9 @@ class LK_Device(ABC):
1243
1267
  save_chip = Util.AGB_Flash_Save_Chips[flash_save_id]
1244
1268
 
1245
1269
  if flash_save_id in (0xBF5B, 0xFFFF): # Bootlegs
1246
- if self.INFO["data"][0:0x10000] == self.INFO["data"][0x10000:0x20000]:
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]:
1247
1273
  save_type = 4
1248
1274
  else:
1249
1275
  save_type = 5
@@ -1253,7 +1279,7 @@ class LK_Device(ABC):
1253
1279
  save_type = 4
1254
1280
  except:
1255
1281
  pass
1256
-
1282
+
1257
1283
  if save_type is None:
1258
1284
  checkBatterylessSRAM = True
1259
1285
  if info["dacs_8m"] is True:
@@ -1284,8 +1310,8 @@ class LK_Device(ABC):
1284
1310
  save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
1285
1311
  eeprom_64k = self.INFO["data"]
1286
1312
  if eeprom_64k in (bytearray([0xFF] * len(eeprom_64k)), bytearray([0] * len(eeprom_64k))):
1287
- save_type = None
1288
1313
  save_size = 0
1314
+ save_type = 0
1289
1315
  elif (eeprom_4k == eeprom_64k[:len(eeprom_4k)]):
1290
1316
  save_type = 2
1291
1317
  save_size = 8192
@@ -1378,6 +1404,7 @@ class LK_Device(ABC):
1378
1404
 
1379
1405
  def ReadFlashSaveID(self):
1380
1406
  # Check if actually SRAM/FRAM
1407
+ dprint("Checking Flash ID of Save Memory")
1381
1408
  test1 = self._cart_read(0, 0x10, agb_save_flash=True)[4]
1382
1409
  self._cart_write_flash([[ 0x0004, test1 ^ 0xFF ]])
1383
1410
  test2 = self._cart_read(0, 0x10, agb_save_flash=True)[4]
@@ -1735,7 +1762,6 @@ class LK_Device(ABC):
1735
1762
  self._set_fw_variable("ADDRESS", address)
1736
1763
  elif self.MODE == "AGB":
1737
1764
  self._set_fw_variable("ADDRESS", address >> 1)
1738
- # print("==>", hex(self._get_fw_variable("ADDRESS")), hex(self._get_fw_variable("TRANSFER_SIZE")), hex(self._get_fw_variable("BUFFER_SIZE")))
1739
1765
 
1740
1766
  skip_init = True
1741
1767
 
@@ -1839,9 +1865,6 @@ class LK_Device(ABC):
1839
1865
  self._write(buffer[i*length:i*length+length])
1840
1866
  ret = self._read(1)
1841
1867
  if ret not in (0x01, 0x03):
1842
- #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)})
1843
- #self.CANCEL = True
1844
- #self.ERROR = True
1845
1868
  return False
1846
1869
 
1847
1870
  self._cart_write(address + length - 1, 0xFF)
@@ -1984,7 +2007,7 @@ class LK_Device(ABC):
1984
2007
  buffer2 = self.ReadROM(0x80, 0x40)
1985
2008
  return buffer1 == buffer2
1986
2009
 
1987
- 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):
1988
2011
  left = length
1989
2012
  chunk_pos = 0
1990
2013
  verified = False
@@ -2010,6 +2033,8 @@ class LK_Device(ABC):
2010
2033
  if crc32_expected != crc32_calculated:
2011
2034
  if i == 0 and flashcart is not None:
2012
2035
  flashcart.Reset(full_reset=True)
2036
+ if mbc is not None:
2037
+ mbc.SelectBankROM(bank)
2013
2038
  verified = (crc32_expected, crc32_calculated)
2014
2039
  continue
2015
2040
  else:
@@ -2065,11 +2090,24 @@ class LK_Device(ABC):
2065
2090
  cmds = []
2066
2091
  cmds_reset = []
2067
2092
  cmds_unlock_read = []
2068
- for f in range(1, len(supported_carts)):
2069
- 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)
2070
2096
  if "command_set" not in cart_type: continue
2071
2097
  if "manual_select" in cart_type and cart_type["manual_select"] is True: continue
2072
- 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":
2073
2111
  self._cart_read(0x102, 1)
2074
2112
  self._cart_write(6, 1)
2075
2113
  self._cart_write_flash(cart_type["commands"]["reset"])
@@ -2110,7 +2148,7 @@ class LK_Device(ABC):
2110
2148
  self._cart_write_flash(cart_type["commands"]["reset"])
2111
2149
  break
2112
2150
  continue
2113
- 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:
2114
2152
  self._set_we_pin_audio()
2115
2153
  self._cart_write_flash(cart_type["commands"]["unlock"], flashcart=False)
2116
2154
  self._cart_write_flash(cart_type["commands"]["reset"])
@@ -2200,6 +2238,7 @@ class LK_Device(ABC):
2200
2238
  self._cart_write(cmd[0], cmd[1], flashcart=True)
2201
2239
 
2202
2240
  flash_id_cmds = sorted(cmds, key=lambda x: x['read_identifier'][0][0])
2241
+ read_cfi_cmds = []
2203
2242
 
2204
2243
  rom_s = " ".join(format(x, '02X') for x in rom)
2205
2244
  if self.MODE == "DMG":
@@ -2225,41 +2264,13 @@ class LK_Device(ABC):
2225
2264
  cmp = self._cart_read(0, 8)
2226
2265
  self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2227
2266
  if rom != cmp: # ROM data changed
2228
- if "read_cfi" not in flash_id_cmds[i]:
2229
- flash_id_cmds[i]["read_cfi"] = [[ flash_id_cmds[i]["read_identifier"][0][0], 0x98 ]]
2230
- self._cart_write_flash(flash_id_cmds[i]["read_cfi"], flashcart=True)
2231
- cfi_buffer = self._cart_read(0, 0x400)
2232
- self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2233
2267
  flash_id_methods.append([we - 1, i, list(cmp), cfi_buffer, flash_id_cmds[i]["read_identifier"]])
2234
-
2235
- if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2236
- with open(Util.CONFIG_PATH + "/debug_cfi.bin", "wb") as f: f.write(cfi_buffer)
2237
- try:
2238
- magic = "{:s}{:s}{:s}".format(chr(cfi_buffer[0x20]), chr(cfi_buffer[0x22]), chr(cfi_buffer[0x24]))
2239
- d_swap = (0, 0)
2240
- if magic == "QRY": # D0D1 not swapped
2241
- pass
2242
- elif magic == "RQZ": # D0D1 swapped
2243
- d_swap = [(0, 1)]
2244
- for j2 in range(0, len(d_swap)):
2245
- for j in range(0, len(cfi_buffer)):
2246
- cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2247
- elif magic == "\x92\x91\x9A": # D0D1+D6D7 swapped
2248
- d_swap = [( 0, 1 ), ( 6, 7 )]
2249
- for j2 in range(0, len(d_swap)):
2250
- for j in range(0, len(cfi_buffer)):
2251
- cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2252
- if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2253
- with open(Util.CONFIG_PATH + "/debug_cfi_d0d1+d6d7.bin", "wb") as f: f.write(cfi_buffer)
2254
- else:
2255
- cfi_buffer = None
2256
- except:
2257
- cfi_buffer = None
2258
-
2259
2268
  if self.MODE == "DMG":
2260
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))
2261
2270
  else:
2262
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"])
2263
2274
 
2264
2275
  dprint(f"Found {len(flash_id_methods):d} result(s)")
2265
2276
  self._cart_write_flash([[0, 0xFF]], True)
@@ -2293,10 +2304,55 @@ class LK_Device(ABC):
2293
2304
 
2294
2305
  dprint("Compatible flash types:", [(index, supported_carts[index]["names"][0]) for index in flash_types])
2295
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
+
2296
2351
  if cfi_buffer is None or len(cfi_buffer) < 0x400:
2297
2352
  cfi = False
2298
2353
  else:
2299
2354
  cfi = ParseCFI(cfi_buffer)
2355
+
2300
2356
  if cfi is not False:
2301
2357
  cfi["raw"] = cfi_buffer
2302
2358
  s = ""
@@ -2472,9 +2528,7 @@ class LK_Device(ABC):
2472
2528
 
2473
2529
  # Firmware check L8
2474
2530
  if self.FW["fw_ver"] < 8 and flashcart and "enable_pullups" in cart_type:
2475
- #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8.", "abortable":False})
2476
- #return False
2477
- 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.FW["pcb_name"], ANSI.RESET))
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))
2478
2532
  del(cart_type["enable_pullups"])
2479
2533
  # Firmware check L8
2480
2534
 
@@ -2535,9 +2589,6 @@ class LK_Device(ABC):
2535
2589
  self.INFO["dump_info"]["agb_read_method"] = "3D Memory"
2536
2590
 
2537
2591
  elif flashcart and "command_set" in cart_type and cart_type["command_set"] == "GBAMP":
2538
- # if not self.CanPowerCycleCart():
2539
- # 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})
2540
- # return False
2541
2592
  self.INFO["dump_info"]["agb_read_method"] = "GBA Movie Player"
2542
2593
  if not "verify_write" in args: self.CartPowerCycleOrAskReconnect()
2543
2594
  flashcart.Unlock()
@@ -2608,9 +2659,6 @@ class LK_Device(ABC):
2608
2659
  else:
2609
2660
  rom_banks = 1
2610
2661
  rom_bank_size = 0x2000000
2611
-
2612
- #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:
2613
- # self.SetAGBReadMethod(0)
2614
2662
 
2615
2663
  if self.ERROR: return
2616
2664
 
@@ -2635,9 +2683,12 @@ class LK_Device(ABC):
2635
2683
  else:
2636
2684
  self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
2637
2685
  dprint("Pullups disabled")
2686
+
2638
2687
  if self.FW["fw_ver"] >= 12:
2639
- if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
2640
- 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)
2641
2692
 
2642
2693
  buffer = bytearray(size)
2643
2694
  max_length = self.MAX_BUFFER_READ
@@ -2661,6 +2712,8 @@ class LK_Device(ABC):
2661
2712
  end_bank = math.ceil((buffer_pos + args["verify_len"]) / rom_bank_size)
2662
2713
  rom_banks = end_bank
2663
2714
  elif "bl_offset" in args:
2715
+ if "bl_layout" in args and args["bl_layout"] in (1, 2):
2716
+ args["bl_size"] <<= 1
2664
2717
  buffer_pos = args["bl_offset"]
2665
2718
  start_address = buffer_pos
2666
2719
  end_address = args["bl_offset"] + args["bl_size"]
@@ -2765,7 +2818,13 @@ class LK_Device(ABC):
2765
2818
  elif lives < 20:
2766
2819
  lives = 20
2767
2820
 
2768
- 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)
2769
2828
  buffer[pos_total:pos_total+len(temp)] = temp
2770
2829
  pos_total += len(temp)
2771
2830
 
@@ -2942,9 +3001,12 @@ class LK_Device(ABC):
2942
3001
  if "cart_type" in args and args["cart_type"] >= 0:
2943
3002
  supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
2944
3003
  cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
3004
+
2945
3005
  if self.FW["fw_ver"] >= 12:
2946
- if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
2947
- 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)
2948
3010
 
2949
3011
  self._set_fw_variable("STATUS_REGISTER_MASK", 0x80)
2950
3012
  self._set_fw_variable("STATUS_REGISTER_VALUE", 0x80)
@@ -2960,7 +3022,9 @@ class LK_Device(ABC):
2960
3022
  save_size = args["save_size"]
2961
3023
  else:
2962
3024
  save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(args["save_type"])]
3025
+
2963
3026
  ram_banks = _mbc.GetRAMBanks(save_size)
3027
+
2964
3028
  buffer_len = min(0x200, _mbc.GetRAMBankSize())
2965
3029
  self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
2966
3030
  self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
@@ -3121,6 +3185,9 @@ class LK_Device(ABC):
3121
3185
  if args["mode"] == 2: # Backup
3122
3186
  action = "SAVE_READ"
3123
3187
  buffer = bytearray()
3188
+ if self.MODE == "DMG" and args["save_type"] == 0x204: # Unlicensed PHOTO!
3189
+ ram_banks = 16
3190
+
3124
3191
  elif args["mode"] == 3: # Restore
3125
3192
  action = "SAVE_WRITE"
3126
3193
  self.INFO["save_erase"] = args["erase"]
@@ -3128,6 +3195,9 @@ class LK_Device(ABC):
3128
3195
  buffer = bytearray([ empty_data_byte ] * save_size)
3129
3196
  if self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
3130
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]
3131
3201
  else:
3132
3202
  if args["path"] is None:
3133
3203
  if "buffer" in args:
@@ -3138,16 +3208,18 @@ class LK_Device(ABC):
3138
3208
  with open(args["path"], "rb") as f:
3139
3209
  buffer = bytearray(f.read())
3140
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
+
3141
3217
  # Fill too small file
3142
3218
  if not (self.MODE == "AGB" and args["save_type"] == 6): # Not DACS
3143
3219
  if args["mode"] == 3:
3144
3220
  while len(buffer) < save_size:
3145
3221
  buffer += bytearray(buffer)
3146
3222
 
3147
- #if self.MODE == "AGB" and "ereader" in self.INFO and self.INFO["ereader"] is True: # e-Reader
3148
- # buffer[0xFF80:0x10000] = bytearray([0] * 0x80)
3149
- # buffer[0x1FF80:0x20000] = bytearray([0] * 0x80)
3150
-
3151
3223
  # Main loop
3152
3224
  if not (args["mode"] == 2 and "verify_write" in args and args["verify_write"]):
3153
3225
  self.INFO["action"] = self.ACTIONS[action]
@@ -3198,10 +3270,6 @@ class LK_Device(ABC):
3198
3270
  else:
3199
3271
  dprint("Unknown bank switching method")
3200
3272
  time.sleep(0.05)
3201
-
3202
- # if "detect" in args and args["detect"] is True:
3203
- # start_address = end_address - 64
3204
- # buffer_len = 64
3205
3273
 
3206
3274
  max_length = 64
3207
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))
@@ -3259,7 +3327,6 @@ class LK_Device(ABC):
3259
3327
  continue
3260
3328
 
3261
3329
  if xe == 2 and in_temp[0] != in_temp[1]:
3262
- # print(in_temp[0], in_temp[1], sep="\n")
3263
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})
3264
3331
  return False
3265
3332
 
@@ -3381,11 +3448,6 @@ class LK_Device(ABC):
3381
3448
 
3382
3449
  pos += buffer_len
3383
3450
  buffer_offset += buffer_len
3384
-
3385
- # if "detect" in args and args["detect"] is True:
3386
- # buffer += bytearray(end_address - 64)
3387
- # pos += end_address - 64
3388
- # buffer_offset += end_address - 64
3389
3451
 
3390
3452
  verified = False
3391
3453
  if args["mode"] == 2: # Backup
@@ -3421,6 +3483,13 @@ class LK_Device(ABC):
3421
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:
3422
3484
  buffer[5] = sram_5
3423
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
+
3424
3493
  if args["path"] is not None:
3425
3494
  if self.MODE == "DMG" and _mbc.GetName() == "MBC2":
3426
3495
  for i in range(0, len(buffer)):
@@ -3447,11 +3516,21 @@ class LK_Device(ABC):
3447
3516
  advance = "rtc_advance" in args and args["rtc_advance"]
3448
3517
  self.SetProgress({"action":"UPDATE_RTC", "method":"write"})
3449
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())
3450
3521
  _mbc.WriteRTC(buffer[-_mbc.GetRTCBufferSize():], advance=advance)
3451
3522
  elif self.MODE == "AGB":
3523
+ if "erase" in args and args["erase"] is True:
3524
+ buffer += bytearray([0xFF] * 0x10)
3452
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)
3453
3526
  _agb_gpio.WriteRTC(buffer[-0x10:], advance=advance)
3454
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
+
3455
3534
  self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer), "force_update":True})
3456
3535
 
3457
3536
  # ↓↓↓ Write verify
@@ -3540,6 +3619,7 @@ class LK_Device(ABC):
3540
3619
  end_bank = 0
3541
3620
  pos_from = 0
3542
3621
  verify_len = 0
3622
+ enable_pullup_wr = False
3543
3623
 
3544
3624
  if "buffer" in args:
3545
3625
  data_import = args["buffer"]
@@ -3552,6 +3632,20 @@ class LK_Device(ABC):
3552
3632
  if "start_addr" in args and args["start_addr"] > 0:
3553
3633
  data_import = bytearray(b'\xFF' * args["start_addr"]) + data_import
3554
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
+
3555
3649
  # Pad data
3556
3650
  if len(data_import) > 0:
3557
3651
  if len(data_import) < 0x400:
@@ -3594,24 +3688,23 @@ class LK_Device(ABC):
3594
3688
 
3595
3689
  supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
3596
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
+
3597
3696
  if cart_type == "RETAIL": return False # Generic ROM Cartridge is not flashable
3598
3697
 
3599
- # Special carts
3600
- # if "power_cycle" in cart_type and cart_type["power_cycle"] is True and not self.CanPowerCycleCart():
3601
- # 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})
3602
- # return False
3603
- # Special carts
3604
- # Firmware check L1
3698
+ # Firmware check L2
3605
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)):
3606
- #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})
3607
3700
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L2.", "abortable":False})
3608
3701
  return False
3609
- # Firmware check L1
3610
3702
  # Firmware check L2
3703
+ # Firmware check L3
3611
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"])):
3612
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))
3613
3706
  del(cart_type["commands"]["buffer_write"])
3614
- # Firmware check L2
3707
+ # Firmware check L3
3615
3708
  # Firmware check L5
3616
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)):
3617
3710
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L5.", "abortable":False})
@@ -3622,21 +3715,25 @@ class LK_Device(ABC):
3622
3715
  # Firmware check L5
3623
3716
  # Firmware check L8
3624
3717
  if (self.FW["fw_ver"] < 8 and "enable_pullups" in cart_type and cart_type["enable_pullups"] is True):
3625
- #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8.", "abortable":False})
3626
- #return False
3627
- 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.FW["pcb_name"], ANSI.RESET))
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))
3628
3719
  del(cart_type["enable_pullups"])
3629
3720
  # Firmware check L8
3630
3721
  # Firmware check L12
3631
3722
  if (self.FW["fw_ver"] < 12 and "set_irq_high" in cart_type and cart_type["set_irq_high"] is True):
3632
- #self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L12.", "abortable":False})
3633
- #return False
3634
- 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.FW["pcb_name"], ANSI.RESET))
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))
3635
3724
  del(cart_type["set_irq_high"])
3636
3725
  if (self.FW["fw_ver"] < 12 and "status_register_mask" in cart_type):
3637
- 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})
3638
3730
  return False
3639
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
3640
3737
 
3641
3738
  # Ensure cart is powered
3642
3739
  if self.CanPowerCycleCart(): self.CartPowerOn()
@@ -3668,7 +3765,7 @@ class LK_Device(ABC):
3668
3765
  flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
3669
3766
 
3670
3767
  rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
3671
-
3768
+
3672
3769
  # ↓↓↓ Set Voltage
3673
3770
  if args["override_voltage"] is not False:
3674
3771
  if args["override_voltage"] == 5:
@@ -3680,20 +3777,22 @@ class LK_Device(ABC):
3680
3777
  elif flashcart.GetVoltage() == 5:
3681
3778
  self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
3682
3779
  # ↑↑↑ Set Voltage
3683
-
3780
+
3684
3781
  # ↓↓↓ Pad data for full chip erase on sector erase mode
3685
3782
  if not flashcart.SupportsChipErase() and flashcart.SupportsSectorErase() and args["prefer_chip_erase"]:
3686
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))
3687
- flash_size = flashcart.GetFlashSize()
3688
- if flash_size is not False and len(data_import) < flash_size:
3689
- # Pad with FF till the end (with MemoryError fix)
3690
- pad_len = flash_size - len(data_import)
3691
- while pad_len > 0x2000000:
3692
- data_import += bytearray([0xFF] * 0x2000000)
3693
- pad_len -= 0x2000000
3694
- 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)))
3695
3794
  # ↑↑↑ Pad data for full chip erase on sector erase mode
3696
-
3795
+
3697
3796
  # ↓↓↓ Flashcart configuration
3698
3797
  if self.FW["fw_ver"] >= 8:
3699
3798
  if "enable_pullups" in cart_type:
@@ -3704,9 +3803,11 @@ class LK_Device(ABC):
3704
3803
  self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
3705
3804
  dprint("Pullups disabled")
3706
3805
  if self.FW["fw_ver"] >= 12:
3707
- if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
3708
- self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
3709
- 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":
3710
3811
  self._set_fw_variable("AGB_IRQ_ENABLED", 1 if "set_irq_high" in cart_type else 0)
3711
3812
 
3712
3813
  _mbc = None
@@ -3793,17 +3894,13 @@ class LK_Device(ABC):
3793
3894
  temp = 0
3794
3895
  if command_set_type == "AMD":
3795
3896
  temp = 0x01
3796
- #self._write(0x01) # FLASH_COMMAND_SET_AMD
3797
- #self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
3798
3897
  dprint("Using AMD command set")
3799
3898
  elif command_set_type == "INTEL":
3800
3899
  temp = 0x02
3801
- #self._write(0x02) # FLASH_COMMAND_SET_INTEL
3802
3900
  self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
3803
3901
  dprint("Using Intel command set")
3804
3902
  elif command_set_type == "SHARP":
3805
3903
  temp = 0x02
3806
- #self._write(0x02) # FLASH_COMMAND_SET_INTEL
3807
3904
  self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 1)
3808
3905
  dprint("Using Sharp/Intel command set")
3809
3906
  elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
@@ -3944,6 +4041,8 @@ class LK_Device(ABC):
3944
4041
  _mbc.SelectBankROM(1)
3945
4042
  if we != 0x00:
3946
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)
3947
4046
  # ↑↑↑ Preparations
3948
4047
 
3949
4048
  # ↓↓↓ Read Flash ID
@@ -3970,8 +4069,6 @@ class LK_Device(ABC):
3970
4069
  if len(sector_offsets) > 0:
3971
4070
  flash_capacity = sector_offsets[-1][0] + sector_offsets[-1][1]
3972
4071
  if flash_capacity < len(data_import) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]):
3973
- #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})
3974
- #return False
3975
4072
  sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size)
3976
4073
 
3977
4074
  sector_offsets_hash = base64.urlsafe_b64encode(hashlib.sha1(str(sector_offsets).encode("UTF-8")).digest()).decode("ASCII", "ignore")[:4]
@@ -4026,7 +4123,6 @@ class LK_Device(ABC):
4026
4123
  bl_sectors = []
4027
4124
  for sector in sector_offsets:
4028
4125
  if flash_offset > sector[0]: continue
4029
- #if len(bl_sectors) > 0 and (flash_offset + flash_size) < (sector[0] + sector[1]): break
4030
4126
  if (flash_offset + flash_size) < sector[0]: break
4031
4127
  bl_sectors.append(sector)
4032
4128
  write_sectors = bl_sectors
@@ -4040,7 +4136,7 @@ class LK_Device(ABC):
4040
4136
 
4041
4137
  dprint("Sectors to update:", write_sectors)
4042
4138
  # ↑↑↑ Read Sector Map
4043
-
4139
+
4044
4140
  # ↓↓↓ Chip erase
4045
4141
  chip_erase = False
4046
4142
  if flashcart.SupportsChipErase() and not flash_offset > 0:
@@ -4055,7 +4151,7 @@ class LK_Device(ABC):
4055
4151
  self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No erase method available.", "abortable":False})
4056
4152
  return False
4057
4153
  # ↑↑↑ Chip erase
4058
-
4154
+
4059
4155
  # ↓↓↓ Flash Write
4060
4156
  if chip_erase:
4061
4157
  write_sectors = [[ 0, len(data_import) ]]
@@ -4064,9 +4160,10 @@ class LK_Device(ABC):
4064
4160
  elif len(write_sectors) == 0:
4065
4161
  write_sectors = sector_offsets
4066
4162
 
4067
- 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"]
4068
4166
  self.SetProgress({"action":"UPDATE_POS", "pos":flash_offset})
4069
- self.INFO["action"] = self.ACTIONS["ROM_WRITE"]
4070
4167
 
4071
4168
  if smallest_sector_size is not False:
4072
4169
  buffer_len = smallest_sector_size
@@ -4121,82 +4218,79 @@ class LK_Device(ABC):
4121
4218
  bank = start_bank
4122
4219
 
4123
4220
  # ↓↓↓ Check if data matches already
4124
- if self.FW["fw_ver"] >= 10 and not (flashcart and cart_type["command_set"] == "GBAMP"):
4125
- if "verify_write" in args and args["verify_write"] is True:
4126
- buffer_pos_matchcheck = buffer_pos
4127
- verified = False
4128
- ts_se_start = time.time()
4129
- while bank < end_bank:
4130
- status = None
4131
- # ↓↓↓ Switch ROM bank
4132
- if self.MODE == "DMG":
4133
- if _mbc.ResetBeforeBankChange(bank) is True:
4134
- dprint("Resetting the MBC")
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)
4135
4239
  self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4136
- (start_address, bank_size) = _mbc.SelectBankROM(bank)
4137
- if flashcart.PulseResetAfterWrite():
4138
- if bank == 0:
4139
- if self.FW["fw_ver"] < 2:
4140
- self._write(self.DEVICE_CMD["OFW_GB_CART_MODE"])
4141
- else:
4142
- self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4143
- self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4144
- # else:
4145
- # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4146
- self._set_fw_variable("DMG_ROM_BANK", bank)
4147
-
4148
- buffer_len = min(buffer_len, bank_size)
4149
- if "start_addr" in flashcart.CONFIG and bank == 0: start_address = flashcart.CONFIG["start_addr"]
4150
- end_address = start_address + bank_size
4151
- start_address += (buffer_pos % rom_bank_size)
4152
- if end_address > start_address + sector[1]:
4153
- end_address = start_address + sector[1]
4154
-
4155
- elif self.MODE == "AGB":
4156
- if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
4157
- if bank != current_bank:
4158
- flashcart.Reset(full_reset=True)
4159
- flashcart.SelectBankROM(bank)
4160
- temp = end_address - start_address
4161
- start_address %= cart_type["flash_bank_size"]
4162
- end_address = min(cart_type["flash_bank_size"], start_address + temp)
4163
- current_bank = bank
4164
- # ↑↑↑ Switch ROM bank
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]
4165
4248
 
4166
- pos = start_address
4167
- #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))
4168
- verified = self.CompareCRC32(buffer=data_import, offset=buffer_pos_matchcheck, length=end_address - pos, address=pos, flashcart=flashcart, reset=True)
4169
- self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos_matchcheck})
4170
- if verified is not True:
4171
- break
4172
- buffer_pos_matchcheck += (end_address - pos)
4173
- bank += 1
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
4174
4268
 
4175
- if verified is True:
4176
- dprint("Skipping sector #{:d}, because the CRC32 matched".format(sector_pos))
4177
- if flashcart.FlashCommandsOnBank1(): _mbc.SelectBankROM(bank)
4178
- self.NO_PROG_UPDATE = True
4179
- se_ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos, skip=verified)
4180
- self.NO_PROG_UPDATE = False
4181
-
4182
- if self.CANCEL:
4183
- cancel_args = {"action":"ABORT", "abortable":False}
4184
- cancel_args.update(self.CANCEL_ARGS)
4185
- self.CANCEL_ARGS = {}
4186
- self.ERROR_ARGS = {}
4187
- self.SetProgress(cancel_args)
4188
- if self.CanPowerCycleCart(): self.CartPowerCycle()
4189
- return
4190
-
4191
- ts_se_elapsed = time.time() - ts_se_start
4192
- if se_ret:
4193
- sector_size = se_ret
4194
- dprint("Next sector size: 0x{:X}".format(sector_size))
4195
- buffer_pos += sector_size
4196
- continue
4197
- else:
4198
- verified = False
4199
- bank = start_bank
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
4200
4294
  # ↑↑↑ Check if data matches already
4201
4295
 
4202
4296
  while bank < end_bank:
@@ -4210,7 +4304,6 @@ class LK_Device(ABC):
4210
4304
  return
4211
4305
 
4212
4306
  status = None
4213
- #print("Bank:", bank, "...")
4214
4307
  # ↓↓↓ Switch ROM bank
4215
4308
  if self.MODE == "DMG":
4216
4309
  if _mbc.ResetBeforeBankChange(bank) is True:
@@ -4224,8 +4317,6 @@ class LK_Device(ABC):
4224
4317
  else:
4225
4318
  self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4226
4319
  self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4227
- # else:
4228
- # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4229
4320
  self._set_fw_variable("DMG_ROM_BANK", bank)
4230
4321
 
4231
4322
  buffer_len = min(buffer_len, bank_size)
@@ -4296,8 +4387,6 @@ class LK_Device(ABC):
4296
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))
4297
4388
  elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] < 12:
4298
4389
  status = self.WriteROM_DMG_MBC5_32M_FLASH(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
4299
- #elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] >= 12:
4300
- # 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))
4301
4390
  elif command_set_type == "EEPROM":
4302
4391
  status = self.WriteROM_DMG_EEPROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank, eeprom_buffer_size=256)
4303
4392
  elif command_set_type == "BLAZE_XPLODER":
@@ -4348,21 +4437,22 @@ class LK_Device(ABC):
4348
4437
  if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
4349
4438
  break
4350
4439
  elif not self.DEVICE.is_open or self.DEVICE is None:
4351
- 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)})
4352
4441
  break
4353
4442
  else:
4354
4443
  if chip_erase: retry_hp = 0
4355
4444
  if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
4356
4445
  retry_hp -= 5
4357
4446
  if retry_hp <= 0:
4358
- 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)})
4359
4448
  continue
4360
4449
  else:
4361
4450
  retry_hp -= 10
4362
4451
  if retry_hp <= 0:
4363
- 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})
4364
4454
  continue
4365
-
4455
+
4366
4456
  rev_buffer_pos = sector_offsets[sector_pos - 1][0]
4367
4457
  buffer_pos = rev_buffer_pos
4368
4458
  bank = start_bank
@@ -4396,10 +4486,6 @@ class LK_Device(ABC):
4396
4486
  self._cart_write(pos, 0xFF)
4397
4487
  if flashcart.Unlock() is False: return False
4398
4488
  continue
4399
-
4400
- self.CANCEL = True
4401
- self.ERROR = True
4402
- continue
4403
4489
 
4404
4490
  skip_init = True
4405
4491
 
@@ -4425,6 +4511,8 @@ class LK_Device(ABC):
4425
4511
 
4426
4512
  # ↓↓↓ Reset flash
4427
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)
4428
4516
  # ↑↑↑ Reset flash
4429
4517
 
4430
4518
  # ↓↓↓ Flash verify
@@ -4490,8 +4578,6 @@ class LK_Device(ABC):
4490
4578
  else:
4491
4579
  self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4492
4580
  self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4493
- # else:
4494
- # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4495
4581
  self._set_fw_variable("DMG_ROM_BANK", bank)
4496
4582
 
4497
4583
  buffer_len = min(buffer_len, bank_size)
@@ -4501,7 +4587,6 @@ class LK_Device(ABC):
4501
4587
  if end_address > start_address + sector[1]:
4502
4588
  end_address = start_address + sector[1]
4503
4589
  pos_from = (bank * start_address)
4504
- # pos_to = (bank * start_address) + verify_len
4505
4590
 
4506
4591
  elif self.MODE == "AGB":
4507
4592
  if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
@@ -4514,14 +4599,13 @@ class LK_Device(ABC):
4514
4599
  current_bank = bank
4515
4600
  verify_len = sector[1]
4516
4601
  pos_from = sector[0]
4517
- # pos_to = pos_from + verify_len
4518
4602
  # ↑↑↑ Switch ROM bank
4519
4603
 
4520
4604
  dprint(f"Verifying ROM bank #{bank} at 0x{pos_from:x} (physical 0x{start_address:X}, 0x{verify_len:X} bytes)")
4521
4605
 
4522
4606
  verified = False
4523
4607
  if self.FW["fw_ver"] >= 12 and sector[1] >= verify_len and crc32_errors < 5:
4524
- 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)
4525
4609
  if verified is True:
4526
4610
  dprint("CRC32 verification successful between 0x{:X} and 0x{:X}".format(pos_from, verify_len))
4527
4611
  self.SetProgress({"action":"UPDATE_POS", "pos":pos_from + verify_len})
@@ -4610,9 +4694,10 @@ class LK_Device(ABC):
4610
4694
 
4611
4695
  self.SetMode(self.MODE)
4612
4696
 
4613
- self.INFO["last_action"] = self.INFO["action"]
4614
- self.INFO["action"] = None
4615
- 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})
4616
4701
  return True
4617
4702
 
4618
4703
  #################################################################
@@ -4639,7 +4724,7 @@ class LK_Device(ABC):
4639
4724
  self.SIGNAL = signal
4640
4725
  try:
4641
4726
  temp = copy.copy(args)
4642
- if "buffer" in temp: temp["buffer"] = "(data)"
4727
+ if "buffer" in temp: temp["buffer"] = "(0x{:x} bytes of data)".format(len(temp["buffer"]))
4643
4728
  dprint("args:", temp)
4644
4729
  del(temp)
4645
4730
  self.NO_PROG_UPDATE = False