FlashGBX 3.37__py3-none-any.whl → 4.0__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/Flashcart.py CHANGED
@@ -99,6 +99,13 @@ class Flashcart:
99
99
  else:
100
100
  return ("buffer_write" in self.CONFIG["commands"])
101
101
 
102
+ def SupportsPageWrite(self):
103
+ buffer_size = self.GetBufferSize()
104
+ if buffer_size is False:
105
+ return False
106
+ else:
107
+ return ("page_write" in self.CONFIG["commands"])
108
+
102
109
  def SupportsSingleWrite(self):
103
110
  return ("single_write" in self.CONFIG["commands"])
104
111
 
@@ -180,17 +187,17 @@ class Flashcart:
180
187
  if full_reset and "power_cycle" in self.CONFIG:
181
188
  self.CART_POWERCYCLE_FNCPTR()
182
189
  time.sleep(0.001)
183
- self.Unlock()
190
+ if self.Unlock() is False: return False
184
191
  elif full_reset and "reset_every" in self.CONFIG and "flash_size" in self.CONFIG:
185
192
  for j in range(0, self.CONFIG["flash_size"], self.CONFIG["reset_every"]):
186
193
  if j >= max_address: break
187
194
  dprint("reset_every @ 0x{:X}".format(j))
188
195
  for command in self.CONFIG["commands"]["reset"]:
189
196
  self.CartWrite([[j + command[0], command[1]]])
190
- time.sleep(0.01)
197
+ #time.sleep(0.01)
191
198
  elif "reset" in self.CONFIG["commands"]:
192
199
  self.CartWrite(self.CONFIG["commands"]["reset"])
193
- time.sleep(0.001)
200
+ #time.sleep(0.001)
194
201
 
195
202
  def _VerifyFlashID(self, config):
196
203
  if "read_identifier" not in config["commands"]: return (False, [])
@@ -238,7 +245,7 @@ class Flashcart:
238
245
  if self.CFI is not None: return self.CFI
239
246
  if "read_cfi" not in self.CONFIG["commands"]:
240
247
  if self.CONFIG["_command_set"] == "INTEL":
241
- self.CONFIG["commands"]["read_cfi"] = self.CONFIG["commands"]["read_identifier"]
248
+ self.CONFIG["commands"]["read_cfi"] = [ [ 0, 0x98 ] ]
242
249
  elif self.CONFIG["_command_set"] == "AMD":
243
250
  self.CONFIG["commands"]["read_cfi"] = [ [ 0xAA, 0x98 ] ]
244
251
 
@@ -365,82 +372,92 @@ class Flashcart:
365
372
  self.Reset(full_reset=True)
366
373
  return True
367
374
 
368
- def SectorErase(self, pos=0, buffer_pos=0):
369
- self.Reset(full_reset=False)
370
- if "sector_erase" not in self.CONFIG["commands"]: return False
371
- if "sector_size" not in self.CONFIG: return False
372
- for i in range(0, len(self.CONFIG["commands"]["sector_erase"])):
373
- addr = self.CONFIG["commands"]["sector_erase"][i][0]
374
- data = self.CONFIG["commands"]["sector_erase"][i][1]
375
- if len(self.CONFIG["commands"]["sector_erase"][i]) > 2:
376
- we = self.CONFIG["commands"]["sector_erase"][i][2]
377
- else:
378
- we = None
379
-
380
- if addr == "SA": addr = pos
381
- if addr == "SA+1": addr = pos + 1
382
- if addr == "SA+2": addr = pos + 2
383
- if addr == "SA+0x4000": addr = pos + 0x4000
384
- if addr == "SA+0x7000": addr = pos + 0x7000
385
- if not addr == None:
386
- if we == "WR":
387
- self.SET_WE_PIN_WR()
388
- elif we == "AUDIO":
389
- self.SET_WE_PIN_AUDIO()
390
- self.CartWrite([[addr, data]])
391
- if we is not None:
392
- if self.DEFAULT_WE == "WR":
393
- self.SET_WE_PIN_WR()
394
- elif self.DEFAULT_WE == "AUDIO":
395
- self.SET_WE_PIN_AUDIO()
396
-
397
- if self.CONFIG["commands"]["sector_erase_wait_for"][i][0] != None:
398
- addr = self.CONFIG["commands"]["sector_erase_wait_for"][i][0]
399
- data = self.CONFIG["commands"]["sector_erase_wait_for"][i][1]
375
+ def SectorErase(self, pos=0, buffer_pos=0, skip=False):
376
+ if not skip:
377
+ self.Reset(full_reset=False)
378
+ if "sector_erase" not in self.CONFIG["commands"]: return False
379
+ if "sector_size" not in self.CONFIG: return False
380
+ for i in range(0, len(self.CONFIG["commands"]["sector_erase"])):
381
+ addr = self.CONFIG["commands"]["sector_erase"][i][0]
382
+ data = self.CONFIG["commands"]["sector_erase"][i][1]
383
+ if len(self.CONFIG["commands"]["sector_erase"][i]) > 2:
384
+ we = self.CONFIG["commands"]["sector_erase"][i][2]
385
+ else:
386
+ we = None
387
+
400
388
  if addr == "SA": addr = pos
401
389
  if addr == "SA+1": addr = pos + 1
402
390
  if addr == "SA+2": addr = pos + 2
403
- if addr == "SA+0x4000": addr = pos + 0x4000
404
- if addr == "SA+0x7000": addr = pos + 0x7000
405
- time.sleep(0.1)
406
- timeout = 100
407
- while True:
408
- if "wait_read_status_register" in self.CONFIG and self.CONFIG["wait_read_status_register"] == True:
409
- for j in range(0, len(self.CONFIG["commands"]["read_status_register"])):
410
- sr_addr = self.CONFIG["commands"]["read_status_register"][j][0]
411
- sr_data = self.CONFIG["commands"]["read_status_register"][j][1]
391
+ if addr == "SA+16384": addr = pos + 0x4000
392
+ if addr == "SA+28672": addr = pos + 0x7000
393
+ if addr == "SA+66": addr = pos + 0x42
394
+ if addr == "SA+132": addr = pos + 0x84
395
+ if not addr == None:
396
+ if we == "WR":
397
+ self.SET_WE_PIN_WR()
398
+ elif we == "AUDIO":
399
+ self.SET_WE_PIN_AUDIO()
400
+ self.CartWrite([[addr, data]])
401
+ if we is not None:
402
+ if self.DEFAULT_WE == "WR":
403
+ self.SET_WE_PIN_WR()
404
+ elif self.DEFAULT_WE == "AUDIO":
405
+ self.SET_WE_PIN_AUDIO()
406
+
407
+ if self.CONFIG["commands"]["sector_erase_wait_for"][i][0] != None:
408
+ addr = self.CONFIG["commands"]["sector_erase_wait_for"][i][0]
409
+ data = self.CONFIG["commands"]["sector_erase_wait_for"][i][1]
410
+ if addr == "SA": addr = pos
411
+ if addr == "SA+1": addr = pos + 1
412
+ if addr == "SA+2": addr = pos + 2
413
+ if addr == "SA+16384": addr = pos + 0x4000
414
+ if addr == "SA+28672": addr = pos + 0x7000
415
+ if addr == "SA+66": addr = pos + 0x42
416
+ if addr == "SA+132": addr = pos + 0x84
417
+ time.sleep(0.05)
418
+ timeout = 100
419
+ while True:
420
+ if "wait_read_status_register" in self.CONFIG and self.CONFIG["wait_read_status_register"] == True:
421
+ for j in range(0, len(self.CONFIG["commands"]["read_status_register"])):
422
+ sr_addr = self.CONFIG["commands"]["read_status_register"][j][0]
423
+ sr_data = self.CONFIG["commands"]["read_status_register"][j][1]
412
424
 
413
- if we == "WR":
414
- self.SET_WE_PIN_WR()
415
- elif we == "AUDIO":
416
- self.SET_WE_PIN_AUDIO()
417
- self.CartWrite([[sr_addr, sr_data]])
418
- if we is not None:
419
- if self.DEFAULT_WE == "WR":
425
+ if we == "WR":
420
426
  self.SET_WE_PIN_WR()
421
- elif self.DEFAULT_WE == "AUDIO":
427
+ elif we == "AUDIO":
422
428
  self.SET_WE_PIN_AUDIO()
423
-
424
- self.CartRead(addr, 2) # dummy read (fixes some bootlegs)
425
- temp = self.CartRead(addr, 2)
426
- if len(temp) != 2:
427
- dprint("Communication error in SectorErase():", temp)
428
- return False
429
- wait_for = struct.unpack("<H", self.CartRead(addr, 2))[0]
430
- self.LAST_SR = wait_for
431
- dprint("Status Register Check: 0x{:X} & 0x{:X} == 0x{:X}? {:s}".format(wait_for, self.CONFIG["commands"]["sector_erase_wait_for"][i][2], data, str(wait_for & self.CONFIG["commands"]["sector_erase_wait_for"][i][2] == data)))
432
- wait_for = wait_for & self.CONFIG["commands"]["sector_erase_wait_for"][i][2]
433
- time.sleep(0.1)
434
- timeout -= 1
435
- if timeout < 1:
436
- dprint("Timeout error in SectorErase():", self.LAST_SR)
437
- #self.PROGRESS_FNCPTR({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The sector erase attempt timed out. The last status register value was 0x{:X}.\n\nPlease make sure that the cartridge contacts are clean, and that the selected cartridge type and settings are correct.".format(self.LAST_SR), "abortable":False})
438
- return False
439
- if wait_for == data: break
440
- self.PROGRESS_FNCPTR({"action":"SECTOR_ERASE", "sector_pos":buffer_pos, "time_start":time.time(), "abortable":True})
441
- dprint("Done waiting!")
429
+ self.CartWrite([[sr_addr, sr_data]])
430
+ if we is not None:
431
+ if self.DEFAULT_WE == "WR":
432
+ self.SET_WE_PIN_WR()
433
+ elif self.DEFAULT_WE == "AUDIO":
434
+ self.SET_WE_PIN_AUDIO()
435
+
436
+ self.CartRead(addr, 2) # dummy read (fixes some bootlegs)
437
+ temp = self.CartRead(addr, 2)
438
+ if len(temp) != 2:
439
+ dprint("Communication error 1 in SectorErase():", temp)
440
+ return False
441
+ wait_for = self.CartRead(addr, 2)
442
+ if len(wait_for) != 2:
443
+ dprint("Communication error 2 in SectorErase():", temp)
444
+ return False
445
+ wait_for = struct.unpack("<H", wait_for)[0]
446
+ self.LAST_SR = wait_for
447
+ dprint("Status Register Check: 0x{:X} & 0x{:X} == 0x{:X}? {:s}".format(wait_for, self.CONFIG["commands"]["sector_erase_wait_for"][i][2], data, str(wait_for & self.CONFIG["commands"]["sector_erase_wait_for"][i][2] == data)))
448
+ wait_for = wait_for & self.CONFIG["commands"]["sector_erase_wait_for"][i][2]
449
+ time.sleep(0.05)
450
+ timeout -= 1
451
+ if timeout < 1:
452
+ dprint(f"Timeout error in SectorErase(): 0x{self.LAST_SR:X}")
453
+ #self.PROGRESS_FNCPTR({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The sector erase attempt timed out. The last status register value was 0x{:X}.\n\nPlease make sure that the cartridge contacts are clean, and that the selected cartridge type and settings are correct.".format(self.LAST_SR), "abortable":False})
454
+ return False
455
+ if wait_for == data: break
456
+ self.PROGRESS_FNCPTR({"action":"SECTOR_ERASE", "sector_pos":buffer_pos, "time_start":time.time(), "abortable":True})
457
+ dprint("Done waiting!")
442
458
 
443
- self.Reset(full_reset=False)
459
+ self.Reset(full_reset=False)
460
+
444
461
  if isinstance(self.CONFIG["sector_size"], list):
445
462
  self.CONFIG["sector_size"][self.SECTOR_POS][1] -= 1
446
463
  if (self.CONFIG["sector_size"][self.SECTOR_POS][1] == 0) and (len(self.CONFIG["sector_size"]) > self.SECTOR_POS + 1):
@@ -488,16 +505,18 @@ class CFI:
488
505
  magic = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
489
506
  info["d_swap"] = None
490
507
  if magic == "QRY": # nothing swapped
491
- info["d_swap"] = ( 0, 0 )
508
+ info["d_swap"] = [( 0, 0 )]
492
509
  elif magic == "RQZ": # D0D1 swapped
493
- info["d_swap"] = ( 0, 1 )
510
+ info["d_swap"] = [( 0, 1 )]
511
+ elif magic == "\x92\x91\x9A": # D0D1+D6D7 swapped
512
+ info["d_swap"] = [( 0, 1 ), ( 6, 7 )]
494
513
  else:
495
514
  return False
496
515
 
497
516
  if info["d_swap"] is not None:
498
- for i in range(0, len(buffer)):
499
- buffer[i] = bitswap(buffer[i], info["d_swap"])
500
-
517
+ for j2 in range(0, len(info["d_swap"])):
518
+ for j in range(0, len(buffer)):
519
+ buffer[j] = bitswap(buffer[j], info["d_swap"][j2])
501
520
  try:
502
521
  info["flash_id"] = buffer[0:8]
503
522
  info["magic"] = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
@@ -586,7 +605,7 @@ class CFI:
586
605
  return False
587
606
 
588
607
  s = ""
589
- if info["d_swap"] is not None and info["d_swap"] != ( 0, 0 ): s += "Swapped pins: {:s}\n".format(str(info["d_swap"]))
608
+ if info["d_swap"] is not None and info["d_swap"] != [( 0, 0 )]: s += "Swapped pins: {:s}\n".format(str(info["d_swap"]))
590
609
  s += "Device size: 0x{:07X} ({:.2f} MB)\n".format(info["device_size"], info["device_size"] / 1024 / 1024)
591
610
  s += "Voltage: {:.1f}–{:.1f} V\n".format(info["vdd_min"], info["vdd_max"])
592
611
  s += "Single write: {:s}\n".format(str(info["single_write"]))
@@ -612,6 +631,88 @@ class CFI:
612
631
 
613
632
  return info
614
633
 
634
+ class Flashcart_AGB_GBAMP(Flashcart):
635
+ def SectorErase(self, pos=0, buffer_pos=0, skip=False):
636
+ for i in range(0, 4):
637
+ sector = pos >> 13 << 16 | (pos & 0x1FFF) + (i * 4)
638
+ ret = super().SectorErase(sector, buffer_pos, skip)
639
+ if ret is False: break
640
+ return ret
641
+
642
+ def VerifyFlashID(self):
643
+ self.CART_POWERCYCLE_FNCPTR()
644
+ verified = False
645
+ self.Unlock()
646
+ rom = list(self.CartRead(0x1E8F << 1, 2) + self.CartRead(0x168F << 1, 2))
647
+ self.CartWrite(self.CONFIG["commands"]["read_identifier"], fast_write=True)
648
+ cart_flash_id = list(self.CartRead(0x1E8F << 1, 2) + self.CartRead(0x168F << 1, 2))
649
+ if rom != cart_flash_id and cart_flash_id == self.CONFIG["flash_ids"][0]:
650
+ self.CartWrite(self.CONFIG["commands"]["reset"], fast_write=True)
651
+ verified = True
652
+ dprint(verified, rom, cart_flash_id)
653
+ return (verified, cart_flash_id)
654
+
655
+ class Flashcart_DMG_BUNG_16M(Flashcart):
656
+ def SupportsSectorErase(self):
657
+ return False
658
+
659
+ def SupportsChipErase(self):
660
+ return True
661
+
662
+ def ChipErase(self, pos=0, buffer_pos=0, skip=False):
663
+ time_start = time.time()
664
+ if self.PROGRESS_FNCPTR is not None: self.PROGRESS_FNCPTR({"action":"ERASE", "time_start":time_start, "abortable":False})
665
+
666
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
667
+ self.CartWrite([[0x6AAA, 0xAA]], fast_write=True)
668
+ self.CartWrite([[0x2000, 0x01]], fast_write=False)
669
+ self.CartWrite([[0x5554, 0x55]], fast_write=True)
670
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
671
+ self.CartWrite([[0x6AAA, 0x80]], fast_write=True)
672
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
673
+ self.CartWrite([[0x6AAA, 0xAA]], fast_write=True)
674
+ self.CartWrite([[0x2000, 0x01]], fast_write=False)
675
+ self.CartWrite([[0x5554, 0x55]], fast_write=True)
676
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
677
+ self.CartWrite([[0x6AAA, 0x10]], fast_write=True)
678
+
679
+ lives = 10
680
+ while lives > 0:
681
+ sr = ord(self.CartRead(0))
682
+ self.LAST_SR = sr
683
+ dprint("Status Register Check: 0x{:X} & 0x{:X} == 0x{:X}? {:s}".format(sr, 0x80, 0x80, str((sr & 0x80) == 0x80)))
684
+ if (sr & 0x80) == 0x80: break
685
+ time.sleep(0.5)
686
+ lives -= 1
687
+ if lives == 0:
688
+ self.PROGRESS_FNCPTR({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Erasing the flash chip timed out. The last status register value was 0x{:X}.\n\nPlease make sure that the cartridge contacts are clean, and that the selected cartridge type and settings are correct.".format(self.LAST_SR), "abortable":False})
689
+ return False
690
+
691
+ self.Reset()
692
+ return True
693
+
694
+ def Reset(self, full_reset=None, max_address=None):
695
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
696
+ self.CartWrite([[0x6AAA, 0xAA]], fast_write=True)
697
+ self.CartWrite([[0x2000, 0x01]], fast_write=False)
698
+ self.CartWrite([[0x5554, 0x55]], fast_write=True)
699
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
700
+ self.CartWrite([[0x6AAA, 0xF0]], fast_write=True)
701
+
702
+ def VerifyFlashID(self):
703
+ rom = list(self.CartRead(0, 4))
704
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
705
+ self.CartWrite([[0x6AAA, 0xAA]], fast_write=True)
706
+ self.CartWrite([[0x2000, 0x01]], fast_write=False)
707
+ self.CartWrite([[0x5554, 0x55]], fast_write=True)
708
+ self.CartWrite([[0x2000, 0x02]], fast_write=False)
709
+ self.CartWrite([[0x6AAA, 0x90]], fast_write=True)
710
+ cart_flash_id = list(self.CartRead(0, 4))
711
+ if rom != cart_flash_id and cart_flash_id == self.CONFIG["flash_ids"][0]:
712
+ self.Reset()
713
+ verified = True
714
+ return (verified, cart_flash_id)
715
+
615
716
  class Flashcart_DMG_MMSA(Flashcart):
616
717
  def ReadCFI(self):
617
718
  return False
@@ -628,7 +729,7 @@ class Flashcart_DMG_MMSA(Flashcart):
628
729
  def EraseHiddenSector(self, buffer):
629
730
  if self.PROGRESS_FNCPTR is not None: self.PROGRESS_FNCPTR({"action":"SECTOR_ERASE", "sector_pos":0, "time_start":time.time(), "abortable":False})
630
731
 
631
- self.UnlockForWriting()
732
+ if self.UnlockForWriting() is False: return False
632
733
 
633
734
  cmds = [
634
735
  [ 0x120, 0x0F ],
@@ -748,13 +849,13 @@ class Flashcart_DMG_MMSA(Flashcart):
748
849
  # Disable writes to MBC registers
749
850
  cmds = [
750
851
  [ 0x120, 0x10 ],
751
- [ 0x13F, 0xa5 ],
852
+ [ 0x13F, 0xA5 ],
752
853
  ]
753
854
  self.CartWrite(cmds)
754
855
  # Undo Wakeup
755
856
  cmds = [
756
857
  [ 0x120, 0x08 ],
757
- [ 0x13F, 0xa5 ],
858
+ [ 0x13F, 0xA5 ],
758
859
  ]
759
860
  self.CartWrite(cmds)
760
861
  return True
@@ -763,7 +864,7 @@ class Flashcart_DMG_MMSA(Flashcart):
763
864
  time_start = time.time()
764
865
  if self.PROGRESS_FNCPTR is not None: self.PROGRESS_FNCPTR({"action":"ERASE", "time_start":time_start, "abortable":False})
765
866
 
766
- self.UnlockForWriting()
867
+ if self.UnlockForWriting() is False: return False
767
868
 
768
869
  # Erase Chip
769
870
  cmds = [
FlashGBX/GBMemory.py CHANGED
@@ -11,10 +11,9 @@ class GBMemoryMap:
11
11
  IS_MENU = False
12
12
 
13
13
  def __init__(self, rom=None, oldmap=None):
14
+ self.MAP_DATA = bytearray([0xFF] * 0x80)
14
15
  if rom is None: return
15
- if rom == bytearray([0xFF] * len(rom)):
16
- self.MAP_DATA = bytearray([0x00] * 0x80)
17
- elif rom is not None:
16
+ if rom is not None:
18
17
  self.ImportROM(rom)
19
18
  if oldmap is not None:
20
19
  self.MAP_DATA[0x70:0x78] = oldmap[0x70:0x78] # keep existing cart id
@@ -316,6 +315,6 @@ class GBMemoryMap:
316
315
  return self.IS_MENU
317
316
 
318
317
  def GetMapData(self):
319
- if self.MAP_DATA == bytearray([0xFF] * 0x80):
320
- return False
318
+ #if self.MAP_DATA == bytearray([0xFF] * 0x80):
319
+ # return False
321
320
  return self.MAP_DATA