FlashGBX 3.36__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
 
@@ -133,6 +140,10 @@ class Flashcart:
133
140
  if "flash_size" not in self.CONFIG: return default
134
141
  return self.CONFIG["flash_size"]
135
142
 
143
+ def SetFlashSize(self, size):
144
+ if "flash_size" not in self.CONFIG: return
145
+ self.CONFIG["flash_size"] = size
146
+
136
147
  def GetBufferSize(self):
137
148
  if "buffer_size" in self.CONFIG:
138
149
  return self.CONFIG["buffer_size"]
@@ -176,17 +187,17 @@ class Flashcart:
176
187
  if full_reset and "power_cycle" in self.CONFIG:
177
188
  self.CART_POWERCYCLE_FNCPTR()
178
189
  time.sleep(0.001)
179
- self.Unlock()
190
+ if self.Unlock() is False: return False
180
191
  elif full_reset and "reset_every" in self.CONFIG and "flash_size" in self.CONFIG:
181
192
  for j in range(0, self.CONFIG["flash_size"], self.CONFIG["reset_every"]):
182
193
  if j >= max_address: break
183
194
  dprint("reset_every @ 0x{:X}".format(j))
184
195
  for command in self.CONFIG["commands"]["reset"]:
185
196
  self.CartWrite([[j + command[0], command[1]]])
186
- time.sleep(0.01)
197
+ #time.sleep(0.01)
187
198
  elif "reset" in self.CONFIG["commands"]:
188
199
  self.CartWrite(self.CONFIG["commands"]["reset"])
189
- time.sleep(0.001)
200
+ #time.sleep(0.001)
190
201
 
191
202
  def _VerifyFlashID(self, config):
192
203
  if "read_identifier" not in config["commands"]: return (False, [])
@@ -234,7 +245,7 @@ class Flashcart:
234
245
  if self.CFI is not None: return self.CFI
235
246
  if "read_cfi" not in self.CONFIG["commands"]:
236
247
  if self.CONFIG["_command_set"] == "INTEL":
237
- self.CONFIG["commands"]["read_cfi"] = self.CONFIG["commands"]["read_identifier"]
248
+ self.CONFIG["commands"]["read_cfi"] = [ [ 0, 0x98 ] ]
238
249
  elif self.CONFIG["_command_set"] == "AMD":
239
250
  self.CONFIG["commands"]["read_cfi"] = [ [ 0xAA, 0x98 ] ]
240
251
 
@@ -361,82 +372,92 @@ class Flashcart:
361
372
  self.Reset(full_reset=True)
362
373
  return True
363
374
 
364
- def SectorErase(self, pos=0, buffer_pos=0):
365
- self.Reset(full_reset=False)
366
- if "sector_erase" not in self.CONFIG["commands"]: return False
367
- if "sector_size" not in self.CONFIG: return False
368
- for i in range(0, len(self.CONFIG["commands"]["sector_erase"])):
369
- addr = self.CONFIG["commands"]["sector_erase"][i][0]
370
- data = self.CONFIG["commands"]["sector_erase"][i][1]
371
- if len(self.CONFIG["commands"]["sector_erase"][i]) > 2:
372
- we = self.CONFIG["commands"]["sector_erase"][i][2]
373
- else:
374
- we = None
375
-
376
- if addr == "SA": addr = pos
377
- if addr == "SA+1": addr = pos + 1
378
- if addr == "SA+2": addr = pos + 2
379
- if addr == "SA+0x4000": addr = pos + 0x4000
380
- if addr == "SA+0x7000": addr = pos + 0x7000
381
- if not addr == None:
382
- if we == "WR":
383
- self.SET_WE_PIN_WR()
384
- elif we == "AUDIO":
385
- self.SET_WE_PIN_AUDIO()
386
- self.CartWrite([[addr, data]])
387
- if we is not None:
388
- if self.DEFAULT_WE == "WR":
389
- self.SET_WE_PIN_WR()
390
- elif self.DEFAULT_WE == "AUDIO":
391
- self.SET_WE_PIN_AUDIO()
392
-
393
- if self.CONFIG["commands"]["sector_erase_wait_for"][i][0] != None:
394
- addr = self.CONFIG["commands"]["sector_erase_wait_for"][i][0]
395
- 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
+
396
388
  if addr == "SA": addr = pos
397
389
  if addr == "SA+1": addr = pos + 1
398
390
  if addr == "SA+2": addr = pos + 2
399
- if addr == "SA+0x4000": addr = pos + 0x4000
400
- if addr == "SA+0x7000": addr = pos + 0x7000
401
- time.sleep(0.1)
402
- timeout = 100
403
- while True:
404
- if "wait_read_status_register" in self.CONFIG and self.CONFIG["wait_read_status_register"] == True:
405
- for j in range(0, len(self.CONFIG["commands"]["read_status_register"])):
406
- sr_addr = self.CONFIG["commands"]["read_status_register"][j][0]
407
- 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]
408
424
 
409
- if we == "WR":
410
- self.SET_WE_PIN_WR()
411
- elif we == "AUDIO":
412
- self.SET_WE_PIN_AUDIO()
413
- self.CartWrite([[sr_addr, sr_data]])
414
- if we is not None:
415
- if self.DEFAULT_WE == "WR":
425
+ if we == "WR":
416
426
  self.SET_WE_PIN_WR()
417
- elif self.DEFAULT_WE == "AUDIO":
427
+ elif we == "AUDIO":
418
428
  self.SET_WE_PIN_AUDIO()
419
-
420
- self.CartRead(addr, 2) # dummy read (fixes some bootlegs)
421
- temp = self.CartRead(addr, 2)
422
- if len(temp) != 2:
423
- dprint("Communication error in SectorErase():", temp)
424
- return False
425
- wait_for = struct.unpack("<H", self.CartRead(addr, 2))[0]
426
- self.LAST_SR = wait_for
427
- 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)))
428
- wait_for = wait_for & self.CONFIG["commands"]["sector_erase_wait_for"][i][2]
429
- time.sleep(0.1)
430
- timeout -= 1
431
- if timeout < 1:
432
- dprint("Timeout error in SectorErase():", self.LAST_SR)
433
- #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})
434
- return False
435
- if wait_for == data: break
436
- self.PROGRESS_FNCPTR({"action":"SECTOR_ERASE", "sector_pos":buffer_pos, "time_start":time.time(), "abortable":True})
437
- 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!")
438
458
 
439
- self.Reset(full_reset=False)
459
+ self.Reset(full_reset=False)
460
+
440
461
  if isinstance(self.CONFIG["sector_size"], list):
441
462
  self.CONFIG["sector_size"][self.SECTOR_POS][1] -= 1
442
463
  if (self.CONFIG["sector_size"][self.SECTOR_POS][1] == 0) and (len(self.CONFIG["sector_size"]) > self.SECTOR_POS + 1):
@@ -450,11 +471,17 @@ class Flashcart:
450
471
  else:
451
472
  return self.CONFIG["sector_size"]
452
473
 
474
+ def HasBanks(self):
475
+ return "flash_bank_select_type" in self.CONFIG
476
+
453
477
  def SelectBankROM(self, index):
454
478
  if "flash_bank_select_type" not in self.CONFIG: return False
479
+ dprint(f"Setting flash bank to {index:d}")
455
480
  if self.CONFIG["flash_bank_select_type"] == 1:
456
- dprint(self.GetName(), "|", index)
481
+ index = index & 0xF
457
482
  self.CartWrite([[2, index << 4]], sram=True)
483
+ self.CartWrite([[3, 0x40]], sram=True)
484
+ self.CartWrite([[4, 0x00]], sram=True)
458
485
  return True
459
486
  elif self.CONFIG["flash_bank_select_type"] == 2: # Flash2Advance Ultra
460
487
  bank1 = 0 if index < 4 else 0x10
@@ -478,16 +505,18 @@ class CFI:
478
505
  magic = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
479
506
  info["d_swap"] = None
480
507
  if magic == "QRY": # nothing swapped
481
- info["d_swap"] = ( 0, 0 )
508
+ info["d_swap"] = [( 0, 0 )]
482
509
  elif magic == "RQZ": # D0D1 swapped
483
- 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 )]
484
513
  else:
485
514
  return False
486
515
 
487
516
  if info["d_swap"] is not None:
488
- for i in range(0, len(buffer)):
489
- buffer[i] = bitswap(buffer[i], info["d_swap"])
490
-
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])
491
520
  try:
492
521
  info["flash_id"] = buffer[0:8]
493
522
  info["magic"] = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
@@ -576,7 +605,7 @@ class CFI:
576
605
  return False
577
606
 
578
607
  s = ""
579
- 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"]))
580
609
  s += "Device size: 0x{:07X} ({:.2f} MB)\n".format(info["device_size"], info["device_size"] / 1024 / 1024)
581
610
  s += "Voltage: {:.1f}–{:.1f} V\n".format(info["vdd_min"], info["vdd_max"])
582
611
  s += "Single write: {:s}\n".format(str(info["single_write"]))
@@ -602,6 +631,88 @@ class CFI:
602
631
 
603
632
  return info
604
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
+
605
716
  class Flashcart_DMG_MMSA(Flashcart):
606
717
  def ReadCFI(self):
607
718
  return False
@@ -618,7 +729,7 @@ class Flashcart_DMG_MMSA(Flashcart):
618
729
  def EraseHiddenSector(self, buffer):
619
730
  if self.PROGRESS_FNCPTR is not None: self.PROGRESS_FNCPTR({"action":"SECTOR_ERASE", "sector_pos":0, "time_start":time.time(), "abortable":False})
620
731
 
621
- self.UnlockForWriting()
732
+ if self.UnlockForWriting() is False: return False
622
733
 
623
734
  cmds = [
624
735
  [ 0x120, 0x0F ],
@@ -738,13 +849,13 @@ class Flashcart_DMG_MMSA(Flashcart):
738
849
  # Disable writes to MBC registers
739
850
  cmds = [
740
851
  [ 0x120, 0x10 ],
741
- [ 0x13F, 0xa5 ],
852
+ [ 0x13F, 0xA5 ],
742
853
  ]
743
854
  self.CartWrite(cmds)
744
855
  # Undo Wakeup
745
856
  cmds = [
746
857
  [ 0x120, 0x08 ],
747
- [ 0x13F, 0xa5 ],
858
+ [ 0x13F, 0xA5 ],
748
859
  ]
749
860
  self.CartWrite(cmds)
750
861
  return True
@@ -753,7 +864,7 @@ class Flashcart_DMG_MMSA(Flashcart):
753
864
  time_start = time.time()
754
865
  if self.PROGRESS_FNCPTR is not None: self.PROGRESS_FNCPTR({"action":"ERASE", "time_start":time_start, "abortable":False})
755
866
 
756
- self.UnlockForWriting()
867
+ if self.UnlockForWriting() is False: return False
757
868
 
758
869
  # Erase Chip
759
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