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/LK_Device.py ADDED
@@ -0,0 +1,4526 @@
1
+ # -*- coding: utf-8 -*-
2
+ # FlashGBX
3
+ # Author: Lesserkuma (github.com/lesserkuma)
4
+
5
+ import time, math, struct, traceback, zlib, copy, hashlib, os, datetime, platform, json, base64
6
+ import serial, serial.tools.list_ports
7
+ from serial import SerialException
8
+ from abc import ABC, abstractmethod
9
+ from .RomFileDMG import RomFileDMG
10
+ from .RomFileAGB import RomFileAGB
11
+ from .Mapper import DMG_MBC, AGB_GPIO
12
+ from .Flashcart import Flashcart, Flashcart_DMG_MMSA, Flashcart_AGB_GBAMP, Flashcart_DMG_BUNG_16M
13
+ from .Util import ANSI, dprint, bitswap, ParseCFI
14
+ from .GBMemory import GBMemoryMap
15
+ from . import Util
16
+
17
+ class LK_Device(ABC):
18
+ DEVICE_NAME = ""
19
+ DEVICE_MIN_FW = 0
20
+ DEVICE_MAX_FW = 0
21
+ DEVICE_LATEST_FW_TS = {}
22
+ PCB_VERSIONS = {}
23
+ BAUDRATE = 1000000
24
+ MAX_BUFFER_READ = 0x2000
25
+ MAX_BUFFER_WRITE = 0x400
26
+
27
+ DEVICE_CMD = {
28
+ "NULL":0x30,
29
+ "OFW_RESET_AVR":0x2A,
30
+ "OFW_CART_MODE":0x43,
31
+ "OFW_FW_VER":0x56,
32
+ "OFW_PCB_VER":0x68,
33
+ "OFW_USART_1_0M_SPEED":0x3C,
34
+ "OFW_USART_1_7M_SPEED":0x3E,
35
+ "OFW_CART_PWR_ON":0x2F,
36
+ "OFW_CART_PWR_OFF":0x2E,
37
+ "OFW_QUERY_CART_PWR":0x5D,
38
+ "OFW_DONE_LED_ON":0x3D,
39
+ "OFW_ERROR_LED_ON":0x3F,
40
+ "OFW_GB_CART_MODE":0x47,
41
+ "OFW_GB_FLASH_BANK_1_COMMAND_WRITES":0x4E,
42
+ "OFW_LNL_QUERY":0x25,
43
+ "DEBUG":0xA0,
44
+ "QUERY_FW_INFO":0xA1,
45
+ "SET_MODE_AGB":0xA2,
46
+ "SET_MODE_DMG":0xA3,
47
+ "SET_VOLTAGE_3_3V":0xA4,
48
+ "SET_VOLTAGE_5V":0xA5,
49
+ "SET_VARIABLE":0xA6,
50
+ "SET_FLASH_CMD":0xA7,
51
+ "SET_ADDR_AS_INPUTS":0xA8,
52
+ "CLK_TOGGLE":0xA9,
53
+ "ENABLE_PULLUPS":0xAB,
54
+ "DISABLE_PULLUPS":0xAC,
55
+ "GET_VARIABLE":0xAD,
56
+ "GET_VAR_STATE":0xAE,
57
+ "SET_VAR_STATE":0xAF,
58
+ "DMG_CART_READ":0xB1,
59
+ "DMG_CART_WRITE":0xB2,
60
+ "DMG_CART_WRITE_SRAM":0xB3,
61
+ "DMG_MBC_RESET":0xB4,
62
+ "DMG_MBC7_READ_EEPROM":0xB5,
63
+ "DMG_MBC7_WRITE_EEPROM":0xB6,
64
+ "DMG_MBC6_MMSA_WRITE_FLASH":0xB7,
65
+ "DMG_SET_BANK_CHANGE_CMD":0xB8,
66
+ "DMG_EEPROM_WRITE":0xB9,
67
+ "DMG_CART_READ_MEASURE":0xBA,
68
+ "AGB_CART_READ":0xC1,
69
+ "AGB_CART_WRITE":0xC2,
70
+ "AGB_CART_READ_SRAM":0xC3,
71
+ "AGB_CART_WRITE_SRAM":0xC4,
72
+ "AGB_CART_READ_EEPROM":0xC5,
73
+ "AGB_CART_WRITE_EEPROM":0xC6,
74
+ "AGB_CART_WRITE_FLASH_DATA":0xC7,
75
+ "AGB_CART_READ_3D_MEMORY":0xC8,
76
+ "AGB_BOOTUP_SEQUENCE":0xC9,
77
+ "AGB_READ_GPIO_RTC":0xCA,
78
+ "DMG_FLASH_WRITE_BYTE":0xD1,
79
+ "AGB_FLASH_WRITE_SHORT":0xD2,
80
+ "FLASH_PROGRAM":0xD3,
81
+ "CART_WRITE_FLASH_CMD":0xD4,
82
+ "CALC_CRC32":0xD5,
83
+ "BOOTLOADER_RESET":0xF1,
84
+ "CART_PWR_ON":0xF2,
85
+ "CART_PWR_OFF":0xF3,
86
+ "QUERY_CART_PWR":0xF4,
87
+ "SET_PIN":0xF5,
88
+ }
89
+ # \#define VAR(\d+)_([^\t]+)\t+(.+)
90
+ DEVICE_VAR = {
91
+ "ADDRESS":[32, 0x00],
92
+ "AUTO_POWEROFF_TIME":[32, 0x01],
93
+ "TRANSFER_SIZE":[16, 0x00],
94
+ "BUFFER_SIZE":[16, 0x01],
95
+ "DMG_ROM_BANK":[16, 0x02],
96
+ "STATUS_REGISTER":[16, 0x03],
97
+ "LAST_BANK_ACCESSED":[16, 0x04],
98
+ "STATUS_REGISTER_MASK":[16, 0x05],
99
+ "STATUS_REGISTER_VALUE":[16, 0x06],
100
+ "CART_MODE":[8, 0x00],
101
+ "DMG_ACCESS_MODE":[8, 0x01],
102
+ "FLASH_COMMAND_SET":[8, 0x02],
103
+ "FLASH_METHOD":[8, 0x03],
104
+ "FLASH_WE_PIN":[8, 0x04],
105
+ "FLASH_PULSE_RESET":[8, 0x05],
106
+ "FLASH_COMMANDS_BANK_1":[8, 0x06],
107
+ "FLASH_SHARP_VERIFY_SR":[8, 0x07],
108
+ "DMG_READ_CS_PULSE":[8, 0x08],
109
+ "DMG_WRITE_CS_PULSE":[8, 0x09],
110
+ "FLASH_DOUBLE_DIE":[8, 0x0A],
111
+ "DMG_READ_METHOD":[8, 0x0B],
112
+ "AGB_READ_METHOD":[8, 0x0C],
113
+ "CART_POWERED":[8, 0x0D],
114
+ "PULLUPS_ENABLED":[8, 0x0E],
115
+ "AUTO_POWEROFF_ENABLED":[8, 0x0F],
116
+ "AGB_IRQ_ENABLED":[8, 0x10],
117
+ }
118
+
119
+ ACTIONS = {"ROM_READ":1, "SAVE_READ":2, "SAVE_WRITE":3, "ROM_WRITE":4, "ROM_WRITE_VERIFY":4, "SAVE_WRITE_VERIFY":3, "RTC_WRITE":5}
120
+ SUPPORTED_CARTS = {}
121
+
122
+ FW = {}
123
+ FW_UPDATE_REQ = False
124
+ FW_VAR = {}
125
+ MODE = None
126
+ PORT = ''
127
+ DEVICE = None
128
+ WORKER = None
129
+ INFO = { "action":None, "last_action":None, "dump_info":{} }
130
+ ERROR = False
131
+ ERROR_ARGS = {}
132
+ CANCEL = False
133
+ CANCEL_ARGS = {}
134
+ SIGNAL = None
135
+ POS = 0
136
+ NO_PROG_UPDATE = False
137
+ FAST_READ = False
138
+ SKIPPING = False
139
+ DEVICE_TIMEOUT = 1
140
+ WRITE_DELAY = False
141
+ READ_ERRORS = 0
142
+ WRITE_ERRORS = 0
143
+ DMG_READ_METHOD = 1
144
+ DMG_READ_METHODS = ["RD", "A15", "SlowA15"]
145
+ AGB_READ_METHOD = 0
146
+ AGB_READ_METHODS = ["Single", "MemCpy", "Stream"]
147
+ LAST_CHECK_ACTIVE = 0
148
+ USER_ANSWER = None
149
+
150
+ def __init__(self):
151
+ pass
152
+
153
+ @abstractmethod
154
+ def Initialize(self, flashcarts, port=None, max_baud=2000000):
155
+ raise NotImplementedError
156
+
157
+ @abstractmethod
158
+ def CheckActive(self):
159
+ raise NotImplementedError
160
+
161
+ @abstractmethod
162
+ def LoadFirmwareVersion(self):
163
+ raise NotImplementedError
164
+
165
+ @abstractmethod
166
+ def GetFirmwareVersion(self, more=False):
167
+ raise NotImplementedError
168
+
169
+ @abstractmethod
170
+ def ChangeBaudRate(self, baudrate):
171
+ raise NotImplementedError
172
+
173
+ @abstractmethod
174
+ def CanSetVoltageManually(self):
175
+ raise NotImplementedError
176
+
177
+ @abstractmethod
178
+ def CanSetVoltageAutomatically(self):
179
+ raise NotImplementedError
180
+
181
+ @abstractmethod
182
+ def CanPowerCycleCart(self):
183
+ raise NotImplementedError
184
+
185
+ @abstractmethod
186
+ def GetSupprtedModes(self):
187
+ raise NotImplementedError
188
+
189
+ @abstractmethod
190
+ def IsSupported3dMemory(self):
191
+ raise NotImplementedError
192
+
193
+ @abstractmethod
194
+ def IsClkConnected(self):
195
+ raise NotImplementedError
196
+
197
+ @abstractmethod
198
+ def GetFullNameExtended(self, more=False):
199
+ raise NotImplementedError
200
+
201
+ @abstractmethod
202
+ def SupportsFirmwareUpdates(self):
203
+ raise NotImplementedError
204
+
205
+ @abstractmethod
206
+ def FirmwareUpdateAvailable(self):
207
+ raise NotImplementedError
208
+
209
+ @abstractmethod
210
+ def GetFirmwareUpdaterClass(self):
211
+ raise NotImplementedError
212
+
213
+ @abstractmethod
214
+ def ResetLEDs(self):
215
+ raise NotImplementedError
216
+
217
+ @abstractmethod
218
+ def SupportsBootloaderReset(self):
219
+ raise NotImplementedError
220
+
221
+ @abstractmethod
222
+ def BootloaderReset(self):
223
+ raise NotImplementedError
224
+
225
+ @abstractmethod
226
+ def SupportsAudioAsWe(self):
227
+ raise NotImplementedError
228
+
229
+ #################################################################
230
+
231
+ def IsSupportedMbc(self, mbc):
232
+ return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x201, 0x202, 0x203, 0x204, 0x205 )
233
+
234
+ def TryConnect(self, port, baudrate):
235
+ dprint("Trying to connect to {:s} at baud rate {:d} ({:s})".format(port, baudrate, type(self).__module__))
236
+ try:
237
+ dev = serial.Serial(port, baudrate, timeout=0.1)
238
+ except SerialException as e:
239
+ dprint(f"Couldn’t connect to port {port:s} at baudrate {baudrate:d}:", e)
240
+ return False
241
+ self.DEVICE = dev
242
+ check = self.LoadFirmwareVersion()
243
+ self.DEVICE = None
244
+ dev.close()
245
+ return check
246
+
247
+ def GetBaudRate(self):
248
+ return self.BAUDRATE
249
+
250
+ def SetDMGReadMethod(self, method):
251
+ if self.FW["fw_ver"] < 12: return
252
+ if 0 < method >= len(self.DMG_READ_METHODS): method = 0
253
+ dprint("Setting DMG Read Method to", method)
254
+ self.DMG_READ_METHOD = method
255
+ self._set_fw_variable("DMG_READ_METHOD", self.DMG_READ_METHOD)
256
+
257
+ def SetAGBReadMethod(self, method):
258
+ if self.FW["fw_ver"] < 12: return
259
+ if 0 < method >= len(self.AGB_READ_METHODS): method = 0
260
+ dprint("Setting AGB Read Method to", method)
261
+ self.AGB_READ_METHOD = method
262
+ self._set_fw_variable("AGB_READ_METHOD", self.AGB_READ_METHOD)
263
+
264
+ def SetPin(self, pins: list, set_high):
265
+ if self.FW["fw_ver"] < 12: return
266
+ pin_names = [ "CART_POWER", "PIN_CLK", "PIN_WR", "PIN_RD", "PIN_CS" ]
267
+ for i in range(0, 24):
268
+ pin_names.append(f"PIN_A{i}")
269
+ pin_names += [ "PIN_CS2", "PIN_AUDIO" ]
270
+ value = 0
271
+ p = 0
272
+ for p in pins:
273
+ if isinstance(p, int):
274
+ if p > 30:
275
+ print("Invalid pin index specified:", p)
276
+ continue
277
+ elif isinstance(p, str):
278
+ if p not in pin_names:
279
+ print("Invalid pin index specified:", p)
280
+ continue
281
+ p = pin_names.index(p)
282
+ value |= (1 << p)
283
+
284
+ for i in range(0, 31):
285
+ if (value >> i & 1) == 1:
286
+ dprint(f"Setting pin {pin_names[p]}: {set_high}")
287
+
288
+ dprint(f"Value: {value:031b}")
289
+ buffer = bytearray([self.DEVICE_CMD["SET_PIN"]])
290
+ buffer.extend(struct.pack(">I", value))
291
+ buffer.extend(struct.pack("B", 1 if set_high else 0))
292
+ return self._write(buffer, wait=True)
293
+
294
+ def UpdateFlashCarts(self, flashcarts):
295
+ self.SUPPORTED_CARTS = {
296
+ "DMG":{ "Generic ROM Cartridge":"RETAIL" },
297
+ "AGB":{ "Generic ROM Cartridge":"RETAIL" }
298
+ }
299
+ for mode in flashcarts.keys():
300
+ for key in sorted(flashcarts[mode].keys(), key=str.casefold):
301
+ self.SUPPORTED_CARTS[mode][key] = flashcarts[mode][key]
302
+
303
+ def IsConnected(self):
304
+ if self.DEVICE is None: return False
305
+ if not self.DEVICE.isOpen(): return False
306
+ try:
307
+ while self.DEVICE.in_waiting > 0:
308
+ dprint("Clearing input buffer... ({:d})".format(self.DEVICE.in_waiting), self.DEVICE.read(self.DEVICE.in_waiting))
309
+ self.DEVICE.reset_input_buffer()
310
+ time.sleep(0.05)
311
+ self.DEVICE.reset_output_buffer()
312
+ return self.CheckActive()
313
+ except SerialException as e:
314
+ print("Connection lost!")
315
+ try:
316
+ if e.args[0].startswith("ClearCommError failed"):
317
+ self.DEVICE.close()
318
+ return False
319
+ except:
320
+ pass
321
+ print(str(e))
322
+ return False
323
+
324
+ def Close(self, cartPowerOff=False):
325
+ if self.IsConnected():
326
+ dprint("Disconnecting from the device")
327
+ try:
328
+ if cartPowerOff and self.CanPowerCycleCart():
329
+ self._set_fw_variable("AUTO_POWEROFF_TIME", 0)
330
+ if self.FW["fw_ver"] >= 12:
331
+ self._write(self.DEVICE_CMD["CART_PWR_OFF"], wait=True)
332
+ else:
333
+ self._write(self.DEVICE_CMD["OFW_CART_PWR_OFF"], wait=False)
334
+ else:
335
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
336
+ self.DEVICE.close()
337
+ except:
338
+ self.DEVICE = None
339
+ self.MODE = None
340
+
341
+ def GetName(self):
342
+ return self.DEVICE_NAME
343
+
344
+ def GetFullNameLabel(self):
345
+ return "{:s} – Firmware {:s}".format(self.GetFullName(), self.GetFirmwareVersion())
346
+
347
+ def GetPCBVersion(self):
348
+ if self.FW["pcb_ver"] in self.PCB_VERSIONS:
349
+ return self.PCB_VERSIONS[self.FW["pcb_ver"]]
350
+ else:
351
+ return "(unknown revision)"
352
+
353
+ def GetFullName(self):
354
+ if len(self.GetPCBVersion()) > 0:
355
+ return "{:s} {:s}".format(self.GetName(), self.GetPCBVersion())
356
+ else:
357
+ return self.GetName()
358
+
359
+ def GetPort(self):
360
+ return self.PORT
361
+
362
+ def GetFWBuildDate(self):
363
+ return self.FW["fw_dt"]
364
+
365
+ def SetWriteDelay(self, enable=True):
366
+ if self.WRITE_DELAY != enable:
367
+ dprint("Setting Write Delay to", enable)
368
+ self.WRITE_DELAY = enable
369
+
370
+ def SetTimeout(self, seconds=1):
371
+ if seconds < 0.1: seconds = 0.1
372
+ self.DEVICE_TIMEOUT = seconds
373
+ self.DEVICE.timeout = self.DEVICE_TIMEOUT
374
+
375
+ def AbortOperation(self, from_user=True):
376
+ self.CANCEL_ARGS["from_user"] = from_user
377
+ self.CANCEL = True
378
+ self.ERROR = False
379
+
380
+ def wait_for_ack(self, values=None):
381
+ if values is None: values = [0x01, 0x03]
382
+ buffer = self._read(1)
383
+ if buffer not in values:
384
+ tb_stack = traceback.extract_stack()
385
+ stack = tb_stack[len(tb_stack)-2] # caller only
386
+ if stack.name == "_write": stack = tb_stack[len(tb_stack)-3]
387
+ dprint("CANCEL_ARGS:", self.CANCEL_ARGS)
388
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
389
+ return False
390
+ elif buffer is False:
391
+ dprint("Timeout error ({:s}(), line {:d}): {:f}".format(stack.name, stack.lineno, self.DEVICE.timeout))
392
+ dprint("Traceback:\n", ''.join(traceback.format_stack()[:-1]))
393
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A timeout error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
394
+ elif buffer == 2:
395
+ dprint("Error reported ({:s}(), line {:d}): {:s}".format(stack.name, stack.lineno, str(buffer)))
396
+ dprint("Traceback:\n", ''.join(traceback.format_stack()[:-1]))
397
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"The device reported an error while running {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
398
+ else:
399
+ dprint("Communication error ({:s}(), line {:d}): {:s}".format(stack.name, stack.lineno, str(buffer)))
400
+ dprint("Traceback:\n", ''.join(traceback.format_stack()[:-1]))
401
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A communication error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
402
+ self.ERROR = True
403
+ self.CANCEL = True
404
+ self.WRITE_ERRORS += 1
405
+ if self.WRITE_ERRORS > 3:
406
+ self.SetWriteDelay(enable=True)
407
+ return False
408
+ return buffer
409
+
410
+ def _try_write(self, data, retries=5):
411
+ while retries > 0:
412
+ ack = self._write(data, wait=True)
413
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
414
+ return False
415
+ if ack is not False:
416
+ self.ERROR = False
417
+ self.CANCEL = False
418
+ self.CANCEL_ARGS = {}
419
+ return ack
420
+ retries -= 1
421
+ dprint("Retries left:", retries)
422
+
423
+ hp = 10
424
+ temp = 0
425
+ while temp not in (1, 2) and hp > 0:
426
+ self.DEVICE.write(b'\x00')
427
+ temp = self._read(1)
428
+ dprint("Current response:", temp)
429
+ hp -= 1
430
+ if hp == 0: break
431
+ return False
432
+
433
+ def _write(self, data, wait=False):
434
+ if not isinstance(data, bytearray):
435
+ data = bytearray([data])
436
+
437
+ if Util.DEBUG:
438
+ dstr = ' '.join(format(x, '02X') for x in data)
439
+ cmd = ""
440
+ if len(data) < 32:
441
+ try:
442
+ cmd = "[{:s}] ".format((list(self.DEVICE_CMD.keys())[list(self.DEVICE_CMD.values()).index(data[0])]))
443
+ except:
444
+ pass
445
+ dprint("[{:02X}] {:s}{:s}".format(int(len(dstr)/3) + 1, cmd, dstr[:96]))
446
+
447
+ self.DEVICE.write(data)
448
+ self.DEVICE.flush()
449
+
450
+ # On MacOS it’s possible not all bytes are transmitted successfully,
451
+ # even though we’re using flush() which is the tcdrain function.
452
+ # Still looking for a better solution than delaying here.
453
+ if self.WRITE_DELAY is True or (platform.system() == "Darwin" and ("pcb_name" not in self.FW or self.FW["pcb_name"] == "GBxCart RW")):
454
+ time.sleep(0.0014)
455
+
456
+ if wait: return self.wait_for_ack()
457
+
458
+ def _read(self, count):
459
+ if self.DEVICE.in_waiting > 1000: dprint("Warning: in_waiting={:d} bytes".format(self.DEVICE.in_waiting))
460
+ buffer = self.DEVICE.read(count)
461
+
462
+ if len(buffer) != count:
463
+ hp = 50
464
+ while (self.DEVICE.in_waiting != (count - len(buffer))) and hp > 0:
465
+ time.sleep(0.01)
466
+ hp -= 1
467
+ if hp > 0:
468
+ # dprint(f"Recovery: {self.DEVICE.in_waiting} byte(s) in queue! (HP: {hp}/50)")
469
+ buffer += self.DEVICE.read(count)
470
+
471
+ if len(buffer) != count:
472
+ tb_stack = traceback.extract_stack()
473
+ stack = tb_stack[len(tb_stack)-2] # caller only
474
+ if stack.name == "_read": stack = tb_stack[len(tb_stack)-3]
475
+ dprint("Error: Received only {:d} of {:d} byte(s) ({:s}(), line {:d})".format(len(buffer), count, stack.name, stack.lineno))
476
+ dprint("Timeout value:", self.DEVICE.timeout)
477
+ dprint("Traceback:\n", ''.join(traceback.format_stack()[:-1]))
478
+ self.READ_ERRORS += 1
479
+ while self.DEVICE.in_waiting > 0:
480
+ self.DEVICE.reset_input_buffer()
481
+ time.sleep(0.5)
482
+ self.DEVICE.reset_output_buffer()
483
+ return False
484
+
485
+ if count == 1:
486
+ return buffer[0]
487
+ else:
488
+ return bytearray(buffer)
489
+
490
+ def _get_fw_variable(self, key):
491
+ if self.FW["fw_ver"] < 10: return 0
492
+ dprint("Getting firmware variable {:s}".format(key))
493
+
494
+ size = 0
495
+ for (k, v) in self.DEVICE_VAR.items():
496
+ if key in k:
497
+ if v[0] == 8: size = 1
498
+ elif v[0] == 16: size = 2
499
+ elif v[0] == 32: size = 4
500
+ key = v[1]
501
+ break
502
+ if size == 0:
503
+ raise KeyError("Unknown variable name specified.")
504
+
505
+ buffer = bytearray([self.DEVICE_CMD["GET_VARIABLE"], size])
506
+ buffer.extend(struct.pack(">I", key))
507
+ self._write(buffer)
508
+ temp = self._read(4)
509
+ try:
510
+ return struct.unpack(">I", temp)[0]
511
+ except:
512
+ dprint("Communication error:", temp)
513
+ return False
514
+
515
+ def _set_fw_variable(self, key, value):
516
+ # if key == "FLASH_WE_PIN" and not self.SupportsAudioAsWe(): return
517
+ dprint("Setting firmware variable {:s} to 0x{:X}".format(key, value))
518
+ self.FW_VAR[key] = value
519
+
520
+ size = 0
521
+ for (k, v) in self.DEVICE_VAR.items():
522
+ if key in k:
523
+ if v[0] == 8: size = 1
524
+ elif v[0] == 16: size = 2
525
+ elif v[0] == 32: size = 4
526
+ key = v[1]
527
+ break
528
+ if size == 0:
529
+ raise KeyError("Unknown variable name specified.")
530
+
531
+ buffer = bytearray([self.DEVICE_CMD["SET_VARIABLE"], size])
532
+ buffer.extend(struct.pack(">I", key))
533
+ buffer.extend(struct.pack(">I", value))
534
+
535
+ self._try_write(buffer)
536
+
537
+ def _cart_read(self, address, length=0, agb_save_flash=False):
538
+ if self.MODE == "DMG":
539
+ if length == 0:
540
+ length = 1
541
+ if address < 0xA000:
542
+ return struct.unpack("B", self.ReadROM(address, 1))[0]
543
+ else:
544
+ return struct.unpack("B", self.ReadRAM(address - 0xA000, 1))[0]
545
+ else:
546
+ if address < 0xA000:
547
+ return self.ReadROM(address, length)
548
+ else:
549
+ return self.ReadRAM(address - 0xA000, length)
550
+ elif self.MODE == "AGB":
551
+ if length == 0:
552
+ if agb_save_flash:
553
+ length = 1
554
+ return struct.unpack("B", self.ReadRAM(address, length, command=self.DEVICE_CMD["AGB_CART_READ_SRAM"]))[0]
555
+ else:
556
+ length = 2
557
+ return struct.unpack(">H", self.ReadROM(address >> 1, length))[0]
558
+ else:
559
+ if agb_save_flash:
560
+ return self.ReadRAM(address, length, command=self.DEVICE_CMD["AGB_CART_READ_SRAM"])
561
+ else:
562
+ return self.ReadROM(address, length)
563
+
564
+ def _cart_write(self, address, value, flashcart=False, sram=False):
565
+ dprint("Writing to cartridge: 0x{:X} = 0x{:X} (args: {:s}, {:s})".format(address, value & 0xFF, str(flashcart), str(sram)))
566
+ if self.MODE == "DMG":
567
+ if flashcart:
568
+ buffer = bytearray([self.DEVICE_CMD["DMG_FLASH_WRITE_BYTE"]])
569
+ buffer.extend(struct.pack(">I", address))
570
+ buffer.extend(struct.pack("B", value & 0xFF))
571
+ else:
572
+ if sram:
573
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
574
+ self._set_fw_variable("ADDRESS", address)
575
+ self._set_fw_variable("TRANSFER_SIZE", 1)
576
+ self._write(self.DEVICE_CMD["DMG_CART_WRITE_SRAM"])
577
+ self._write(value, wait=True)
578
+ return
579
+ else:
580
+ buffer = bytearray([self.DEVICE_CMD["DMG_CART_WRITE"]])
581
+ buffer.extend(struct.pack(">I", address))
582
+ buffer.extend(struct.pack("B", value & 0xFF))
583
+ elif self.MODE == "AGB":
584
+ if sram:
585
+ self._set_fw_variable("TRANSFER_SIZE", 1)
586
+ self._set_fw_variable("ADDRESS", address)
587
+ self._write(self.DEVICE_CMD["AGB_CART_WRITE_SRAM"])
588
+ self._write(value, wait=True)
589
+ return
590
+ elif flashcart:
591
+ buffer = bytearray([self.DEVICE_CMD["AGB_FLASH_WRITE_SHORT"]])
592
+ else:
593
+ buffer = bytearray([self.DEVICE_CMD["AGB_CART_WRITE"]])
594
+
595
+ buffer.extend(struct.pack(">I", address >> 1))
596
+ buffer.extend(struct.pack(">H", value & 0xFFFF))
597
+
598
+ self._try_write(buffer)
599
+
600
+ if self.MODE == "DMG" and sram: self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
601
+
602
+ def _cart_write_flash(self, commands, flashcart=False):
603
+ if self.FW["fw_ver"] < 6 and not (self.MODE == "AGB" and not flashcart):
604
+ for command in commands:
605
+ if self._cart_write(command[0], command[1], flashcart=flashcart) is False:
606
+ print("ERROR")
607
+ return
608
+
609
+ num = len(commands)
610
+ buffer = bytearray([self.DEVICE_CMD["CART_WRITE_FLASH_CMD"]])
611
+ if self.FW["fw_ver"] >= 6:
612
+ buffer.extend(struct.pack("B", 1 if flashcart else 0))
613
+ buffer.extend(struct.pack("B", num))
614
+ for i in range(0, num):
615
+ dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d})".format(commands[i][0], commands[i][1], i+1, num))
616
+ if self.MODE == "AGB" and flashcart:
617
+ buffer.extend(struct.pack(">I", commands[i][0] >> 1))
618
+ else:
619
+ buffer.extend(struct.pack(">I", commands[i][0]))
620
+
621
+ if self.FW["fw_ver"] < 6:
622
+ buffer.extend(struct.pack("B", commands[i][1]))
623
+ else:
624
+ buffer.extend(struct.pack(">H", commands[i][1]))
625
+
626
+ self._write(buffer)
627
+ ret = self._read(1)
628
+ if ret != 0x01:
629
+ if ret is False:
630
+ msg = "Error: No response while trying to write to the device."
631
+ time.sleep(0.5)
632
+ self.DEVICE.reset_input_buffer()
633
+ else:
634
+ msg = "Error: Bad response “{:s}” while trying to write to the device.".format(str(ret))
635
+ print(f"{ANSI.RED}{msg}{ANSI.RESET}")
636
+ dprint(msg)
637
+ #self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A critical communication error occured during a write. Please avoid passive USB hubs, try different USB ports/cables and re-connect the device."})
638
+ #self.CANCEL = True
639
+ #self.ERROR = True
640
+ return False
641
+ return True
642
+
643
+ def _clk_toggle(self, num=1):
644
+ if self.FW["fw_ver"] >= 12:
645
+ buffer = bytearray()
646
+ buffer.extend(struct.pack("B", self.DEVICE_CMD["CLK_TOGGLE"]))
647
+ buffer.extend(struct.pack(">I", num))
648
+ return self._write(buffer, wait=True)
649
+ else:
650
+ if not self.CanPowerCycleCart(): return False
651
+ for _ in range(0, num):
652
+ self._write(0xA9) # CLK_HIGH
653
+ self._write(0xAA) # CLK_LOW
654
+ return True
655
+
656
+ def _set_we_pin_wr(self):
657
+ if self.MODE == "DMG":
658
+ self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
659
+ def _set_we_pin_audio(self):
660
+ if self.MODE == "DMG":
661
+ self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
662
+
663
+ def CartPowerCycleOrAskReconnect(self, delay=0.1):
664
+ if self.CanPowerCycleCart():
665
+ self.CartPowerCycle(delay=delay)
666
+ return
667
+
668
+ mode = self.GetMode()
669
+ var_state = self.GetVarState()
670
+
671
+ title = "Power cycle required"
672
+ msg = f"To continue, please re-connect the USB cable of your {self.GetName()} device on port {self.PORT}, preferably on the computer side."
673
+ while True:
674
+ self.SetProgress({"action":"USER_ACTION", "user_action":"REINSERT_CART", "msg":msg, "title":title})
675
+ while self.USER_ANSWER is None:
676
+ dprint("Waiting for the user to confirm the re-connecting of the device.")
677
+ time.sleep(1)
678
+
679
+ if self.USER_ANSWER is False:
680
+ self.CANCEL = True
681
+ self.USER_ANSWER = None
682
+ return False
683
+
684
+ self.USER_ANSWER = None
685
+ if self.CheckActive(): continue
686
+
687
+ dev = None
688
+ try:
689
+ dev = serial.Serial(self.PORT, self.BAUDRATE, timeout=0.1)
690
+ self.DEVICE = dev
691
+ self.LoadFirmwareVersion()
692
+ self.SetMode(mode)
693
+ self.SetVarState(var_state)
694
+ time.sleep(0.5)
695
+ break
696
+
697
+ except SerialException as e:
698
+ del(dev)
699
+ dprint("An error occured while re-connecting to the device:\n", e)
700
+ continue
701
+
702
+ def CartPowerCycle(self, delay=0.1):
703
+ if self.CanPowerCycleCart():
704
+ dprint("Power cycling cartridge with a delay of {:.1f} seconds".format(delay))
705
+ self.CartPowerOff(delay=delay)
706
+ self.CartPowerOn(delay=delay)
707
+
708
+ def CartPowerOff(self, delay=0.1):
709
+ dprint("Turning off the cartridge power")
710
+ if self.CanPowerCycleCart():
711
+ if self.FW["fw_ver"] >= 12:
712
+ self._write(self.DEVICE_CMD["CART_PWR_OFF"], wait=self.FW["fw_ver"] >= 12)
713
+ else:
714
+ self._write(self.DEVICE_CMD["OFW_CART_PWR_OFF"])
715
+ time.sleep(delay)
716
+ else:
717
+ self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"], wait=self.FW["fw_ver"] >= 12)
718
+
719
+ def CartPowerOn(self, delay=0.1):
720
+ if self.CanPowerCycleCart():
721
+ if self.FW["fw_ver"] >= 12:
722
+ self._write(self.DEVICE_CMD["QUERY_CART_PWR"])
723
+ if self._read(1) == 0:
724
+ dprint("Turning on the cartridge power")
725
+ if self.MODE == "DMG":
726
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
727
+ elif self.MODE == "AGB":
728
+ self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
729
+ self._write(self.DEVICE_CMD["CART_PWR_ON"])
730
+ time.sleep(0.2)
731
+ hp = 10
732
+ while hp > 0: # Workaround for GBxCart RW, it sometimes glitches after cart power on?
733
+ if self.DEVICE.in_waiting == 0:
734
+ dprint("Waiting for ACK...")
735
+ hp -= 1
736
+ time.sleep(0.05)
737
+ continue
738
+ temp = self.DEVICE.read(self.DEVICE.in_waiting)
739
+ if len(temp) >= 1: temp = temp[len(temp) - 1]
740
+ if temp == 1: break
741
+ self.DEVICE.timeout = 0.1
742
+ dprint("Unexpected ACK value:", temp)
743
+ self._write(self.DEVICE_CMD["QUERY_CART_PWR"])
744
+ time.sleep(0.05)
745
+ hp -= 1
746
+ self.DEVICE.timeout = self.DEVICE_TIMEOUT
747
+
748
+ self._write(self.DEVICE_CMD["QUERY_CART_PWR"])
749
+ if self._read(1) != 1:
750
+ dprint("Warning: No response from firmware on QUERY_CART_PWR.")
751
+
752
+ if self.MODE == "DMG":
753
+ dprint("Resetting Memory Bank Controller")
754
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True) # Sachen (and Xploder GB?) may need this
755
+ elif self.MODE == "AGB":
756
+ dprint("Executing AGB Bootup Sequence")
757
+ self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=self.FW["fw_ver"] >= 12)
758
+
759
+ if self.FW["pcb_name"] == "GBxCart RW":
760
+ self._cart_write(0, 0xFF) # workaround for strange bootlegs
761
+
762
+ else:
763
+ self._write(self.DEVICE_CMD["OFW_QUERY_CART_PWR"])
764
+ if self._read(1) == 0:
765
+ dprint("Turning on the cartridge power.")
766
+ self._write(self.DEVICE_CMD["OFW_CART_PWR_ON"])
767
+ time.sleep(delay)
768
+ self.DEVICE.reset_input_buffer() # bug workaround
769
+
770
+ else:
771
+ if self.MODE == "AGB":
772
+ dprint("Executing AGB Bootup Sequence")
773
+ self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=self.FW["fw_ver"] >= 12)
774
+
775
+ return True
776
+
777
+ def GetVarState(self):
778
+ self._write(self.DEVICE_CMD["GET_VAR_STATE"])
779
+ time.sleep(0.2)
780
+ var_state = bytearray(self.DEVICE.read(self.DEVICE.in_waiting))
781
+ dprint("Got the state of variables ({:d} bytes)".format(len(var_state)))
782
+ return var_state
783
+
784
+ def SetVarState(self, var_state):
785
+ dprint("Sending the state of variables ({:d} bytes)".format(len(var_state)))
786
+ self._write(self.DEVICE_CMD["SET_VAR_STATE"])
787
+ time.sleep(0.2)
788
+ self.DEVICE.write(var_state)
789
+
790
+ def GetMode(self):
791
+ if time.time() < self.LAST_CHECK_ACTIVE + 1: return self.MODE
792
+ if self.CheckActive() is False: return None
793
+ if self.MODE is None: return None
794
+ if self.FW["fw_ver"] < 12: return self.MODE
795
+ mode = self._get_fw_variable("CART_MODE")
796
+ if mode is False:
797
+ print("{:s}Error: Couldn’t get mode variable from firmware. Please re-connect the device.{:s}".format(ANSI.RED, ANSI.RESET))
798
+ self.DEVICE.close()
799
+ self.DEVICE = None
800
+ self.ERROR = True
801
+ self.CANCEL = True
802
+ return self.MODE
803
+ if mode == 0:
804
+ return None
805
+ modes = self.GetSupprtedModes()
806
+ if mode > len(modes):
807
+ print("{:s}Error: Invalid mode: {:s}{:s}".format(ANSI.RED, str(mode - 1), ANSI.RESET))
808
+ return self.MODE
809
+ self.MODE = modes[mode - 1]
810
+ return self.MODE
811
+
812
+ def SetMode(self, mode, delay=0.1):
813
+ if mode == "DMG":
814
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
815
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
816
+ self._set_fw_variable("DMG_READ_METHOD", self.DMG_READ_METHOD)
817
+ self._set_fw_variable("CART_MODE", 1)
818
+ self.MODE = "DMG"
819
+ elif mode == "AGB":
820
+ self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
821
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
822
+ self._set_fw_variable("AGB_READ_METHOD", self.AGB_READ_METHOD)
823
+ self._set_fw_variable("CART_MODE", 2)
824
+ if self.FW["fw_ver"] >= 12: self._set_fw_variable("AGB_IRQ_ENABLED", 0)
825
+ self.MODE = "AGB"
826
+ self._set_fw_variable(key="ADDRESS", value=0)
827
+
828
+ if self.CanPowerCycleCart():
829
+ self.CartPowerOn()
830
+ else:
831
+ if mode == "DMG":
832
+ self.SetPin(["PIN_AUDIO"], True)
833
+ elif mode == "AGB":
834
+ self.SetPin(["PIN_AUDIO"], False)
835
+
836
+ def SetAutoPowerOff(self, value):
837
+ if not self.CanPowerCycleCart(): return
838
+ value &= 0xFFFFFFFF
839
+ dprint(f"Setting automatic power off time value to {value}")
840
+ self._set_fw_variable("AUTO_POWEROFF_TIME", value)
841
+ self._set_fw_variable("AUTO_POWEROFF_ENABLED", 1 if value != 0 else 0)
842
+
843
+ def GetSupportedCartridgesDMG(self):
844
+ return (list(self.SUPPORTED_CARTS['DMG'].keys()), list(self.SUPPORTED_CARTS['DMG'].values()))
845
+
846
+ def GetSupportedCartridgesAGB(self):
847
+ return (list(self.SUPPORTED_CARTS['AGB'].keys()), list(self.SUPPORTED_CARTS['AGB'].values()))
848
+
849
+ def SetProgress(self, args):
850
+ if self.CANCEL and args["action"] not in ("ABORT", "FINISHED", "ERROR"): return
851
+ if "pos" in args: self.POS = args["pos"]
852
+ if args["action"] == "UPDATE_POS": self.INFO["transferred"] = args["pos"]
853
+ try:
854
+ self.SIGNAL.emit(args)
855
+ except AttributeError:
856
+ if self.SIGNAL is not None:
857
+ self.SIGNAL(args)
858
+
859
+ if args["action"] == "INITIALIZE":
860
+ if self.CanPowerCycleCart(): self.CartPowerOn() # Ensure cart is powered
861
+ self.POS = 0
862
+ elif args["action"] == "FINISHED":
863
+ self.POS = 0
864
+ self.SIGNAL = None
865
+
866
+ def Debug(self):
867
+ # self.SetPin(["PIN_AUDIO"], True)
868
+ return
869
+
870
+ def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
871
+ self.Debug()
872
+
873
+ if not self.IsConnected(): raise ConnectionError("Couldn’t access the the device.")
874
+ data = {}
875
+ self.SIGNAL = None
876
+
877
+ if self.CanPowerCycleCart():
878
+ self.ResetLEDs()
879
+ self.CartPowerOn()
880
+
881
+ if self.FW["fw_ver"] >= 8: self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
882
+ if self.MODE == "DMG":
883
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
884
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
885
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
886
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
887
+ elif self.MODE == "AGB":
888
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
889
+ if not self.CanPowerCycleCart():
890
+ dprint("Executing AGB Bootup Sequence")
891
+ self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=self.FW["fw_ver"] >= 12)
892
+ else:
893
+ print("{:s}Error: No mode was set.{:s}".format(ANSI.RED, ANSI.RESET))
894
+ return False
895
+
896
+ header = self.ReadROM(0, 0x180)
897
+
898
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
899
+ with open("debug_header.bin", "wb") as f: f.write(header)
900
+
901
+ # Parse ROM header
902
+ if self.MODE == "DMG":
903
+ data = RomFileDMG(header).GetHeader()
904
+ if "game_title" in data and data["game_title"] == "TETRIS" and hashlib.sha1(header).digest() != bytearray([0x1D, 0x69, 0x2A, 0x4B, 0x31, 0x7A, 0xA5, 0xE9, 0x67, 0xEE, 0xC2, 0x2F, 0xCC, 0x32, 0x43, 0x8C, 0xCB, 0xC5, 0x78, 0x0B]): # Sachen
905
+ header = self.ReadROM(0, 0x280)
906
+ data = RomFileDMG(header).GetHeader()
907
+ if "logo_correct" in data and data["logo_correct"] is False and not b"Future Console Design" in header:
908
+ self._cart_write(0, 0xFF) # workaround for strange bootlegs
909
+ time.sleep(0.1)
910
+ header = self.ReadROM(0, 0x280)
911
+ data = RomFileDMG(header).GetHeader()
912
+ if "mapper_raw" in data and data["mapper_raw"] == 0x203 or b"Future Console Design" in header: # Xploder GB version number
913
+ self._cart_write(0x0006, 0)
914
+ header[0:0x10] = self.ReadROM(0x4000, 0x10)
915
+ header[0xD0:0xE0] = self.ReadROM(0x40D0, 0x10)
916
+ data = RomFileDMG(header).GetHeader()
917
+ if data == {}: return False
918
+
919
+ data["has_rtc"] = False
920
+ data["rtc_dict"] = {}
921
+ data["rtc_string"] = "Not available"
922
+ if data["logo_correct"] is True:
923
+ _mbc = DMG_MBC().GetInstance(args={"mbc":data["mapper_raw"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
924
+ if checkRtc:
925
+ data["has_rtc"] = _mbc.HasRTC() is True
926
+ if data["has_rtc"] is True:
927
+ if _mbc.GetName() == "TAMA5": _mbc.EnableMapper()
928
+ _mbc.LatchRTC()
929
+ data["rtc_buffer"] = _mbc.ReadRTC()
930
+ if _mbc.GetName() == "TAMA5": self._set_fw_variable("DMG_READ_CS_PULSE", 0)
931
+ try:
932
+ data["rtc_dict"] = _mbc.GetRTCDict()
933
+ data["rtc_string"] = _mbc.GetRTCString()
934
+ except:
935
+ data["rtc_string"] = "Invalid data"
936
+ if _mbc.GetName() == "G-MMC1":
937
+ try:
938
+ temp = bytearray([0] * 0x100000)
939
+ temp[0:0x180] = header
940
+ _mbc.SelectBankROM(7)
941
+ if data["game_title"] == "NP M-MENU MENU":
942
+ gbmem_menudata = self.ReadROM(0x4000, 0x1000)
943
+ temp[0x1C000:0x1C000+0x1000] = gbmem_menudata
944
+ elif data["game_title"] == "DMG MULTI MENU ":
945
+ gbmem_menudata = self.ReadROM(0x4000, 0x4000)
946
+ temp[0x1C000:0x1C000+0x4000] = gbmem_menudata
947
+ _mbc.SelectBankROM(0)
948
+ data["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=_mbc.ReadHiddenSector(), buffer_rom=temp)
949
+ except:
950
+ print(traceback.format_exc())
951
+ 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))
952
+
953
+ elif self.MODE == "AGB":
954
+ # Unlock DACS carts on older firmware
955
+ if not self.CanPowerCycleCart() or self.FW["fw_ver"] == 1:
956
+ if header[0x04:0x04+0x9C] in (bytearray([0x00] * 0x9C), bytearray([0xFF] * 0x9C)):
957
+ self.ReadROM(0x1FFFFE0, 20)
958
+ header = self.ReadROM(0, 0x180)
959
+
960
+ data = RomFileAGB(header).GetHeader()
961
+ if data["logo_correct"] is False: # workaround for strange bootlegs
962
+ self._cart_write(0, 0xFF)
963
+ header = self.ReadROM(0, 0x180)
964
+ data = RomFileAGB(header).GetHeader()
965
+
966
+ if data["vast_fame"]: # Unlock full address space for Vast Fame protected carts
967
+ self._cart_write(0xFFF8, 0x99, sram=True)
968
+ self._cart_write(0xFFF9, 0x02, sram=True)
969
+ self._cart_write(0xFFFA, 0x05, sram=True)
970
+ self._cart_write(0xFFFB, 0x02, sram=True)
971
+ self._cart_write(0xFFFC, 0x03, sram=True)
972
+
973
+ self._cart_write(0xFFFD, 0x00, sram=True)
974
+
975
+ self._cart_write(0xFFF8, 0x99, sram=True)
976
+ self._cart_write(0xFFF9, 0x03, sram=True)
977
+ self._cart_write(0xFFFA, 0x62, sram=True)
978
+ self._cart_write(0xFFFB, 0x02, sram=True)
979
+ self._cart_write(0xFFFC, 0x56, sram=True)
980
+
981
+ if data["empty"] or data["empty_nocart"]:
982
+ data["rom_size"] = 0x2000000
983
+ else:
984
+ # Check where the ROM data repeats (for unlicensed carts)
985
+ size_check = header[0xA0:0xA0+16]
986
+ currAddr = 0x10000
987
+ while currAddr < 0x2000000:
988
+ buffer = self.ReadROM(currAddr + 0xA0, 64)[:16]
989
+ if buffer == size_check: break
990
+ currAddr *= 2
991
+
992
+ if data["vast_fame"]:
993
+ if currAddr < 0x2000000:
994
+ currAddr >>= 1 # Vast Fame carts are blank for the 1st mirror, so divide by 2
995
+ else: # Some Vast Fame carts have no mirror, check using VF pattern behaviour instead
996
+ currAddr = 0x200000
997
+ while currAddr < 0x2000000:
998
+ sentinel = self.ReadROM(currAddr + 0x2AAAA, 2)
999
+ if int.from_bytes(sentinel) == 0xAAAA: break
1000
+ currAddr *= 2
1001
+
1002
+ data["rom_size"] = currAddr
1003
+
1004
+ if (self.ReadROM(0x1FFE000, 0x0C) == b"AGBFLASHDACS"):
1005
+ data["dacs_8m"] = True
1006
+ if self.FW["pcb_name"] == "GBFlash" and self.FW["pcb_ver"] < 13:
1007
+ print("{:s}Note: This cartridge may not be fully compatible with your GBFlash hardware revision. Upgrade to v1.3 or newer for better compatibility.{:s}".format(ANSI.YELLOW, ANSI.RESET))
1008
+
1009
+ data["rtc_dict"] = {}
1010
+ data["rtc_string"] = "Not available"
1011
+ if checkRtc and data["logo_correct"] is True and header[0xC5] == 0 and header[0xC7] == 0 and header[0xC9] == 0:
1012
+ _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)
1013
+ if self.FW["fw_ver"] >= 12:
1014
+ self._write(self.DEVICE_CMD["AGB_READ_GPIO_RTC"])
1015
+ temp = self._read(8)
1016
+ data["has_rtc"] = _agb_gpio.HasRTC(temp) is True
1017
+ if data["has_rtc"] is True:
1018
+ data["rtc_buffer"] = temp[1:8]
1019
+ else:
1020
+ data["has_rtc"] = _agb_gpio.HasRTC() is True
1021
+ if data["has_rtc"] is True:
1022
+ data["rtc_buffer"] = _agb_gpio.ReadRTC()
1023
+ try:
1024
+ data["rtc_dict"] = _agb_gpio.GetRTCDict(has_rtc=data["has_rtc"])
1025
+ data["rtc_string"] = _agb_gpio.GetRTCString(has_rtc=data["has_rtc"])
1026
+ except Exception as e:
1027
+ dprint("RTC exception:", str(e.args[0]))
1028
+ data["has_rtc"] = False
1029
+ else:
1030
+ data["has_rtc"] = False
1031
+ data["no_rtc_reason"] = None
1032
+
1033
+ if data["ereader"] is True:
1034
+ bank = 0
1035
+ dprint("Switching to FLASH bank {:d}".format(bank))
1036
+ cmds = [
1037
+ [ 0x5555, 0xAA ],
1038
+ [ 0x2AAA, 0x55 ],
1039
+ [ 0x5555, 0xB0 ],
1040
+ [ 0, bank ]
1041
+ ]
1042
+ self._cart_write_flash(cmds)
1043
+ temp = self.ReadRAM(address=0xD000, length=0x2000, command=self.DEVICE_CMD["AGB_CART_READ_SRAM"])
1044
+ if temp[0:0x14] == b'Card-E Reader 2001\0\0':
1045
+ data["ereader_calibration"] = temp
1046
+ else:
1047
+ data["ereader_calibration"] = None
1048
+ del(data["ereader_calibration"])
1049
+
1050
+ dprint("Header data:", data)
1051
+ data["raw"] = header
1052
+ self.INFO = {**self.INFO, **data}
1053
+ if "batteryless_sram" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["batteryless_sram"])
1054
+ self.INFO["dump_info"]["header"] = data
1055
+ self.INFO["flash_type"] = 0
1056
+ self.INFO["last_action"] = 0
1057
+
1058
+ if self.MODE == "DMG": #and setPinsAsInputs:
1059
+ self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"], wait=self.FW["fw_ver"] >= 12)
1060
+
1061
+ return data
1062
+
1063
+ def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True):
1064
+ self.SIGNAL = None
1065
+ self.CANCEL = False
1066
+ self.ERROR = False
1067
+ cart_type_id = 0
1068
+ save_type = None
1069
+ save_chip = None
1070
+ sram_unstable = None
1071
+ save_size = None
1072
+ checkBatterylessSRAM = False
1073
+
1074
+ # Header
1075
+ info = self.ReadInfo(checkRtc=True)
1076
+ if self.MODE == "DMG" and mbc is None:
1077
+ mbc = info["mapper_raw"]
1078
+ if mbc > 0x200: checkSaveType = False
1079
+
1080
+ # Disable Auto Power Off
1081
+ _apoe = False
1082
+ if self.CanPowerCycleCart():
1083
+ self.CartPowerCycle()
1084
+ if self.FW["fw_ver"] >= 12:
1085
+ _apoe = self._get_fw_variable("AUTO_POWEROFF_ENABLED") == 1
1086
+ if _apoe is True:
1087
+ _apot = self._get_fw_variable("AUTO_POWEROFF_TIME")
1088
+ self._set_fw_variable("AUTO_POWEROFF_TIME", 5000)
1089
+
1090
+ # Detect Flash Cart
1091
+ ret = self.DetectFlash(limitVoltage=limitVoltage)
1092
+ if ret is False: return False
1093
+ (cart_types, cart_type_id, flash_id, cfi_s, cfi, detected_size) = ret
1094
+ supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
1095
+ cart_type = supported_carts[cart_type_id]
1096
+
1097
+ # Preparations
1098
+ if "command_set" in cart_type and cart_type["command_set"] in ("DMG-MBC5-32M-FLASH", "GBAMP"):
1099
+ checkSaveType = False
1100
+ elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
1101
+ save_size = 65536
1102
+ save_type = 7
1103
+ checkSaveType = False
1104
+ elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 1:
1105
+ checkSaveType = False
1106
+ elif self.MODE == "DMG" and "mbc" in cart_type and cart_type["mbc"] == 0x105: # G-MMC1
1107
+ header = self.ReadROM(0, 0x180)
1108
+ data = RomFileDMG(header).GetHeader()
1109
+ _mbc = DMG_MBC().GetInstance(args={"mbc":cart_type["mbc"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
1110
+ temp = bytearray([0] * 0x100000)
1111
+ temp[0:0x180] = header
1112
+ _mbc.SelectBankROM(7)
1113
+ if data["game_title"] == "NP M-MENU MENU":
1114
+ gbmem_menudata = self.ReadROM(0x4000, 0x1000)
1115
+ temp[0x1C000:0x1C000+0x1000] = gbmem_menudata
1116
+ elif data["game_title"] == "DMG MULTI MENU ":
1117
+ gbmem_menudata = self.ReadROM(0x4000, 0x4000)
1118
+ temp[0x1C000:0x1C000+0x4000] = gbmem_menudata
1119
+ _mbc.SelectBankROM(0)
1120
+ info["gbmem"] = _mbc.ReadHiddenSector()
1121
+ info["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=info["gbmem"], buffer_rom=temp)
1122
+
1123
+ # Save Type and Size
1124
+ if checkSaveType:
1125
+ if self.MODE == "DMG":
1126
+ save_size = 131072
1127
+ save_type = 0x04
1128
+ if mbc == 0x20: # MBC6
1129
+ save_size = 1081344
1130
+ save_type = 0x104
1131
+ return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
1132
+ elif mbc == 0x22: # MBC7
1133
+ save_type = 0x102
1134
+ save_size = 512
1135
+ elif mbc == 0xFD: # TAMA5
1136
+ save_size = 32
1137
+ save_type = 0x103
1138
+ return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
1139
+ args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':save_type, 'rtc':False, 'detect':True }
1140
+ elif self.MODE == "AGB":
1141
+ args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':8, 'rtc':False, 'detect':True }
1142
+
1143
+ ret = self._BackupRestoreRAM(args=args)
1144
+
1145
+ if ret is not False and "data" in self.INFO:
1146
+ save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
1147
+ else:
1148
+ save_size = 0
1149
+
1150
+ if self.MODE == "DMG":
1151
+ try:
1152
+ save_type = Util.DMG_Header_RAM_Sizes_Map[Util.DMG_Header_RAM_Sizes_Flasher_Map.index(save_size)]
1153
+ except:
1154
+ save_size = 0
1155
+ save_type = 0
1156
+
1157
+ if save_size > 0x20:
1158
+ if mbc == 0x22: # MBC7
1159
+ if save_size == 256:
1160
+ save_type = 0x101
1161
+ elif save_size == 512:
1162
+ save_type = 0x102
1163
+ elif save_size > 0x10000: # MBC30+RTC?
1164
+ check = True
1165
+ for i in range(0x8000, 0x10000, 0x40):
1166
+ if self.INFO["data"][i:i+3] != bytearray([self.INFO["data"][i]] * 3):
1167
+ check = False
1168
+ break
1169
+ if check:
1170
+ save_size = 32768
1171
+ save_type = 0x03
1172
+ else:
1173
+ check = True
1174
+ for i in range(0x1A000, 0x20000, 0x40):
1175
+ if self.INFO["data"][i:i+3] != bytearray([self.INFO["data"][i]] * 3):
1176
+ check = False
1177
+ break
1178
+ if check:
1179
+ save_size = 65536
1180
+ save_type = 0x05
1181
+
1182
+ elif self.MODE == "AGB":
1183
+ if info["3d_memory"] is True:
1184
+ save_type = None
1185
+ save_size = 0
1186
+ else:
1187
+ # Check for FLASH
1188
+ ret = self.ReadFlashSaveID()
1189
+ if ret is not False:
1190
+ (flash_save_id, _) = ret
1191
+ try:
1192
+ if flash_save_id != 0 and flash_save_id in Util.AGB_Flash_Save_Chips:
1193
+ save_size = Util.AGB_Flash_Save_Chips_Sizes[list(Util.AGB_Flash_Save_Chips).index(flash_save_id)]
1194
+ save_chip = Util.AGB_Flash_Save_Chips[flash_save_id]
1195
+ if save_size == 131072:
1196
+ save_type = 5
1197
+ elif save_size == 65536:
1198
+ save_type = 4
1199
+ except:
1200
+ pass
1201
+
1202
+ if save_type is None:
1203
+ checkBatterylessSRAM = True
1204
+ if info["dacs_8m"] is True:
1205
+ save_size = 1048576
1206
+ save_type = 6
1207
+ elif save_size > 256: # SRAM
1208
+ if save_size == 131072:
1209
+ save_type = 8
1210
+ checkBatterylessSRAM = True
1211
+ elif save_size == 65536:
1212
+ save_type = 7
1213
+ checkBatterylessSRAM = True
1214
+ elif save_size == 32768:
1215
+ save_type = 3
1216
+ elif save_size in Util.AGB_Header_Save_Sizes:
1217
+ save_type = Util.AGB_Header_Save_Sizes.index(save_size)
1218
+ else:
1219
+ save_type = None
1220
+ save_size = 0
1221
+ else:
1222
+ dprint("Testing EEPROM")
1223
+ # Check for 4K EEPROM
1224
+ self._BackupRestoreRAM(args={ 'mode':2, 'path':None, 'mbc':mbc, 'save_type':1, 'rtc':False, 'detect':True })
1225
+ save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
1226
+ eeprom_4k = self.INFO["data"]
1227
+ # Check for 64K EEPROM
1228
+ self._BackupRestoreRAM(args={ 'mode':2, 'path':None, 'mbc':mbc, 'save_type':2, 'rtc':False, 'detect':True })
1229
+ save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
1230
+ eeprom_64k = self.INFO["data"]
1231
+ if eeprom_64k in (bytearray([0xFF] * len(eeprom_64k)), bytearray([0] * len(eeprom_64k))):
1232
+ save_type = None
1233
+ save_size = 0
1234
+ elif (eeprom_4k == eeprom_64k[:len(eeprom_4k)]):
1235
+ save_type = 2
1236
+ save_size = 8192
1237
+ checkBatterylessSRAM = False
1238
+ else:
1239
+ save_type = 1
1240
+ save_size = 512
1241
+ checkBatterylessSRAM = False
1242
+
1243
+ if checkBatterylessSRAM:
1244
+ batteryless = self.CheckBatterylessSRAM()
1245
+ if batteryless is not False:
1246
+ save_type = 9
1247
+ info["batteryless_sram"] = batteryless
1248
+ self.INFO["dump_info"]["batteryless_sram"] = batteryless
1249
+
1250
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
1251
+ self.INFO["last_action"] = 0
1252
+ self.INFO["action"] = None
1253
+
1254
+ if self.CanPowerCycleCart() and _apoe is True:
1255
+ self._set_fw_variable("AUTO_POWEROFF_TIME", _apot)
1256
+
1257
+ return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
1258
+
1259
+ def CheckBatterylessSRAM(self):
1260
+ bl_size = None
1261
+ bl_offset = None
1262
+ if self.MODE == "AGB":
1263
+ buffer = self.ReadROM(0, 0x180)
1264
+ header = RomFileAGB(buffer).GetHeader()
1265
+ if header["game_code"] in ("GMBC", "PNES"):
1266
+ bl_size = 0x10000
1267
+ state_id1 = 0x57a731d7
1268
+ state_id2 = 0x57a731d8
1269
+ state_id3 = 0x57a731d9
1270
+ if struct.unpack("<I", self.ReadROM(0x400000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
1271
+ bl_offset = 0x400000-0x40000
1272
+ elif struct.unpack("<I", self.ReadROM(0x800000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
1273
+ bl_offset = 0x800000-0x40000
1274
+ elif struct.unpack("<I", self.ReadROM(0x1000000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
1275
+ bl_offset = 0x1000000-0x40000
1276
+ elif struct.unpack("<I", self.ReadROM(0x2000000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
1277
+ bl_offset = 0x2000000-0x40000
1278
+ dprint("Detected Goomba Color or PocketNES Batteryless ROM by Lesserkuma")
1279
+ else:
1280
+ boot_vector = (struct.unpack("<I", buffer[0:3] + bytearray([0]))[0] + 2) << 2
1281
+ batteryless_loader = self.ReadROM(boot_vector, 0x2000)
1282
+ try:
1283
+ if bytearray(b'<3 from Maniac') in batteryless_loader:
1284
+ payload_size = struct.unpack("<H", batteryless_loader[batteryless_loader.index(bytearray(b'<3 from Maniac')):][0x0E:0x10])[0]
1285
+ if payload_size == 0:
1286
+ payload_size = 0x414
1287
+ bl_offset = batteryless_loader.index(bytearray(b'<3 from Maniac')) + boot_vector + 0x10
1288
+ payload = self.ReadROM(bl_offset - payload_size, payload_size)
1289
+ bl_size = struct.unpack("<I", payload[0x8:0xC])[0]
1290
+ dprint("Detected Batteryless SRAM ROM made with the Automatic batteryless saving patcher for GBA by metroid-maniac")
1291
+ if bl_size not in (0x2000, 0x8000, 0x10000, 0x20000):
1292
+ print("{:s}Warning: Unsupported Batteryless SRAM size value detected: 0x{:X}{:s}".format(ANSI.YELLOW, bl_size, ANSI.RESET))
1293
+ elif (bytearray([0x02, 0x13, 0xA0, 0xE3]) in batteryless_loader):
1294
+ if (
1295
+ bytearray([0x09, 0x04, 0xA0, 0xE3]) in batteryless_loader or
1296
+ bytearray([0x09, 0x14, 0xA0, 0xE3]) in batteryless_loader or
1297
+ bytearray([0x09, 0x24, 0xA0, 0xE3]) in batteryless_loader or
1298
+ bytearray([0x09, 0x34, 0xA0, 0xE3]) in batteryless_loader
1299
+ ):
1300
+ bl_size = 0x20000
1301
+ else:
1302
+ bl_size = 0x10000
1303
+ base_addr = batteryless_loader.index(bytearray([0x02, 0x13, 0xA0, 0xE3]))
1304
+ addr_value = batteryless_loader[base_addr - 8]
1305
+ addr_rotate_right = batteryless_loader[base_addr - 7] * 2
1306
+ addr_shift = batteryless_loader[base_addr - 3] << 1
1307
+ address = (addr_value >> addr_rotate_right) | (addr_value << (32 - addr_rotate_right)) & 0xFFFFFFFF
1308
+ address = (address << addr_shift)
1309
+ if address < 32*1024*1024 and address > 0x1000:
1310
+ bl_offset = address
1311
+ dprint("Detected Chinese bootleg Batteryless SRAM ROM")
1312
+ else:
1313
+ dprint("Bad offset with Chinese bootleg Batteryless SRAM ROM:", hex(address))
1314
+ except Exception as e:
1315
+ dprint("An error occured while trying to determine the Batteryless SRAM method.\n", e)
1316
+ bl_offset = None
1317
+ bl_size = None
1318
+ if bl_offset is None or bl_size is None:
1319
+ dprint("No Batteryless SRAM routine detected")
1320
+ return False
1321
+ dprint("bl_offset=0x{:X}, bl_size=0x{:X}".format(bl_offset, bl_size))
1322
+ return {"bl_offset":bl_offset, "bl_size":bl_size}
1323
+
1324
+ def ReadFlashSaveID(self):
1325
+ # Check if actually SRAM/FRAM
1326
+ test1 = self._cart_read(0, 0x10, agb_save_flash=True)[4]
1327
+ self._cart_write_flash([[ 0x0004, test1 ^ 0xFF ]])
1328
+ test2 = self._cart_read(0, 0x10, agb_save_flash=True)[4]
1329
+ self._cart_write_flash([[ 0x0004, test1 ]])
1330
+ if test1 != test2:
1331
+ dprint(f"Seems to be SRAM/FRAM, not FLASH (value 0x{test1:02X} was changed to 0x{test2:02X} by SRAM access at address 0x4)", test1, test2)
1332
+ return False
1333
+
1334
+ # Read Chip ID
1335
+ temp5555 = self._cart_read(0x5555, agb_save_flash=True) >> 8
1336
+ temp2AAA = self._cart_read(0x2AAA, agb_save_flash=True) >> 8
1337
+ temp0000 = self._cart_read(0x0000, agb_save_flash=True) >> 8
1338
+
1339
+ cmds = [
1340
+ [ 0x5555, 0xAA ],
1341
+ [ 0x2AAA, 0x55 ],
1342
+ [ 0x5555, 0x90 ]
1343
+ ]
1344
+ self._cart_write_flash(cmds)
1345
+ agb_flash_chip = struct.unpack(">H", self._cart_read(0, 2, agb_save_flash=True))[0]
1346
+ cmds = [
1347
+ [ 0x5555, 0xAA ],
1348
+ [ 0x2AAA, 0x55 ],
1349
+ [ 0x5555, 0xF0 ]
1350
+ ]
1351
+ self._cart_write_flash(cmds)
1352
+ time.sleep(0.01)
1353
+ self._cart_write_flash([ [ 0, 0xF0 ] ])
1354
+ time.sleep(0.01)
1355
+
1356
+ if agb_flash_chip not in Util.AGB_Flash_Save_Chips:
1357
+ # Restore SRAM values
1358
+ cmds = [
1359
+ [ 0x5555, temp5555 ],
1360
+ [ 0x2AAA, temp2AAA ],
1361
+ [ 0x0000, temp0000 ]
1362
+ ]
1363
+ self._cart_write_flash(cmds)
1364
+ agb_flash_chip_name = "Unknown flash chip ID (0x{:04X})".format(agb_flash_chip)
1365
+ else:
1366
+ agb_flash_chip_name = Util.AGB_Flash_Save_Chips[agb_flash_chip]
1367
+
1368
+ dprint(agb_flash_chip_name)
1369
+ return (agb_flash_chip, agb_flash_chip_name)
1370
+
1371
+ def ReadROM(self, address, length, skip_init=False, max_length=64):
1372
+ num = math.ceil(length / max_length)
1373
+ dprint("Reading 0x{:X} bytes from cartridge ROM at 0x{:X} in {:d} iteration(s)".format(length, address, num))
1374
+ if length > max_length: length = max_length
1375
+
1376
+ buffer = bytearray()
1377
+ if not skip_init:
1378
+ self._set_fw_variable("TRANSFER_SIZE", length)
1379
+ if self.MODE == "DMG":
1380
+ self._set_fw_variable("ADDRESS", address)
1381
+ self._set_fw_variable("DMG_ACCESS_MODE", 1) # MODE_ROM_READ
1382
+ elif self.MODE == "AGB":
1383
+ self._set_fw_variable("ADDRESS", address >> 1)
1384
+
1385
+ if self.MODE == "DMG":
1386
+ command = "DMG_CART_READ"
1387
+ elif self.MODE == "AGB":
1388
+ command = "AGB_CART_READ"
1389
+
1390
+ for n in range(0, num):
1391
+ self._write(self.DEVICE_CMD[command])
1392
+ temp = self._read(length)
1393
+ if temp is not False and isinstance(temp, int): temp = bytearray([temp])
1394
+ if temp is False or len(temp) != length:
1395
+ dprint("Error while trying to read 0x{:X} bytes from cartridge ROM at 0x{:X} in iteration {:d} of {:d} (response: {:s})".format(length, address, n, num, str(temp)))
1396
+ return bytearray()
1397
+ buffer += temp
1398
+ if self.INFO["action"] in (self.ACTIONS["ROM_READ"], self.ACTIONS["SAVE_READ"], self.ACTIONS["ROM_WRITE_VERIFY"]) and not self.NO_PROG_UPDATE:
1399
+ self.SetProgress({"action":"READ", "bytes_added":len(temp)})
1400
+
1401
+ return buffer
1402
+
1403
+ def ReadROM_3DMemory(self, address, length, max_length=64):
1404
+ buffer_size = 0x1000
1405
+ num = math.ceil(length / max_length)
1406
+ dprint("Reading 0x{:X} bytes from cartridge ROM in {:d} iteration(s)".format(length, num))
1407
+ if length > max_length: length = max_length
1408
+
1409
+ self._set_fw_variable("TRANSFER_SIZE", length)
1410
+ self._set_fw_variable("BUFFER_SIZE", buffer_size)
1411
+ self._set_fw_variable("ADDRESS", address >> 1)
1412
+
1413
+ buffer = bytearray()
1414
+ error = False
1415
+ for _ in range(0, int(num / (buffer_size / length))): #32
1416
+ for _ in range(0, int(buffer_size / length)): # 0x1000/0x200=8
1417
+ self._write(self.DEVICE_CMD["AGB_CART_READ_3D_MEMORY"])
1418
+ temp = self._read(length)
1419
+ if isinstance(temp, int): temp = bytearray([temp])
1420
+ if temp is False or len(temp) != length:
1421
+ error = True
1422
+ buffer += temp
1423
+ if self.INFO["action"] == self.ACTIONS["ROM_READ"] and not self.NO_PROG_UPDATE:
1424
+ self.SetProgress({"action":"READ", "bytes_added":length})
1425
+ self._write(0)
1426
+
1427
+ if error: return bytearray()
1428
+ return buffer
1429
+
1430
+ def ReadROM_GBAMP(self, address, length, max_length=64):
1431
+ dprint("GBAMP ROM Read Mode", hex(address), hex(length), hex(max_length))
1432
+ addr = (address >> 13 << 16) | (address & 0x1FFF)
1433
+ dprint(f"0x{address:07X} → 0x{addr:07X}")
1434
+ return self.ReadROM(address=addr, length=length, max_length=max_length)
1435
+
1436
+ def ReadRAM(self, address, length, command=None, max_length=64):
1437
+ num = math.ceil(length / max_length)
1438
+ dprint("Reading 0x{:X} bytes from cartridge RAM in {:d} iteration(s)".format(length, num))
1439
+ if length > max_length: length = max_length
1440
+ buffer = bytearray()
1441
+ self._set_fw_variable("TRANSFER_SIZE", length)
1442
+
1443
+ if self.MODE == "DMG":
1444
+ self._set_fw_variable("ADDRESS", 0xA000 + address)
1445
+ self._set_fw_variable("DMG_ACCESS_MODE", 3) # MODE_RAM_READ
1446
+ self._set_fw_variable("DMG_READ_CS_PULSE", 1)
1447
+ if command is None: command = self.DEVICE_CMD["DMG_CART_READ"]
1448
+ elif self.MODE == "AGB":
1449
+ self._set_fw_variable("ADDRESS", address)
1450
+ if command is None: command = self.DEVICE_CMD["AGB_CART_READ_SRAM"]
1451
+
1452
+ for _ in range(0, num):
1453
+ self._write(command)
1454
+ temp = self._read(length)
1455
+ if isinstance(temp, int): temp = bytearray([temp])
1456
+ if temp is False or len(temp) != length: return bytearray()
1457
+ buffer += temp
1458
+ if not self.NO_PROG_UPDATE:
1459
+ self.SetProgress({"action":"READ", "bytes_added":len(temp)})
1460
+
1461
+ if self.MODE == "DMG":
1462
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
1463
+
1464
+ return buffer
1465
+
1466
+ def ReadRAM_MBC7(self, address, length):
1467
+ max_length = 32
1468
+ num = math.ceil(length / max_length)
1469
+ dprint("Reading 0x{:X} bytes from cartridge EEPROM in {:d} iteration(s)".format(length, num))
1470
+ if length > max_length: length = max_length
1471
+ buffer = bytearray()
1472
+ self._set_fw_variable("TRANSFER_SIZE", length)
1473
+ self._set_fw_variable("ADDRESS", address)
1474
+ for _ in range(0, num):
1475
+ self._write(self.DEVICE_CMD["DMG_MBC7_READ_EEPROM"])
1476
+ temp = self._read(length)
1477
+ if isinstance(temp, int): temp = bytearray([temp])
1478
+ buffer += temp
1479
+ if not self.NO_PROG_UPDATE:
1480
+ self.SetProgress({"action":"READ", "bytes_added":len(temp)})
1481
+
1482
+ return buffer
1483
+
1484
+ def ReadRAM_TAMA5(self):
1485
+ dprint("Reading 0x20 bytes from cartridge RAM")
1486
+ buffer = bytearray()
1487
+ npu = self.NO_PROG_UPDATE
1488
+ self.NO_PROG_UPDATE = True
1489
+ self._set_fw_variable("DMG_READ_CS_PULSE", 1)
1490
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
1491
+
1492
+ # Read save state
1493
+ for i in range(0, 0x20):
1494
+ self._cart_write(0xA001, 0x06, sram=True) # register select and address (high)
1495
+ self._cart_write(0xA000, i >> 4 | 0x01 << 1, sram=True) # bit 0 = higher ram address, rest = command
1496
+ self._cart_write(0xA001, 0x07, sram=True) # address (low)
1497
+ self._cart_write(0xA000, i & 0x0F, sram=True) # bits 0-3 = lower ram address
1498
+ self._cart_write(0xA001, 0x0D, sram=True) # data out (high)
1499
+ value1, value2 = None, None
1500
+ while value1 is None or value1 != value2:
1501
+ value2 = value1
1502
+ value1 = self._cart_read(0xA000)
1503
+ data_h = value1
1504
+ self._cart_write(0xA001, 0x0C, sram=True) # data out (low)
1505
+
1506
+ value1, value2 = None, None
1507
+ while value1 is None or value1 != value2:
1508
+ value2 = value1
1509
+ value1 = self._cart_read(0xA000)
1510
+ data_l = value1
1511
+
1512
+ data = ((data_h & 0xF) << 4) | (data_l & 0xF)
1513
+ buffer.append(data)
1514
+ self.SetProgress({"action":"UPDATE_POS", "abortable":False, "pos":i+1})
1515
+
1516
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
1517
+
1518
+ self.NO_PROG_UPDATE = npu
1519
+ return buffer
1520
+
1521
+ def WriteRAM(self, address, buffer, command=None, max_length=256):
1522
+ length = len(buffer)
1523
+ num = math.ceil(length / max_length)
1524
+ dprint("Writing 0x{:X} bytes to cartridge RAM in {:d} iteration(s)".format(length, num))
1525
+ if length > max_length: length = max_length
1526
+
1527
+ self._set_fw_variable("TRANSFER_SIZE", length)
1528
+ if self.MODE == "DMG":
1529
+ self._set_fw_variable("ADDRESS", 0xA000 + address)
1530
+ self._set_fw_variable("DMG_ACCESS_MODE", 4) # MODE_RAM_WRITE
1531
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
1532
+ if command is None: command = self.DEVICE_CMD["DMG_CART_WRITE_SRAM"]
1533
+ elif self.MODE == "AGB":
1534
+ self._set_fw_variable("ADDRESS", address)
1535
+ if command is None: command = self.DEVICE_CMD["AGB_CART_WRITE_SRAM"]
1536
+
1537
+ for i in range(0, num):
1538
+ self._write(command)
1539
+ self._write(buffer[i*length:i*length+length], wait=True)
1540
+ #self._read(1)
1541
+ if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
1542
+ self.SetProgress({"action":"WRITE", "bytes_added":length})
1543
+
1544
+ if self.MODE == "DMG":
1545
+ self._set_fw_variable("ADDRESS", 0)
1546
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
1547
+
1548
+ return True
1549
+
1550
+ def WriteFlash_MBC6(self, address, buffer, mapper):
1551
+ length = len(buffer)
1552
+ max_length = 128
1553
+ num = math.ceil(length / max_length)
1554
+ if length > max_length: length = max_length
1555
+ dprint("Write 0x{:X} bytes to cartridge FLASH in {:d} iteration(s)".format(length, num))
1556
+
1557
+ skip_write = False
1558
+ for i in range(0, num):
1559
+ self._set_fw_variable("TRANSFER_SIZE", length)
1560
+ self._set_fw_variable("ADDRESS", address)
1561
+ dprint("Now in iteration {:d}".format(i))
1562
+
1563
+ if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
1564
+ skip_write = True
1565
+ address += length
1566
+ continue
1567
+
1568
+ cmds = [
1569
+ [ 0x2000, 0x01 ],
1570
+ [ 0x3000, 0x02 ],
1571
+ [ 0x7555, 0xAA ],
1572
+ [ 0x4AAA, 0x55 ],
1573
+ [ 0x7555, 0xA0 ],
1574
+ ]
1575
+ self._cart_write_flash(cmds)
1576
+ mapper.SelectBankFlash(mapper.GetROMBank())
1577
+ self._write(self.DEVICE_CMD["DMG_MBC6_MMSA_WRITE_FLASH"])
1578
+ self._write(buffer[i*length:i*length+length])
1579
+ ret = self._read(1)
1580
+ if ret not in (0x01, 0x03):
1581
+ dprint("Flash write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length))
1582
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]: return False
1583
+ 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)})
1584
+ self.CANCEL = True
1585
+ self.ERROR = True
1586
+ return False
1587
+ self._cart_write(address + length - 1, 0x00)
1588
+ hp = 100
1589
+ while hp > 0:
1590
+ sr = self._cart_read(address + length - 1)
1591
+ if sr == 0x80: break
1592
+ time.sleep(0.001)
1593
+ hp -= 1
1594
+
1595
+ if hp == 0:
1596
+ dprint("Flash write timeout (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length))
1597
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]: return False
1598
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Flash write timeout (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
1599
+ self.CANCEL = True
1600
+ self.ERROR = True
1601
+ return False
1602
+
1603
+ address += length
1604
+ if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
1605
+ self.SetProgress({"action":"WRITE", "bytes_added":length})
1606
+ self._cart_write(address - 1, 0xF0)
1607
+ self.SKIPPING = skip_write
1608
+
1609
+ def WriteEEPROM_MBC7(self, address, buffer):
1610
+ length = len(buffer)
1611
+ max_length = 32
1612
+ num = math.ceil(length / max_length)
1613
+ if length > max_length: length = max_length
1614
+ dprint("Write 0x{:X} bytes to cartridge EEPROM in {:d} iteration(s)".format(length, num))
1615
+ self._set_fw_variable("TRANSFER_SIZE", length)
1616
+ self._set_fw_variable("ADDRESS", address)
1617
+ for i in range(0, num):
1618
+ self._write(self.DEVICE_CMD["DMG_MBC7_WRITE_EEPROM"])
1619
+ self._write(buffer[i*length:i*length+length])
1620
+ response = self._read(1)
1621
+ dprint("Response:", response)
1622
+ if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
1623
+ self.SetProgress({"action":"WRITE", "bytes_added":length})
1624
+
1625
+ def WriteRAM_TAMA5(self, buffer):
1626
+ npu = self.NO_PROG_UPDATE
1627
+ self.NO_PROG_UPDATE = True
1628
+
1629
+ for i in range(0, 0x20):
1630
+ self._cart_write(0xA001, 0x05, sram=True) # data in (high)
1631
+ self._cart_write(0xA000, buffer[i] >> 4, sram=True)
1632
+ self._cart_write(0xA001, 0x04, sram=True) # data in (low)
1633
+ self._cart_write(0xA000, buffer[i] & 0xF, sram=True)
1634
+ self._cart_write(0xA001, 0x06, sram=True) # register select and address (high)
1635
+ self._cart_write(0xA000, i >> 4 | 0x00 << 1, sram=True) # bit 0 = higher ram address, rest = command
1636
+ self._cart_write(0xA001, 0x07, sram=True) # address (low)
1637
+ self._cart_write(0xA000, i & 0x0F, sram=True) # bits 0-3 = lower ram address
1638
+ value1, value2 = None, None
1639
+ while value1 is None or value1 != value2:
1640
+ value2 = value1
1641
+ value1 = self._cart_read(0xA000)
1642
+ self.SetProgress({"action":"UPDATE_POS", "abortable":False, "pos":i+1})
1643
+
1644
+ self.NO_PROG_UPDATE = npu
1645
+
1646
+ def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False, max_length=MAX_BUFFER_WRITE):
1647
+ length = len(buffer)
1648
+ num = math.ceil(length / max_length)
1649
+ dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
1650
+ if length == 0:
1651
+ dprint("Length is zero?")
1652
+ return False
1653
+ elif length > max_length:
1654
+ length = max_length
1655
+
1656
+ skip_write = False
1657
+ ret = 0
1658
+ num_of_chunks = math.ceil(flash_buffer_size / length)
1659
+ pos = 0
1660
+
1661
+ if not skip_init:
1662
+ self._set_fw_variable("TRANSFER_SIZE", length)
1663
+ if flash_buffer_size is not False:
1664
+ self._set_fw_variable("BUFFER_SIZE", flash_buffer_size)
1665
+
1666
+ for i in range(0, num):
1667
+ data = bytearray(buffer[i*length:i*length+length])
1668
+ if (num_of_chunks == 1 or flash_buffer_size == 0) and (data == bytearray([0xFF] * len(data))):
1669
+ skip_init = False
1670
+ skip_write = True
1671
+ else:
1672
+ if skip_write:
1673
+ skip_write = False
1674
+
1675
+ if not skip_write:
1676
+ if not skip_init:
1677
+ if self.MODE == "DMG":
1678
+ self._set_fw_variable("ADDRESS", address)
1679
+ elif self.MODE == "AGB":
1680
+ self._set_fw_variable("ADDRESS", address >> 1)
1681
+ # print("==>", hex(self._get_fw_variable("ADDRESS")), hex(self._get_fw_variable("TRANSFER_SIZE")), hex(self._get_fw_variable("BUFFER_SIZE")))
1682
+
1683
+ skip_init = True
1684
+
1685
+ if ret != 0x03: self._write(self.DEVICE_CMD["FLASH_PROGRAM"])
1686
+ ret = self._write(data, wait=True)
1687
+
1688
+ if ret not in (0x01, 0x03):
1689
+ dprint("Flash error at 0x{:X} in iteration {:d} of {:d} while trying to write a total of 0x{:X} bytes (response = {:s})".format(address, i, num, len(buffer), str(ret)))
1690
+ self.ERROR_ARGS = { "iteration":i }
1691
+ self.SKIPPING = False
1692
+ return False
1693
+ pos += len(data)
1694
+
1695
+ if rumble_stop and flash_buffer_size > 0 and pos % flash_buffer_size == 0:
1696
+ self._cart_write(address=0xC4, value=0x00, flashcart=True)
1697
+ self._cart_write(address=0xC6, value=0x00, flashcart=True)
1698
+ rumble_stop = False
1699
+
1700
+ address += length
1701
+ if ((pos % length) * 10 == 0) and (self.INFO["action"] in (self.ACTIONS["ROM_WRITE"], self.ACTIONS["SAVE_WRITE"]) and not self.NO_PROG_UPDATE):
1702
+ self.SetProgress({"action":"WRITE", "bytes_added":length, "skipping":skip_write})
1703
+
1704
+ self.SKIPPING = skip_write
1705
+
1706
+ def WriteROM_GBMEMORY(self, address, buffer, bank):
1707
+ length = len(buffer)
1708
+ max_length = 128
1709
+ num = math.ceil(length / max_length)
1710
+ if length > max_length: length = max_length
1711
+ dprint("Writing 0x{:X} bytes to GB-Memory Flash ROM in {:d} iteration(s)".format(length, num))
1712
+
1713
+ skip_write = False
1714
+ for i in range(0, num):
1715
+ self._set_fw_variable("TRANSFER_SIZE", length)
1716
+ self._set_fw_variable("ADDRESS", address)
1717
+ dprint("Now in iteration {:d}".format(i))
1718
+
1719
+ if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
1720
+ skip_write = True
1721
+ address += length
1722
+ continue
1723
+
1724
+ # Enable flash chip access
1725
+ self._cart_write_flash([
1726
+ [ 0x120, 0x09 ],
1727
+ [ 0x121, 0xAA ],
1728
+ [ 0x122, 0x55 ],
1729
+ [ 0x13F, 0xA5 ],
1730
+ ])
1731
+ # Re-Enable writes to MBC registers
1732
+ self._cart_write_flash([
1733
+ [ 0x120, 0x11 ],
1734
+ [ 0x13F, 0xA5 ],
1735
+ ])
1736
+ # Bank 1 for commands
1737
+ self._cart_write_flash([
1738
+ [ 0x2100, 0x01 ],
1739
+ ])
1740
+
1741
+ # Write setup
1742
+ self._cart_write_flash([
1743
+ [ 0x120, 0x0F ],
1744
+ [ 0x125, 0x55 ],
1745
+ [ 0x126, 0x55 ],
1746
+ [ 0x127, 0xAA ],
1747
+ [ 0x13F, 0xA5 ],
1748
+ ])
1749
+ self._cart_write_flash([
1750
+ [ 0x120, 0x0F ],
1751
+ [ 0x125, 0x2A ],
1752
+ [ 0x126, 0xAA ],
1753
+ [ 0x127, 0x55 ],
1754
+ [ 0x13F, 0xA5 ],
1755
+ ])
1756
+ self._cart_write_flash([
1757
+ [ 0x120, 0x0F ],
1758
+ [ 0x125, 0x55 ],
1759
+ [ 0x126, 0x55 ],
1760
+ [ 0x127, 0xA0 ],
1761
+ [ 0x13F, 0xA5 ],
1762
+ ])
1763
+
1764
+ # Set bank back
1765
+ self._cart_write_flash([
1766
+ [ 0x2100, bank ],
1767
+ ])
1768
+
1769
+ # Disable writes to MBC registers
1770
+ self._cart_write_flash([
1771
+ [ 0x120, 0x10 ],
1772
+ [ 0x13F, 0xA5 ],
1773
+ ])
1774
+
1775
+ # Undo Wakeup
1776
+ self._cart_write_flash([
1777
+ [ 0x120, 0x08 ],
1778
+ [ 0x13F, 0xA5 ],
1779
+ ])
1780
+
1781
+ self._write(self.DEVICE_CMD["DMG_MBC6_MMSA_WRITE_FLASH"])
1782
+ self._write(buffer[i*length:i*length+length])
1783
+ ret = self._read(1)
1784
+ if ret not in (0x01, 0x03):
1785
+ #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)})
1786
+ #self.CANCEL = True
1787
+ #self.ERROR = True
1788
+ return False
1789
+
1790
+ self._cart_write(address + length - 1, 0xFF)
1791
+ while True:
1792
+ sr = self._cart_read(address + length - 1)
1793
+ if sr & 0x80 == 0x80: break
1794
+ time.sleep(0.001)
1795
+
1796
+ address += length
1797
+ if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
1798
+ self.SetProgress({"action":"WRITE", "bytes_added":length})
1799
+
1800
+ self._cart_write(address - 1, 0xF0)
1801
+ self.SKIPPING = skip_write
1802
+
1803
+ def WriteROM_DMG_MBC5_32M_FLASH(self, address, buffer, bank):
1804
+ length = len(buffer)
1805
+ max_length = 128
1806
+ num = math.ceil(length / max_length)
1807
+ if length > max_length: length = max_length
1808
+ dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
1809
+
1810
+ skip_write = False
1811
+ for i in range(0, num):
1812
+ self._set_fw_variable("TRANSFER_SIZE", length)
1813
+ self._set_fw_variable("ADDRESS", address)
1814
+ dprint("Now in iteration {:d}".format(i))
1815
+
1816
+ if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
1817
+ skip_write = True
1818
+ address += length
1819
+ continue
1820
+
1821
+ self._cart_write(0x4000, 0xFF)
1822
+ self._cart_write_flash([
1823
+ [ address, 0xE0 ],
1824
+ [ address, length - 1 ],
1825
+ [ address, 0x00 ],
1826
+ ])
1827
+ self._write(self.DEVICE_CMD["DMG_MBC6_MMSA_WRITE_FLASH"])
1828
+ self._write(buffer[i*length:i*length+length])
1829
+ ret = self._read(1)
1830
+ if ret not in (0x01, 0x03):
1831
+ 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)})
1832
+ self.CANCEL = True
1833
+ self.ERROR = True
1834
+ return False
1835
+
1836
+ self._cart_write_flash([
1837
+ [ address, 0x0C ],
1838
+ [ address, length - 1 ],
1839
+ [ address, 0x00 ],
1840
+ ])
1841
+ lives = 100
1842
+ while lives > 0:
1843
+ self._cart_write(0x4000, 0x70)
1844
+ try:
1845
+ sr = self._cart_read(address + length - 1)
1846
+ dprint("sr=0x{:X}".format(sr))
1847
+ except:
1848
+ sr = 0
1849
+ dprint("sr=Error")
1850
+ if sr & 0x80 == 0x80: break
1851
+ lives -= 1
1852
+ self._cart_write(0x4000, 0xFF)
1853
+
1854
+ if lives == 0:
1855
+ 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(sr), i, length)})
1856
+ self.CANCEL = True
1857
+ self.ERROR = True
1858
+ return False
1859
+
1860
+ address += length
1861
+ if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
1862
+ self.SetProgress({"action":"WRITE", "bytes_added":length})
1863
+
1864
+ self._cart_write(address - 1, 0xFF)
1865
+ self.SKIPPING = skip_write
1866
+
1867
+ def WriteROM_DMG_DatelOrbitV2(self, address, buffer, bank):
1868
+ length = len(buffer)
1869
+ dprint("Writing 0x{:X} bytes to Datel Orbit V2 cartridge".format(length))
1870
+ for i in range(0, length):
1871
+ self._cart_write(0x7FE1, 2)
1872
+ self._cart_write(0x5555, 0xAA)
1873
+ self._cart_write(0x2AAA, 0x55)
1874
+ self._cart_write(0x5555, 0xA0)
1875
+ self._cart_write(0x7FE1, bank)
1876
+ self._cart_write(address + i, buffer[i])
1877
+ if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
1878
+ self.SetProgress({"action":"WRITE", "bytes_added":1})
1879
+ return True
1880
+
1881
+ def WriteROM_DMG_EEPROM(self, address, buffer, bank, eeprom_buffer_size=0x80):
1882
+ length = len(buffer)
1883
+ if not self.CanPowerCycleCart() or self.BAUDRATE == 1000000:
1884
+ max_length = 256
1885
+ else:
1886
+ max_length = 1024
1887
+ num = math.ceil(length / max_length)
1888
+ if length > max_length: length = max_length
1889
+ dprint("Writing 0x{:X} bytes to EEPROM in {:d} iteration(s)".format(length, num))
1890
+
1891
+ for i in range(0, num):
1892
+ self._set_fw_variable("BUFFER_SIZE", eeprom_buffer_size)
1893
+ self._set_fw_variable("TRANSFER_SIZE", length)
1894
+ self._set_fw_variable("ADDRESS", address)
1895
+ dprint("Now in iteration {:d}".format(i))
1896
+
1897
+ self._write(self.DEVICE_CMD["DMG_EEPROM_WRITE"])
1898
+ self._write(buffer[i*length:i*length+length])
1899
+ ret = self._read(1)
1900
+ if ret not in (0x01, 0x03):
1901
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"EEPROM write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
1902
+ self.CANCEL = True
1903
+ self.ERROR = True
1904
+ return False
1905
+
1906
+ address += length
1907
+ if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
1908
+ self.SetProgress({"action":"WRITE", "bytes_added":length})
1909
+
1910
+ commands = [
1911
+ [0x0006, 0x01],
1912
+ [0x5555, 0xAA],
1913
+ [0x2AAA, 0x55],
1914
+ [0x5555, 0xF0],
1915
+ [0x0006, bank]
1916
+ ]
1917
+ for command in commands:
1918
+ self._cart_write(command[0], command[1])
1919
+
1920
+ def CheckROMStable(self):
1921
+ if not self.IsConnected(): raise ConnectionError("Couldn’t access the the device.")
1922
+ if self.CanPowerCycleCart():
1923
+ self.CartPowerOn()
1924
+
1925
+ buffer1 = self.ReadROM(0x80, 0x40)
1926
+ time.sleep(0.1)
1927
+ buffer2 = self.ReadROM(0x80, 0x40)
1928
+ return buffer1 == buffer2
1929
+
1930
+ def CompareCRC32(self, buffer, offset, length, address, flashcart=None, max_length=0x20000, reset=False):
1931
+ left = length
1932
+ chunk_pos = 0
1933
+ verified = False
1934
+ while left > 0:
1935
+ chunk_len = max_length if left > max_length else left
1936
+ chunk_from = offset + chunk_pos
1937
+ chunk_to = offset + chunk_pos + chunk_len
1938
+ dprint(f"Running CRC32 verification, comparing between source 0x{chunk_from:x}~0x{chunk_to:x} and target 0x{address+chunk_pos:x}~0x{address+chunk_pos+chunk_len:x}")
1939
+ crc32_expected = zlib.crc32(buffer[chunk_from:chunk_to])
1940
+
1941
+ for i in range(0, 2 if (reset is True and flashcart is not None) else 1): # for retrying with reset
1942
+ if self.MODE == "DMG":
1943
+ self._set_fw_variable("ADDRESS", address + chunk_pos)
1944
+ elif self.MODE == "AGB":
1945
+ self._set_fw_variable("ADDRESS", (address + chunk_pos) >> 1)
1946
+ self._write(self.DEVICE_CMD["CALC_CRC32"])
1947
+ self._write(bytearray(struct.pack(">I", chunk_len)))
1948
+ temp = self._read(4)
1949
+ if temp is False:
1950
+ crc32_calculated = False
1951
+ else:
1952
+ crc32_calculated = struct.unpack(">I", temp)[0]
1953
+ if crc32_expected != crc32_calculated:
1954
+ if i == 0 and flashcart is not None:
1955
+ flashcart.Reset(full_reset=True)
1956
+ verified = (crc32_expected, crc32_calculated)
1957
+ continue
1958
+ else:
1959
+ return (crc32_expected, crc32_calculated)
1960
+ else:
1961
+ verified = True
1962
+ break
1963
+
1964
+ left -= chunk_len
1965
+ chunk_pos += chunk_len
1966
+
1967
+ return verified
1968
+
1969
+ def DetectFlash(self, limitVoltage=False):
1970
+ supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
1971
+ fc_fncptr = {
1972
+ "cart_write_fncptr":self._cart_write,
1973
+ "cart_write_fast_fncptr":self._cart_write_flash,
1974
+ "cart_read_fncptr":self.ReadROM,
1975
+ "cart_powercycle_fncptr":self.CartPowerCycleOrAskReconnect,
1976
+ "progress_fncptr":self.SetProgress,
1977
+ "set_we_pin_wr":self._set_we_pin_wr,
1978
+ "set_we_pin_audio":self._set_we_pin_audio,
1979
+ }
1980
+
1981
+ detected_size = 0
1982
+ cfi = False
1983
+ cfi_buffer = bytearray()
1984
+ cfi_s = ""
1985
+ flash_type_id = 0
1986
+ flash_id_s = ""
1987
+ flash_types = []
1988
+ flash_id_methods = []
1989
+
1990
+ if self.MODE == "DMG":
1991
+ if limitVoltage:
1992
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
1993
+ else:
1994
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
1995
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
1996
+
1997
+ elif self.MODE == "AGB":
1998
+ read_method = self.AGB_READ_METHOD
1999
+ self.SetAGBReadMethod(0)
2000
+ self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
2001
+
2002
+ rom = self._cart_read(0, 8)
2003
+
2004
+ dprint("Resetting and unlocking all cart types")
2005
+ cmds = []
2006
+ cmds_reset = []
2007
+ cmds_unlock_read = []
2008
+ for f in range(1, len(supported_carts)):
2009
+ cart_type = supported_carts[f]
2010
+ if "command_set" not in cart_type: continue
2011
+ if "manual_select" in cart_type and cart_type["manual_select"] is True: continue
2012
+ if self.MODE == "DMG" and cart_type["command_set"] == "BLAZE_XPLODER":
2013
+ self._cart_read(0x102, 1)
2014
+ self._cart_write(6, 1)
2015
+ self._cart_write_flash(cart_type["commands"]["reset"])
2016
+ self._cart_write(6, 0)
2017
+ rom1 = self._cart_read(0x4000, 8)
2018
+ self._cart_write(6, 1)
2019
+ self._cart_write_flash(cart_type["commands"]["read_identifier"])
2020
+ rom2 = self._cart_read(0x4000, 8)
2021
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2022
+ found = True
2023
+ flash_types.append(f)
2024
+ dprint("Found a BLAZE Xploder GB")
2025
+ self._cart_write_flash(cart_type["commands"]["reset"])
2026
+ break
2027
+ continue
2028
+ elif self.MODE == "DMG" and cart_type["command_set"] == "DATEL_ORBITV2":
2029
+ rom1 = self._cart_read(cart_type["read_identifier_at"], 10)
2030
+ for cmd in cart_type["commands"]["unlock_read"]: self._cart_read(cmd[0], 1)
2031
+ self._cart_write_flash(cart_type["commands"]["unlock"])
2032
+ self._cart_write_flash(cart_type["commands"]["read_identifier"])
2033
+ rom2 = self._cart_read(cart_type["read_identifier_at"], 10)
2034
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2035
+ found = True
2036
+ flash_types.append(f)
2037
+ dprint("Found a GameShark or Action Replay")
2038
+ self._cart_write_flash(cart_type["commands"]["reset"])
2039
+ break
2040
+ continue
2041
+ elif self.MODE == "DMG" and cart_type["command_set"] == "GBMEMORY":
2042
+ rom1 = self._cart_read(0, 8)
2043
+ self._cart_write_flash(cart_type["commands"]["unlock"])
2044
+ self._cart_write_flash(cart_type["commands"]["read_identifier"])
2045
+ rom2 = self._cart_read(0, 8)
2046
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2047
+ found = True
2048
+ flash_types.append(f)
2049
+ dprint("Found a GB-Memory Cartridge")
2050
+ self._cart_write_flash(cart_type["commands"]["reset"])
2051
+ break
2052
+ continue
2053
+ elif self.MODE == "DMG" and cart_type["command_set"] == "DMG-MBC5-32M-FLASH":
2054
+ self._set_we_pin_audio()
2055
+ self._cart_write_flash(cart_type["commands"]["unlock"], flashcart=False)
2056
+ self._cart_write_flash(cart_type["commands"]["reset"])
2057
+ rom1 = self._cart_read(0, 8)
2058
+ self._cart_write_flash(cart_type["commands"]["read_identifier"])
2059
+ rom2 = self._cart_read(0, 8)
2060
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2061
+ found = True
2062
+ flash_types.append(f)
2063
+ dprint("Found a DMG-MBC5-32M-FLASH Development Cartridge")
2064
+ self._cart_write_flash(cart_type["commands"]["reset"])
2065
+ break
2066
+ self._set_we_pin_wr()
2067
+ continue
2068
+ elif self.MODE == "AGB" and cart_type["command_set"] == "GBAMP":
2069
+ rom1 = self._cart_read(0x1E8F << 1, 2) + self._cart_read(0x168F << 1, 2)
2070
+ for cmd in cart_type["commands"]["unlock_read"]: self._cart_read(cmd[0] << 1)
2071
+ self._cart_write_flash(cart_type["commands"]["read_identifier"], flashcart=True)
2072
+ rom2 = self._cart_read(0x1E8F << 1, 2) + self._cart_read(0x168F << 1, 2)
2073
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2074
+ found = True
2075
+ flash_types.append(f)
2076
+ dprint("Found a GBA Movie Player v2")
2077
+ self._cart_write_flash(cart_type["commands"]["reset"], flashcart=True)
2078
+ break
2079
+ continue
2080
+ elif self.MODE == "DMG" and cart_type["command_set"] == "BUNG_16M":
2081
+ self._set_we_pin_audio()
2082
+ rom1 = self._cart_read(0, 4)
2083
+ self._cart_write(0x2000, 0x02, flashcart=False)
2084
+ self._cart_write(0x6AAA, 0xAA, flashcart=True)
2085
+ self._cart_write(0x2000, 0x01, flashcart=False)
2086
+ self._cart_write(0x5554, 0x55, flashcart=True)
2087
+ self._cart_write(0x2000, 0x02, flashcart=False)
2088
+ self._cart_write(0x6AAA, 0x90, flashcart=True)
2089
+ rom2 = self._cart_read(0, 4)
2090
+ if rom1 != rom2 and list(rom2[:len(cart_type["flash_ids"][0])]) == cart_type["flash_ids"][0]:
2091
+ found = True
2092
+ flash_types.append(f)
2093
+ dprint("Found a BUNG Doctor GB Card 16M")
2094
+ self._cart_write(0x2000, 0x02, flashcart=False)
2095
+ self._cart_write(0x6AAA, 0xAA, flashcart=True)
2096
+ self._cart_write(0x2000, 0x01, flashcart=False)
2097
+ self._cart_write(0x5554, 0x55, flashcart=True)
2098
+ self._cart_write(0x2000, 0x02, flashcart=False)
2099
+ self._cart_write(0x6AAA, 0xF0, flashcart=True)
2100
+ self._cart_write(0x2000, 0x00, flashcart=False)
2101
+ break
2102
+ self._set_we_pin_wr()
2103
+ continue
2104
+
2105
+ if "commands" in cart_type:
2106
+ c = {
2107
+ "reset":[],
2108
+ "read_identifier":[],
2109
+ "read_cfi":[],
2110
+ }
2111
+ if "reset" in cart_type["commands"]:
2112
+ c["reset"] = cart_type["commands"]["reset"]
2113
+ if cart_type["commands"]["reset"] not in cmds_reset:
2114
+ cmds_reset.append(cart_type["commands"]["reset"])
2115
+ if "unlock_read" in cart_type["commands"]:
2116
+ if cart_type["commands"]["unlock_read"] not in cmds_unlock_read:
2117
+ cmds_unlock_read.append(cart_type["commands"]["unlock_read"])
2118
+ if "unlock" in cart_type["commands"]:
2119
+ if cart_type["commands"]["unlock"] not in cmds_reset:
2120
+ cmds_reset.append(cart_type["commands"]["unlock"])
2121
+ if cart_type["commands"]["reset"] not in cmds_reset:
2122
+ cmds_reset.append(cart_type["commands"]["reset"])
2123
+ if "read_identifier" in cart_type["commands"]:
2124
+ c["read_identifier"] = cart_type["commands"]["read_identifier"]
2125
+ if "read_cfi" in cart_type["commands"]:
2126
+ c["read_cfi"] = cart_type["commands"]["read_cfi"]
2127
+ if len(c["read_identifier"]) > 0:
2128
+ if self.MODE == "DMG" and cart_type["commands"]["read_identifier"][0][0] > 0x7000: continue
2129
+ if c not in cmds:
2130
+ found = False
2131
+ for t in cmds:
2132
+ if c["read_identifier"] == t["read_identifier"]:
2133
+ found = True
2134
+ if not found:
2135
+ cmds.append(c)
2136
+
2137
+ for c in cmds_reset:
2138
+ for cmd in c:
2139
+ dprint(f"Resetting by writing to 0x{cmd[0]:X}=0x{cmd[1]:X}")
2140
+ self._cart_write(cmd[0], cmd[1], flashcart=True)
2141
+
2142
+ flash_id_cmds = sorted(cmds, key=lambda x: x['read_identifier'][0][0])
2143
+
2144
+ rom_s = " ".join(format(x, '02X') for x in rom)
2145
+ if self.MODE == "DMG":
2146
+ flash_id_s = "[ ROM ] " + rom_s + "\n"
2147
+ we_pins = [ "WR", "AUDIO" ]
2148
+ else:
2149
+ flash_id_s = "[ ROM ] " + rom_s + "\n"
2150
+ we_pins = [ None ]
2151
+
2152
+ if len(flash_types) == 0:
2153
+ dprint("Trying to find the Flash ID")
2154
+ if self.SupportsAudioAsWe():
2155
+ wes = {"DMG":[1, 2], "AGB":[0]}
2156
+ else:
2157
+ wes = {"DMG":[1], "AGB":[0]}
2158
+ #rom = self._cart_read(0, 8)
2159
+ for we in wes[self.MODE]:
2160
+ if self.MODE == "DMG": self._set_fw_variable("FLASH_WE_PIN", we)
2161
+ for i in range(0, len(flash_id_cmds)):
2162
+ self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2163
+ self._cart_write_flash(flash_id_cmds[i]["read_identifier"], flashcart=True)
2164
+
2165
+ cmp = self._cart_read(0, 8)
2166
+ self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2167
+ if rom != cmp: # ROM data changed
2168
+ if "read_cfi" not in flash_id_cmds[i]:
2169
+ flash_id_cmds[i]["read_cfi"] = [[ flash_id_cmds[i]["read_identifier"][0][0], 0x98 ]]
2170
+ self._cart_write_flash(flash_id_cmds[i]["read_cfi"], flashcart=True)
2171
+ cfi_buffer = self._cart_read(0, 0x400)
2172
+ self._cart_write_flash(flash_id_cmds[i]["reset"], flashcart=True)
2173
+ flash_id_methods.append([we - 1, i, list(cmp), cfi_buffer, flash_id_cmds[i]["read_identifier"]])
2174
+
2175
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2176
+ with open("debug_cfi.bin", "wb") as f: f.write(cfi_buffer)
2177
+ magic = "{:s}{:s}{:s}".format(chr(cfi_buffer[0x20]), chr(cfi_buffer[0x22]), chr(cfi_buffer[0x24]))
2178
+ d_swap = (0, 0)
2179
+ if magic == "QRY": # D0D1 not swapped
2180
+ pass
2181
+ elif magic == "RQZ": # D0D1 swapped
2182
+ d_swap = [(0, 1)]
2183
+ for j2 in range(0, len(d_swap)):
2184
+ for j in range(0, len(cfi_buffer)):
2185
+ cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2186
+ elif magic == "\x92\x91\x9A": # D0D1+D6D7 swapped
2187
+ d_swap = [( 0, 1 ), ( 6, 7 )]
2188
+ for j2 in range(0, len(d_swap)):
2189
+ for j in range(0, len(cfi_buffer)):
2190
+ cfi_buffer[j] = bitswap(cfi_buffer[j], d_swap[j2])
2191
+ with open("debug_cfi_2.bin", "wb") as f: f.write(cfi_buffer)
2192
+ else:
2193
+ cfi_buffer = None
2194
+
2195
+ if self.MODE == "DMG":
2196
+ 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))
2197
+ else:
2198
+ 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))
2199
+
2200
+ dprint(f"Found {len(flash_id_methods):d} result(s)")
2201
+ self._cart_write_flash([[0, 0xFF]], True)
2202
+ self._cart_write_flash([[0, 0xF0]], True)
2203
+
2204
+ for f in range(1, len(supported_carts)):
2205
+ cart_type = supported_carts[f]
2206
+ flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
2207
+ if "flash_ids" not in cart_type or len(cart_type["flash_ids"]) == 0: continue
2208
+ if "commands" not in cart_type or len(cart_type["commands"]) == 0: continue
2209
+ found = False
2210
+ if flash_id_methods not in (None, []) and len(flash_id_methods) > 0:
2211
+ for we, type, flash_id, _, cmd_rfi in flash_id_methods:
2212
+ if (cmd_rfi != cart_type["commands"]["read_identifier"]): continue
2213
+ if self.MODE == "DMG" and "write_pin" in cart_type and cart_type["write_pin"] != we_pins[we]: continue
2214
+ fcm_flash_ids = list(map(list, {tuple(sublist) for sublist in cart_type["flash_ids"]}))
2215
+ for fcm_flash_id in fcm_flash_ids:
2216
+ if fcm_flash_id == flash_id[:len(fcm_flash_id)]:
2217
+ if self.MODE == "DMG":
2218
+ dprint("“{:s}” matches with Flash ID “{:s}” ({:s}/{:X}/{:X})".format(cart_type["names"][0], " ".join(format(x, '02X') for x in fcm_flash_id), we_pins[we], flash_id_cmds[type]["read_identifier"][0][0], flash_id_cmds[type]["read_identifier"][0][1]))
2219
+ elif self.MODE == "AGB":
2220
+ dprint("“{:s}” matches with Flash ID “{:s}” ({:X})/{:X}".format(cart_type["names"][0], " ".join(format(x, '02X') for x in fcm_flash_id), flash_id_cmds[type]["read_identifier"][0][0], flash_id_cmds[type]["read_identifier"][0][1]))
2221
+ found = True
2222
+ flash_type = f
2223
+ flash_types.append(flash_type)
2224
+ if "commands" in cart_type and "reset" in cart_type["commands"]:
2225
+ self._cart_write_flash(cart_type["commands"]["reset"], flashcart=True)
2226
+ break
2227
+ if found is True: break
2228
+ if found is False: continue
2229
+
2230
+ dprint("Compatible flash types:", [(index, supported_carts[index]["names"][0]) for index in flash_types])
2231
+
2232
+ if cfi_buffer is None or len(cfi_buffer) < 0x400:
2233
+ cfi = False
2234
+ else:
2235
+ cfi = ParseCFI(cfi_buffer)
2236
+ if cfi is not False:
2237
+ cfi["raw"] = cfi_buffer
2238
+ s = ""
2239
+ if d_swap is not None and d_swap != ( 0, 0 ): s += "Swapped pins: {:s}\n".format(str(d_swap).replace("[", "").replace("]", ""))
2240
+ s += "Device size: 0x{:07X} ({:.2f} MiB)\n".format(cfi["device_size"], cfi["device_size"] / 1024 / 1024)
2241
+ s += "Voltage: {:.1f}–{:.1f} V\n".format(cfi["vdd_min"], cfi["vdd_max"])
2242
+ s += "Single write: {:s}\n".format(str(cfi["single_write"]))
2243
+ if "buffer_size" in cfi:
2244
+ s += "Buffered write: {:s} ({:d} Bytes)\n".format(str(cfi["buffer_write"]), cfi["buffer_size"])
2245
+ else:
2246
+ s += "Buffered write: {:s}\n".format(str(cfi["buffer_write"]))
2247
+ if cfi["chip_erase"]: s += "Chip erase: {:d}–{:d} ms\n".format(cfi["chip_erase_time_avg"], cfi["chip_erase_time_max"])
2248
+ if cfi["sector_erase"]: s += "Sector erase: {:d}–{:d} ms\n".format(cfi["sector_erase_time_avg"], cfi["sector_erase_time_max"])
2249
+ if cfi["tb_boot_sector"] is not False: s += "Sector flags: {:s}\n".format(str(cfi["tb_boot_sector"]))
2250
+ pos = 0
2251
+ oversize = False
2252
+ s = s[:-1]
2253
+ for i in range(0, cfi['erase_sector_regions']):
2254
+ esb = cfi['erase_sector_blocks'][i]
2255
+ s += "\nRegion {:d}: 0x{:07X}–0x{:07X} @ 0x{:X} Bytes × {:d}".format(i+1, pos, pos+esb[2]-1, esb[0], esb[1])
2256
+ if oversize: s += " (alt)"
2257
+ pos += esb[2]
2258
+ if pos >= cfi['device_size']:
2259
+ pos = 0
2260
+ oversize = True
2261
+ cfi_s = s
2262
+
2263
+ # Check flash size
2264
+ if len(flash_types) > 0:
2265
+ flash_type_id = flash_types[0]
2266
+ flashcart = Flashcart(config=supported_carts[flash_type_id], fncptr=fc_fncptr)
2267
+ if self.MODE == "DMG":
2268
+ supp_flash_types = self.GetSupportedCartridgesDMG()
2269
+ elif self.MODE == "AGB":
2270
+ supp_flash_types = self.GetSupportedCartridgesAGB()
2271
+
2272
+ if "flash_size" in supp_flash_types[1][flash_types[0]]:
2273
+ size = supp_flash_types[1][flash_types[0]]["flash_size"]
2274
+ size_undetected = False
2275
+ for i in range(0, len(flash_types)):
2276
+ if "flash_size" in supp_flash_types[1][flash_types[i]]:
2277
+ if size != supp_flash_types[1][flash_types[i]]["flash_size"]:
2278
+ size_undetected = True
2279
+
2280
+ if size_undetected:
2281
+ if "flash_bank_select_type" in supp_flash_types[1][flash_types[0]] and supp_flash_types[1][flash_types[0]]["flash_bank_select_type"] == 1:
2282
+ # Check where the ROM data repeats (by bank switching)
2283
+ flashcart.SelectBankROM(0)
2284
+ size_check = self.ReadROM(0, 0x1000) + self.ReadROM(0x1FFF000, 0x1000)
2285
+ num_banks = 1
2286
+ while num_banks < (flashcart.GetFlashSize() // 0x2000000) + 1:
2287
+ dprint("Checking bank {:d}".format(num_banks))
2288
+ flashcart.SelectBankROM(num_banks)
2289
+ buffer = self.ReadROM(0, 0x1000) + self.ReadROM(0x1FFF000, 0x1000)
2290
+ if buffer == size_check: break
2291
+ num_banks <<= 1
2292
+ detected_size = 0x2000000 * num_banks
2293
+ for i in range(0, len(flash_types)):
2294
+ if detected_size == supp_flash_types[1][flash_types[i]]["flash_size"]:
2295
+ dprint("Detected {:d} flash banks".format(num_banks))
2296
+ flash_type_id = flash_types[i]
2297
+ size_undetected = False
2298
+ break
2299
+ flashcart.SelectBankROM(0)
2300
+
2301
+ elif isinstance(cfi, dict) and "device_size" in cfi:
2302
+ for i in range(0, len(flash_types)):
2303
+ if "flash_size" in supp_flash_types[1][flash_types[i]] and cfi['device_size'] == supp_flash_types[1][flash_types[i]]["flash_size"]:
2304
+ flash_type_id = flash_types[i]
2305
+ size_undetected = False
2306
+ break
2307
+ else:
2308
+ if self.MODE == "AGB":
2309
+ # Check where the ROM data repeats (for unlicensed carts)
2310
+ header = self.ReadROM(0, 0x180)
2311
+ size_check = header[0xA0:0xA0+16]
2312
+ currAddr = 0x10000
2313
+ while currAddr < 0x2000000:
2314
+ buffer = self.ReadROM(currAddr + 0xA0, 64)[:16]
2315
+ if buffer == size_check: break
2316
+ currAddr *= 2
2317
+ rom_size = currAddr
2318
+
2319
+ for i in range(0, len(flash_types)):
2320
+ if "flash_size" in supp_flash_types[1][flash_types[i]] and rom_size == supp_flash_types[1][flash_types[i]]["flash_size"]:
2321
+ flash_type_id = flash_types[i]
2322
+ size_undetected = False
2323
+ break
2324
+
2325
+ if self.MODE == "DMG":
2326
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
2327
+ self._set_fw_variable("FLASH_WE_PIN", 1) # back to WE=WR
2328
+ time.sleep(0.1)
2329
+ elif self.MODE == "AGB":
2330
+ self.SetAGBReadMethod(read_method)
2331
+
2332
+ return (flash_types, flash_type_id, flash_id_s, cfi_s, cfi, detected_size)
2333
+
2334
+ def GetDumpReport(self):
2335
+ return Util.GetDumpReport(self.INFO["dump_info"], self)
2336
+
2337
+ def GetReadErrors(self):
2338
+ return self.READ_ERRORS
2339
+
2340
+ #################################################################
2341
+
2342
+ def DoTransfer(self, mode, fncSetProgress, args):
2343
+ from . import DataTransfer
2344
+ args['mode'] = mode
2345
+ args['port'] = self
2346
+ if self.WORKER is None:
2347
+ self.WORKER = DataTransfer.DataTransfer(args)
2348
+ if fncSetProgress not in (False, None):
2349
+ self.WORKER.updateProgress.connect(fncSetProgress)
2350
+ else:
2351
+ self.WORKER.setConfig(args)
2352
+ self.WORKER.start()
2353
+
2354
+ def BackupROM(self, fncSetProgress=None, args=None):
2355
+ self.DoTransfer(1, fncSetProgress, args)
2356
+
2357
+ def BackupRAM(self, fncSetProgress=None, args=None):
2358
+ if fncSetProgress is False:
2359
+ args['mode'] = 2
2360
+ args['port'] = self
2361
+ self._BackupRestoreRAM(args=args)
2362
+ else:
2363
+ self.DoTransfer(2, fncSetProgress, args)
2364
+
2365
+ def RestoreRAM(self, fncSetProgress=None, args=None):
2366
+ self.DoTransfer(3, fncSetProgress, args)
2367
+
2368
+ def FlashROM(self, fncSetProgress=None, args=None):
2369
+ self.DoTransfer(4, fncSetProgress, args)
2370
+
2371
+ #################################################################
2372
+
2373
+ def _BackupROM(self, args):
2374
+ file = None
2375
+ if len(args["path"]) > 0:
2376
+ file = open(args["path"], "wb")
2377
+
2378
+ self.FAST_READ = True
2379
+ agb_read_method = self.AGB_READ_METHOD
2380
+ dmg_read_method = self.DMG_READ_METHOD
2381
+ flashcart = False
2382
+ supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
2383
+ cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
2384
+ if not isinstance(cart_type, str):
2385
+ cart_type["_index"] = 0
2386
+ for i in range(0, len(list(self.SUPPORTED_CARTS[self.MODE].keys()))):
2387
+ if i == args["cart_type"]:
2388
+ try:
2389
+ cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i])
2390
+
2391
+ fc_fncptr = {
2392
+ "cart_write_fncptr":self._cart_write,
2393
+ "cart_write_fast_fncptr":self._cart_write_flash,
2394
+ "cart_read_fncptr":self.ReadROM,
2395
+ "cart_powercycle_fncptr":self.CartPowerCycleOrAskReconnect,
2396
+ "progress_fncptr":self.SetProgress,
2397
+ "set_we_pin_wr":self._set_we_pin_wr,
2398
+ "set_we_pin_audio":self._set_we_pin_audio,
2399
+ }
2400
+ flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
2401
+ except:
2402
+ pass
2403
+
2404
+ # Firmware check L8
2405
+ if self.FW["fw_ver"] < 8 and flashcart and "enable_pullups" in cart_type:
2406
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8.", "abortable":False})
2407
+ return False
2408
+ # Firmware check L8
2409
+
2410
+ buffer_len = 0x4000
2411
+
2412
+ self.INFO["dump_info"]["timestamp"] = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
2413
+ self.INFO["dump_info"]["file_name"] = args["path"]
2414
+ self.INFO["dump_info"]["file_size"] = args["rom_size"]
2415
+ self.INFO["dump_info"]["cart_type"] = args["cart_type"]
2416
+ self.INFO["dump_info"]["system"] = self.MODE
2417
+
2418
+ is_3dmemory = (self.MODE == "AGB" and "command_set" in cart_type and cart_type["command_set"] == "3DMEMORY")
2419
+
2420
+ if self.MODE == "DMG":
2421
+ self.INFO["dump_info"]["rom_size"] = args["rom_size"]
2422
+ self.INFO["dump_info"]["mapper_type"] = args["mbc"]
2423
+ self.INFO["dump_info"]["dmg_read_method"] = self.DMG_READ_METHODS[self.DMG_READ_METHOD]
2424
+
2425
+ self.INFO["mapper_raw"] = args["mbc"]
2426
+ if not self.IsSupportedMbc(args["mbc"]):
2427
+ msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device.".format(Util.APPNAME, self.GetFullName())
2428
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
2429
+ return False
2430
+
2431
+ if "verify_mbc" in args and args["verify_mbc"] is not None:
2432
+ _mbc = args["verify_mbc"]
2433
+ else:
2434
+ _mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
2435
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
2436
+
2437
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
2438
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
2439
+ if _mbc.GetName() == "TAMA5":
2440
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
2441
+ self._set_fw_variable("DMG_READ_CS_PULSE", 1)
2442
+ _mbc.EnableMapper()
2443
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
2444
+ elif _mbc.GetName() == "Sachen":
2445
+ start_bank = int(args["rom_size"] / 0x4000)
2446
+ _mbc.SetStartBank(start_bank)
2447
+ else:
2448
+ _mbc.EnableMapper()
2449
+
2450
+ rom_size = args["rom_size"]
2451
+ rom_banks = _mbc.GetROMBanks(rom_size)
2452
+ rom_bank_size = _mbc.GetROMBankSize()
2453
+ size = _mbc.GetROMSize()
2454
+
2455
+ elif self.MODE == "AGB":
2456
+ self.INFO["dump_info"]["mapper_type"] = None
2457
+ self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
2458
+
2459
+ buffer_len = 0x10000
2460
+ size = 32 * 1024 * 1024
2461
+ if "agb_rom_size" in args: size = args["agb_rom_size"]
2462
+ self.INFO["dump_info"]["rom_size"] = size
2463
+ if is_3dmemory:
2464
+ self.INFO["dump_info"]["agb_read_method"] = "3D Memory"
2465
+
2466
+ elif flashcart and "command_set" in cart_type and cart_type["command_set"] == "GBAMP":
2467
+ # if not self.CanPowerCycleCart():
2468
+ # 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})
2469
+ # return False
2470
+ self.INFO["dump_info"]["agb_read_method"] = "GBA Movie Player"
2471
+ if not "verify_write" in args: self.CartPowerCycleOrAskReconnect()
2472
+ flashcart.Unlock()
2473
+ buffer_len = 0x4000
2474
+
2475
+ # Initialize Vast Fame ROM (if it was not autodetected) and determine SRAM address/value bit reordering (necessary for emulation)
2476
+ elif flashcart and "command_set" in cart_type and cart_type["command_set"] == "VASTFAME":
2477
+ addr_reorder = [[-1 for i in range(16)] for j in range(4)]
2478
+ value_reorder = [[-1 for i in range(8)] for j in range(4)]
2479
+ for mode in range(0, 16):
2480
+ # Set SRAM mode
2481
+ self._cart_write(0xFFF8, 0x99, sram=True)
2482
+ self._cart_write(0xFFF9, 0x02, sram=True)
2483
+ self._cart_write(0xFFFA, 0x05, sram=True)
2484
+ self._cart_write(0xFFFB, 0x02, sram=True)
2485
+ self._cart_write(0xFFFC, 0x03, sram=True)
2486
+
2487
+ self._cart_write(0xFFFD, 0x00, sram=True)
2488
+ self._cart_write(0xFFFE, mode, sram=True)
2489
+
2490
+ self._cart_write(0xFFF8, 0x99, sram=True)
2491
+ self._cart_write(0xFFF9, 0x03, sram=True)
2492
+ self._cart_write(0xFFFA, 0x62, sram=True)
2493
+ self._cart_write(0xFFFB, 0x02, sram=True)
2494
+ self._cart_write(0xFFFC, 0x56, sram=True)
2495
+
2496
+ # Blank SRAM
2497
+ for i in range(0, 16):
2498
+ self._cart_write(1 << i, 0, sram=True)
2499
+ self._cart_write(0x8000, 0, sram=True)
2500
+
2501
+ # Get address reordering for SRAM writes (repeats every 4 modes so only check first 4)
2502
+ if mode < 4:
2503
+ for i in range(0, 16):
2504
+ self._cart_write(1 << i, 0xAA, sram=True)
2505
+ for j in range(0, 16):
2506
+ value = self._cart_read(1 << j, 1, True)[0]
2507
+ if value != 0:
2508
+ addr_reorder[mode][j] = i
2509
+ break
2510
+ self._cart_write(1 << i, 0, sram=True) # reblank SRAM
2511
+ addr_reorder[mode].reverse()
2512
+
2513
+ # Get value reordering for SRAM writes/reads (assumes upper bit of address is never reordered)
2514
+ if mode % 4 == 0:
2515
+ for i in range(0, 8):
2516
+ self._cart_write(0x8000, 1 << i, sram=True)
2517
+ value = self._cart_read(0x8000, 1, True)[0]
2518
+ for j in range(0, 8):
2519
+ if (1 << j) == value:
2520
+ value_reorder[mode // 4][j] = i
2521
+ break
2522
+ value_reorder[mode // 4].reverse()
2523
+
2524
+ self.INFO["dump_info"]["vf_addr_reorder"] = addr_reorder
2525
+ self.INFO["dump_info"]["vf_value_reorder"] = value_reorder
2526
+
2527
+ else:
2528
+ self.INFO["dump_info"]["agb_read_method"] = self.AGB_READ_METHODS[self.AGB_READ_METHOD]
2529
+
2530
+ if flashcart and "flash_bank_size" in cart_type:
2531
+ if "verify_write" in args:
2532
+ rom_banks = math.ceil(len(args["verify_write"]) / cart_type["flash_bank_size"])
2533
+ else:
2534
+ rom_banks = math.ceil(size / cart_type["flash_bank_size"])
2535
+ rom_bank_size = cart_type["flash_bank_size"]
2536
+ else:
2537
+ rom_banks = 1
2538
+ rom_bank_size = 0x2000000
2539
+
2540
+ #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:
2541
+ # self.SetAGBReadMethod(0)
2542
+
2543
+ if self.ERROR: return
2544
+
2545
+ if "verify_write" in args:
2546
+ size = len(args["verify_write"])
2547
+ buffer_len = min(buffer_len, size)
2548
+ else:
2549
+ if "bl_offset" in args:
2550
+ method = "SAVE_READ"
2551
+ else:
2552
+ method = "ROM_READ"
2553
+ pos = 0
2554
+ self.SetProgress({"action":"INITIALIZE", "method":method, "size":size})
2555
+ self.INFO["action"] = self.ACTIONS[method]
2556
+ if self.FW["fw_ver"] >= 8:
2557
+ if flashcart and "enable_pullups" in cart_type:
2558
+ self._write(self.DEVICE_CMD["ENABLE_PULLUPS"], wait=True)
2559
+ self.SetAGBReadMethod(0)
2560
+ dprint("Pullups enabled")
2561
+ if (self.FW["pcb_name"] == "GBFlash" and self.FW["pcb_ver"] < 13) or (self.FW["pcb_name"] == "Joey Jr"):
2562
+ print("{:s}Note: This cartridge may not be fully compatible with your {:s} device.{:s}".format(ANSI.YELLOW, self.FW["pcb_name"], ANSI.RESET))
2563
+ else:
2564
+ self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
2565
+ dprint("Pullups disabled")
2566
+ if self.FW["fw_ver"] >= 12:
2567
+ if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
2568
+ self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
2569
+
2570
+ buffer = bytearray(size)
2571
+ max_length = self.MAX_BUFFER_READ
2572
+ dprint("Max buffer size: 0x{:X}".format(max_length))
2573
+ if is_3dmemory:
2574
+ max_length = min(max_length, 0x1000)
2575
+ else:
2576
+ max_length = min(max_length, 0x2000)
2577
+ self.INFO["dump_info"]["transfer_size"] = max_length
2578
+ pos_total = 0
2579
+ start_address = 0
2580
+ end_address = size
2581
+ # dprint("ROM banks:", rom_banks)
2582
+
2583
+ start_bank = 0
2584
+ if "verify_write" in args:
2585
+ buffer_pos = args["verify_from"]
2586
+ start_address = buffer_pos
2587
+ end_address = args["verify_from"] + args["verify_len"]
2588
+ start_bank = math.floor(buffer_pos / rom_bank_size)
2589
+ end_bank = math.ceil((buffer_pos + args["verify_len"]) / rom_bank_size)
2590
+ rom_banks = end_bank
2591
+ elif "bl_offset" in args:
2592
+ buffer_pos = args["bl_offset"]
2593
+ start_address = buffer_pos
2594
+ end_address = args["bl_offset"] + args["bl_size"]
2595
+ start_bank = math.floor(buffer_pos / rom_bank_size)
2596
+ end_bank = math.ceil((buffer_pos + args["bl_size"]) / rom_bank_size)
2597
+ rom_banks = end_bank
2598
+
2599
+ dprint("start_address=0x{:X}, end_address=0x{:X}, start_bank=0x{:X}, rom_banks=0x{:X}, buffer_len=0x{:X}, max_length=0x{:X}".format(start_address, end_address, start_bank, rom_banks, buffer_len, max_length))
2600
+ bank = start_bank
2601
+ while bank < rom_banks:
2602
+ # ↓↓↓ Switch ROM bank
2603
+ if self.MODE == "DMG":
2604
+ if _mbc.ResetBeforeBankChange(bank) is True:
2605
+ dprint("Resetting the MBC")
2606
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
2607
+ (start_address, bank_size) = _mbc.SelectBankROM(bank)
2608
+ end_address = start_address + bank_size
2609
+ buffer_len = min(buffer_len, _mbc.GetROMBankSize())
2610
+ if "verify_write" in args:
2611
+ buffer_len = min(buffer_len, bank_size, len(args["verify_write"]))
2612
+ end_address = start_address + bank_size
2613
+ start_address += (buffer_pos % rom_bank_size)
2614
+ if end_address > start_address + args["verify_len"]:
2615
+ end_address = start_address + args["verify_len"]
2616
+ elif self.MODE == "AGB":
2617
+ if "verify_write" in args:
2618
+ buffer_len = min(buffer_len, len(args["verify_write"]))
2619
+ if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
2620
+ flashcart.SelectBankROM(bank)
2621
+ temp = end_address - start_address
2622
+ start_address %= cart_type["flash_bank_size"]
2623
+ end_address = min(cart_type["flash_bank_size"], start_address + temp)
2624
+ # ↑↑↑ Switch ROM bank
2625
+
2626
+ skip_init = False
2627
+ pos = start_address
2628
+ lives = 20
2629
+
2630
+ while pos < end_address:
2631
+ temp = bytearray()
2632
+ if self.CANCEL:
2633
+ cancel_args = {"action":"ABORT", "abortable":False}
2634
+ cancel_args.update(self.CANCEL_ARGS)
2635
+ self.CANCEL_ARGS = {}
2636
+ self.ERROR_ARGS = {}
2637
+ self.SetProgress(cancel_args)
2638
+ try:
2639
+ if file is not None: file.close()
2640
+ except:
2641
+ pass
2642
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
2643
+ return
2644
+
2645
+ if is_3dmemory:
2646
+ temp = self.ReadROM_3DMemory(address=pos, length=buffer_len, max_length=max_length)
2647
+ elif flashcart and cart_type["command_set"] == "GBAMP":
2648
+ temp = self.ReadROM_GBAMP(address=pos, length=buffer_len, max_length=max_length)
2649
+ else:
2650
+ if self.FW["fw_ver"] >= 10 and "verify_write" in args:
2651
+ if self.MODE == "AGB": self.SetAGBReadMethod(0)
2652
+ if self.MODE == "DMG": self.SetDMGReadMethod(2)
2653
+ temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
2654
+ if self.MODE == "AGB": self.SetAGBReadMethod(agb_read_method)
2655
+ if self.MODE == "DMG": self.SetDMGReadMethod(dmg_read_method)
2656
+ else:
2657
+ # Normal read
2658
+ temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
2659
+ skip_init = True
2660
+
2661
+ if len(temp) != buffer_len:
2662
+ pos_temp = pos_total
2663
+ if "verify_write" in args:
2664
+ pos_temp += args["verify_base_pos"]
2665
+ self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
2666
+ else:
2667
+ self.SetProgress({"action":"UPDATE_POS", "pos":pos_total})
2668
+
2669
+ err_text = "{:s}Note: Incomplete transfer detected. Resuming from 0x{:X}...{:s}".format(ANSI.YELLOW, pos_temp, ANSI.RESET)
2670
+ if (max_length >> 1) < 64:
2671
+ dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}.".format(buffer_len, pos_temp))
2672
+ max_length = 64
2673
+ elif lives >= 20 and pos_temp != 0:
2674
+ dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}.".format(buffer_len, pos_temp))
2675
+ else:
2676
+ dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}. Decreasing maximum transfer buffer size to 0x{:X}.".format(buffer_len, pos_temp, max_length >> 1))
2677
+ max_length >>= 1
2678
+ self.MAX_BUFFER_READ = max_length
2679
+ err_text += "\nBuffer size adjusted to {:d} bytes.".format(max_length)
2680
+ if ".dev" in Util.VERSION_PEP440 and not Util.DEBUG: print(err_text)
2681
+
2682
+ self.INFO["dump_info"]["transfer_size"] = max_length
2683
+ skip_init = False
2684
+ self.DEVICE.reset_input_buffer()
2685
+ self.DEVICE.reset_output_buffer()
2686
+ lives -= 1
2687
+ if lives == 0:
2688
+ self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while reading from the cartridge. When connecting the device, avoid passive USB hubs and try different USB ports/cables."})
2689
+ self.CANCEL = True
2690
+ self.ERROR = True
2691
+ if "verify_write" in args: return False
2692
+ continue
2693
+ elif lives < 20:
2694
+ lives = 20
2695
+
2696
+ if file is not None: file.write(temp)
2697
+ buffer[pos_total:pos_total+len(temp)] = temp
2698
+ pos_total += len(temp)
2699
+
2700
+ if "verify_write" in args:
2701
+ #if pos_total >= len(args["verify_write"]): break
2702
+ check = args["verify_write"][pos_total-len(temp):pos_total]
2703
+ if temp[:len(check)] != check:
2704
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
2705
+ dprint("Writing 0x{:X} bytes to debug_verify_0x{:X}.bin".format(len(temp), pos_total-len(temp)))
2706
+ with open("debug_verify_0x{:X}.bin".format(pos_total-len(temp)), "ab") as f: f.write(temp)
2707
+
2708
+ for i in range(0, pos_total):
2709
+ if (i < len(args["verify_write"]) - 1) and (i < pos_total - 1) and args["verify_write"][i] != buffer[i]:
2710
+ if args["rtc_area"] is True and i in (0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9):
2711
+ dprint("Skipping RTC area at 0x{:X}".format(i))
2712
+ else:
2713
+ dprint("Mismatch during verification at 0x{:X}".format(i))
2714
+ return i
2715
+ else:
2716
+ dprint("Verification successful between 0x{:X} and 0x{:X}".format(pos_total-len(temp), pos_total))
2717
+ self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
2718
+ else:
2719
+ self.SetProgress({"action":"UPDATE_POS", "pos":pos_total})
2720
+
2721
+ pos += buffer_len
2722
+
2723
+ bank += 1
2724
+
2725
+ if "verify_write" in args:
2726
+ return min(pos_total, len(args["verify_write"]))
2727
+
2728
+ if not "bl_offset" in args:
2729
+ # Hidden sector (GB-Memory)
2730
+ if self.MODE == "DMG" and len(args["path"]) > 0 and _mbc.HasHiddenSector():
2731
+ file = open(os.path.splitext(args["path"])[0] + ".map", "wb")
2732
+ temp = _mbc.ReadHiddenSector()
2733
+ self.INFO["hidden_sector"] = temp
2734
+ self.INFO["dump_info"]["gbmem"] = temp
2735
+ self.INFO["dump_info"]["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=temp, buffer_rom=buffer)
2736
+ file.write(temp)
2737
+ file.close()
2738
+ gbmp = self.INFO["dump_info"]["gbmem_parsed"]
2739
+ if (isinstance(gbmp, list)) and len(args["path"]) > 2:
2740
+ for i in range(1, len(gbmp)):
2741
+ if gbmp[i]["header"] == {} or gbmp[i]["header"]["logo_correct"] is False: continue
2742
+ settings = None
2743
+ if "settings" in args: settings = args["settings"]
2744
+ gbmp_n = Util.GenerateFileName(mode="DMG", header=gbmp[i]["header"], settings=settings)
2745
+ gbmp_p = "{:s} - {:s}".format(os.path.splitext(args["path"])[0], gbmp_n)
2746
+ with open(gbmp_p, "wb") as f:
2747
+ f.write(buffer[gbmp[i]["rom_offset"]:gbmp[i]["rom_offset"]+gbmp[i]["rom_size"]])
2748
+ else:
2749
+ if "hidden_sector" in self.INFO: del(self.INFO["hidden_sector"])
2750
+ if "gbmem" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem"])
2751
+ if "gbmem_parsed" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem_parsed"])
2752
+
2753
+ # Calculate Global Checksum
2754
+ if self.MODE == "DMG":
2755
+ if _mbc.GetName() == "MMM01":
2756
+ self.INFO["dump_info"]["header"].update(RomFileDMG(buffer[-0x8000:-0x8000+0x180]).GetHeader(unchanged=True))
2757
+ elif _mbc.GetName() == "Sachen":
2758
+ pass
2759
+ #chk = _mbc.CalcChecksum(buffer)
2760
+ self.INFO["rom_checksum_calc"] = _mbc.CalcChecksum(buffer)
2761
+ elif self.MODE == "AGB":
2762
+ self.INFO["dump_info"]["header"].update(RomFileAGB(buffer[:0x180]).GetHeader())
2763
+
2764
+ temp_ver = "N/A"
2765
+ try:
2766
+ ids = [ b"SRAM_", b"EEPROM_V", b"FLASH_V", b"FLASH512_V", b"FLASH1M_V", b"AGB_8MDACS_DL_V" ]
2767
+ for id in ids:
2768
+ temp_pos = buffer.find(id)
2769
+ if temp_pos > 0:
2770
+ temp_ver = buffer[temp_pos:temp_pos+0x20]
2771
+ temp_ver = temp_ver[:temp_ver.index(0x00)].decode("ascii", "replace")
2772
+ break
2773
+ except ValueError:
2774
+ temp_ver = "N/A"
2775
+ self.INFO["dump_info"]["agb_savelib"] = temp_ver
2776
+ self.INFO["dump_info"]["agb_save_flash_id"] = None
2777
+ if "FLASH" in temp_ver:
2778
+ try:
2779
+ agb_save_flash_id = self.ReadFlashSaveID()
2780
+ if agb_save_flash_id is not False and len(agb_save_flash_id) == 2:
2781
+ self.INFO["dump_info"]["agb_save_flash_id"] = agb_save_flash_id
2782
+ except:
2783
+ print("Error querying the flash save chip.")
2784
+ self.DEVICE.reset_input_buffer()
2785
+ self.DEVICE.reset_output_buffer()
2786
+
2787
+ if "eeprom_data" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["eeprom_data"])
2788
+ if "EEPROM" in temp_ver and len(buffer) == 0x2000000:
2789
+ padding_byte = buffer[0x1FFFEFF]
2790
+ dprint("Replacing unmapped ROM data of cartridge (32 MiB ROM + EEPROM save type) with the original padding byte of 0x{:02X}.".format(padding_byte))
2791
+ self.INFO["dump_info"]["eeprom_data"] = buffer[0x1FFFF00:0x1FFFF10]
2792
+ buffer[0x1FFFF00:0x2000000] = bytearray([padding_byte] * 0x100)
2793
+ file.seek(0x1FFFF00)
2794
+ file.write(buffer[0x1FFFF00:0x2000000])
2795
+
2796
+ self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
2797
+ self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
2798
+ self.INFO["file_sha256"] = hashlib.sha256(buffer).hexdigest()
2799
+ self.INFO["file_md5"] = hashlib.md5(buffer).hexdigest()
2800
+ self.INFO["dump_info"]["hash_crc32"] = self.INFO["file_crc32"]
2801
+ self.INFO["dump_info"]["hash_sha1"] = self.INFO["file_sha1"]
2802
+ self.INFO["dump_info"]["hash_sha256"] = self.INFO["file_sha256"]
2803
+ self.INFO["dump_info"]["hash_md5"] = self.INFO["file_md5"]
2804
+
2805
+ # Check for ROM loops
2806
+ self.INFO["loop_detected"] = False
2807
+ temp = min(0x2000000, len(buffer))
2808
+ while temp > 0x4000:
2809
+ temp = temp >> 1
2810
+ if (buffer[0:0x4000] == buffer[temp:temp+0x4000]):
2811
+ if buffer[0:temp] == buffer[temp:temp*2]:
2812
+ self.INFO["loop_detected"] = temp
2813
+ else:
2814
+ break
2815
+
2816
+ if file is not None: file.close()
2817
+
2818
+ # ↓↓↓ Switch to first ROM bank
2819
+ if self.MODE == "DMG":
2820
+ if _mbc.ResetBeforeBankChange(0) is True:
2821
+ dprint("Resetting the MBC")
2822
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
2823
+ _mbc.SelectBankROM(0)
2824
+ self.SetDMGReadMethod(dmg_read_method)
2825
+ elif self.MODE == "AGB":
2826
+ if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
2827
+ flashcart.SelectBankROM(0)
2828
+ self.SetAGBReadMethod(agb_read_method)
2829
+ # ↑↑↑ Switch to first ROM bank
2830
+
2831
+ # Clean up
2832
+ self.INFO["last_action"] = self.INFO["action"]
2833
+ self.INFO["action"] = None
2834
+ self.INFO["last_path"] = args["path"]
2835
+ self.SetProgress({"action":"FINISHED"})
2836
+ return True
2837
+
2838
+ def WriteRTC(self, args):
2839
+ if self.CanPowerCycleCart():
2840
+ self.CartPowerOn()
2841
+
2842
+ if self.MODE == "DMG":
2843
+ _mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
2844
+ if not _mbc.HasRTC(): return False
2845
+ ret = _mbc.WriteRTCDict(args["rtc_dict"])
2846
+ elif self.MODE == "AGB":
2847
+ _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)
2848
+ if _agb_gpio.HasRTC() is not True: return False
2849
+ ret = _agb_gpio.WriteRTCDict(args["rtc_dict"])
2850
+ return ret
2851
+
2852
+ def _BackupRestoreRAM(self, args):
2853
+ self.FAST_READ = False
2854
+ if "rtc" not in args: args["rtc"] = False
2855
+
2856
+ # Prepare some stuff
2857
+ command = None
2858
+ empty_data_byte = 0x00
2859
+ extra_size = 0
2860
+ audio_low = False
2861
+
2862
+ cart_type = None
2863
+ if "cart_type" in args and args["cart_type"] >= 0:
2864
+ supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
2865
+ cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
2866
+ if self.FW["fw_ver"] >= 12:
2867
+ if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
2868
+ self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
2869
+
2870
+ self._set_fw_variable("STATUS_REGISTER_MASK", 0x80)
2871
+ self._set_fw_variable("STATUS_REGISTER_VALUE", 0x80)
2872
+
2873
+ if self.MODE == "DMG":
2874
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True) # fixes Taobao FRAM cart save data
2875
+ _mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
2876
+ if not self.IsSupportedMbc(args["mbc"]):
2877
+ msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device.".format(Util.APPNAME, self.GetFullName())
2878
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
2879
+ return False
2880
+ if "save_size" in args:
2881
+ save_size = args["save_size"]
2882
+ else:
2883
+ save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(args["save_type"])]
2884
+ ram_banks = _mbc.GetRAMBanks(save_size)
2885
+ buffer_len = min(0x200, _mbc.GetRAMBankSize())
2886
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
2887
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
2888
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
2889
+
2890
+ # Enable mappers
2891
+ if _mbc.GetName() == "TAMA5":
2892
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
2893
+ self._set_fw_variable("DMG_READ_CS_PULSE", 1)
2894
+ _mbc.EnableMapper()
2895
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
2896
+ buffer_len = 0x20
2897
+ elif _mbc.GetName() == "MBC7":
2898
+ buffer_len = save_size
2899
+ elif _mbc.GetName() == "MBC6":
2900
+ empty_data_byte = 0xFF
2901
+ audio_low = True
2902
+ self._set_fw_variable("FLASH_METHOD", 0x04) # FLASH_METHOD_DMG_MBC6
2903
+ self._set_fw_variable("FLASH_WE_PIN", 0x01) # WR
2904
+ _mbc.EnableFlash(enable=True, enable_write=True if (args["mode"] == 3) else False)
2905
+ elif _mbc.GetName() == "Xploder GB":
2906
+ empty_data_byte = 0xFF
2907
+ self._set_fw_variable("FLASH_PULSE_RESET", 0)
2908
+ self._set_fw_variable("FLASH_DOUBLE_DIE", 0)
2909
+ self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
2910
+ self._write(0x00) # FLASH_COMMAND_SET_NONE
2911
+ self._write(0x01) # FLASH_METHOD_UNBUFFERED
2912
+ self._write(0x01) # FLASH_WE_PIN_WR
2913
+ commands = [
2914
+ [ 0x5555, 0xAA ],
2915
+ [ 0x2AAA, 0x55 ],
2916
+ [ 0x5555, 0xA0 ],
2917
+ ]
2918
+ for i in range(0, 6):
2919
+ if i >= len(commands):
2920
+ self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
2921
+ else:
2922
+ self._write(bytearray(struct.pack(">I", commands[i][0])) + bytearray(struct.pack(">H", commands[i][1])))
2923
+ if self.FW["fw_ver"] >= 12: self.wait_for_ack()
2924
+ self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
2925
+ self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
2926
+ self._write(1) # number of commands
2927
+ self._write(bytearray(struct.pack(">I", 0x0006))) # address/value
2928
+ self._write(0) # type = address
2929
+ ret = self._read(1)
2930
+ if ret != 0x01:
2931
+ print("Error in DMG_SET_BANK_CHANGE_CMD:", ret)
2932
+ else:
2933
+ _mbc.EnableMapper()
2934
+
2935
+ if args["rtc"] is True:
2936
+ extra_size = _mbc.GetRTCBufferSize()
2937
+
2938
+ # Check for DMG-MBC5-32M-FLASH
2939
+ self._cart_write(0x2000, 0x00)
2940
+ self._cart_write(0x4000, 0x90)
2941
+ flash_id = self._cart_read(0x4000, 2)
2942
+ if flash_id == bytearray([0xB0, 0x88]): audio_low = True
2943
+ self._cart_write(0x4000, 0xF0)
2944
+ self._cart_write(0x4000, 0xFF)
2945
+ self._cart_write(0x2000, 0x01)
2946
+ if audio_low:
2947
+ dprint("DMG-MBC5-32M-FLASH Development Cartridge detected")
2948
+ self._set_fw_variable("FLASH_WE_PIN", 0x01)
2949
+ self.SetPin(["PIN_AUDIO"], False)
2950
+ self._cart_write(0x4000, 0x00)
2951
+
2952
+ _mbc.EnableRAM(enable=True)
2953
+
2954
+ elif self.MODE == "AGB":
2955
+ self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
2956
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
2957
+ buffer_len = 0x2000
2958
+ if "save_size" in args:
2959
+ save_size = args["save_size"]
2960
+ else:
2961
+ save_size = Util.AGB_Header_Save_Sizes[args["save_type"]]
2962
+ ram_banks = math.ceil(save_size / 0x10000)
2963
+ agb_flash_chip = 0
2964
+
2965
+ if args["save_type"] in (1, 2): # EEPROM
2966
+ if args["mode"] == 3:
2967
+ buffer_len = 0x40
2968
+ else:
2969
+ buffer_len = 0x100
2970
+ elif args["save_type"] in (4, 5): # FLASH
2971
+ empty_data_byte = 0xFF
2972
+ ret = self.ReadFlashSaveID()
2973
+ if ret is False:
2974
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldn’t detect the save data flash chip type.", "abortable":False})
2975
+ return False
2976
+ buffer_len = 0x1000
2977
+ (agb_flash_chip, _) = ret
2978
+ if agb_flash_chip in (0xBF5B, 0xFFFF): # Bootlegs
2979
+ buffer_len = 0x800
2980
+ elif args["save_type"] == 6: # DACS
2981
+ # self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=self.FW["fw_ver"] >= 12)
2982
+ empty_data_byte = 0xFF
2983
+ # Read Chip ID
2984
+ ram_banks = 1
2985
+ self._cart_write(0, 0x90)
2986
+ flash_id = self._cart_read(0, 4)
2987
+ self._cart_write(0, 0x50)
2988
+ self._cart_write(0, 0xFF)
2989
+ if flash_id != bytearray([ 0xB0, 0x00, 0x9F, 0x00 ]):
2990
+ dprint("Warning: Unknown DACS flash chip ID ({:s})".format(' '.join(format(x, '02X') for x in flash_id)))
2991
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldn’t detect the DACS flash chip.\nUnknown Flash ID: {:s}".format(' '.join(format(x, '02X') for x in flash_id)), "abortable":False})
2992
+ return False
2993
+ buffer_len = 0x2000
2994
+
2995
+ # ↓↓↓ Load commands into firmware
2996
+ flash_cmds = [
2997
+ [ "PA", 0x70 ],
2998
+ [ "PA", 0x10 ],
2999
+ [ "PA", "PD" ]
3000
+ ]
3001
+ self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 1)
3002
+ self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
3003
+ self._write(0x02) # FLASH_COMMAND_SET_INTEL
3004
+ self._write(0x01) # FLASH_METHOD_UNBUFFERED
3005
+ self._write(0x00) # unset
3006
+ for i in range(0, 6):
3007
+ if i > len(flash_cmds) - 1: # skip
3008
+ self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
3009
+ else:
3010
+ address = flash_cmds[i][0]
3011
+ value = flash_cmds[i][1]
3012
+ if not isinstance(address, int): address = 0
3013
+ if not isinstance(value, int): value = 0
3014
+ address >>= 1
3015
+ dprint("Setting command #{:d} to 0x{:X}=0x{:X}".format(i, address, value))
3016
+ self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value)))
3017
+ if self.FW["fw_ver"] >= 12: self.wait_for_ack()
3018
+ # ↑↑↑ Load commands into firmware
3019
+
3020
+ # Bootleg mapper
3021
+ if cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
3022
+ sram_5 = struct.unpack("B", bytes(self._cart_read(address=5, length=1, agb_save_flash=True)))[0]
3023
+ self._cart_write(address=5, value=1, sram=True)
3024
+
3025
+ commands = [ # save type commands
3026
+ [ [None], [None] ], # No save
3027
+ [ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 1]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 1]) ], # 4K EEPROM
3028
+ [ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 2]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 2]) ], # 64K EEPROM
3029
+ [ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 256K SRAM
3030
+ [ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 2 if agb_flash_chip == 0x1F3D else 1]) ], # 512K FLASH
3031
+ [ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 1]) ], # 1M FLASH
3032
+ [ False, False ], # 8M DACS
3033
+ [ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 512K SRAM
3034
+ [ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 1M SRAM
3035
+ ]
3036
+ command = commands[args["save_type"]][args["mode"] - 2]
3037
+
3038
+ if args["rtc"] is True:
3039
+ extra_size = 0x10
3040
+
3041
+ if args["mode"] == 2: # Backup
3042
+ action = "SAVE_READ"
3043
+ buffer = bytearray()
3044
+ elif args["mode"] == 3: # Restore
3045
+ action = "SAVE_WRITE"
3046
+ self.INFO["save_erase"] = args["erase"]
3047
+ if args["erase"] == True:
3048
+ buffer = bytearray([ empty_data_byte ] * save_size)
3049
+ if self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
3050
+ buffer[0] = 0x00
3051
+ else:
3052
+ if args["path"] is None:
3053
+ if "buffer" in args:
3054
+ buffer = args["buffer"]
3055
+ else:
3056
+ buffer = self.INFO["data"]
3057
+ else:
3058
+ with open(args["path"], "rb") as f:
3059
+ buffer = bytearray(f.read())
3060
+
3061
+ # Fill too small file
3062
+ if not (self.MODE == "AGB" and args["save_type"] == 6): # Not DACS
3063
+ if args["mode"] == 3:
3064
+ while len(buffer) < save_size:
3065
+ buffer += bytearray(buffer)
3066
+
3067
+ #if self.MODE == "AGB" and "ereader" in self.INFO and self.INFO["ereader"] is True: # e-Reader
3068
+ # buffer[0xFF80:0x10000] = bytearray([0] * 0x80)
3069
+ # buffer[0x1FF80:0x20000] = bytearray([0] * 0x80)
3070
+
3071
+ # Main loop
3072
+ if not (args["mode"] == 2 and "verify_write" in args and args["verify_write"]):
3073
+ self.INFO["action"] = self.ACTIONS[action]
3074
+ self.SetProgress({"action":"INITIALIZE", "method":action, "size":save_size+extra_size})
3075
+
3076
+ buffer_offset = 0
3077
+ for bank in range(0, ram_banks):
3078
+ if self.MODE == "DMG":
3079
+ if _mbc.GetName() == "MBC6" and bank > 7:
3080
+ self._set_fw_variable("DMG_ROM_BANK", bank - 8)
3081
+ (start_address, bank_size) = _mbc.SelectBankFlash(bank - 8)
3082
+ end_address = start_address + bank_size
3083
+ buffer_len = 0x2000
3084
+ if args["mode"] == 3: # Restore
3085
+ if ((buffer_offset - 0x8000) % 0x20000) == 0:
3086
+ dprint("Erasing flash sector at position 0x{:X}".format(buffer_offset))
3087
+ _mbc.EraseFlashSector()
3088
+ elif _mbc.GetName() == "Xploder GB":
3089
+ self._set_fw_variable("DMG_ROM_BANK", bank + 8)
3090
+ (start_address, bank_size) = _mbc.SelectBankRAM(bank)
3091
+ end_address = min(save_size, start_address + bank_size)
3092
+ else:
3093
+ self._set_fw_variable("DMG_WRITE_CS_PULSE", 1 if _mbc.WriteWithCSPulse() else 0)
3094
+ (start_address, bank_size) = _mbc.SelectBankRAM(bank)
3095
+ end_address = min(save_size, start_address + bank_size)
3096
+ elif self.MODE == "AGB":
3097
+ start_address = 0
3098
+ bank_size = 0x10000
3099
+ if args["save_type"] == 6: # DACS
3100
+ bank_size = min(save_size, 0x100000)
3101
+ buffer_len = 0x2000
3102
+
3103
+ end_address = min(save_size, bank_size)
3104
+
3105
+ if save_size > bank_size:
3106
+ if args["save_type"] == 8 or agb_flash_chip in (0xBF5B, 0xFFFF): # Bootleg 1M
3107
+ dprint("Switching to bootleg save bank {:d}".format(bank))
3108
+ self._cart_write(0x1000000, bank)
3109
+ elif args["save_type"] == 5: # FLASH 1M
3110
+ dprint("Switching to FLASH bank {:d}".format(bank))
3111
+ cmds = [
3112
+ [ 0x5555, 0xAA ],
3113
+ [ 0x2AAA, 0x55 ],
3114
+ [ 0x5555, 0xB0 ],
3115
+ [ 0, bank ]
3116
+ ]
3117
+ self._cart_write_flash(cmds)
3118
+ else:
3119
+ dprint("Unknown bank switching method")
3120
+ time.sleep(0.05)
3121
+
3122
+ # if "detect" in args and args["detect"] is True:
3123
+ # start_address = end_address - 64
3124
+ # buffer_len = 64
3125
+
3126
+ max_length = 64
3127
+ 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))
3128
+ pos = start_address
3129
+ while pos < end_address:
3130
+ if self.CANCEL:
3131
+ cancel_args = {"action":"ABORT", "abortable":False}
3132
+ cancel_args.update(self.CANCEL_ARGS)
3133
+ self.CANCEL_ARGS = {}
3134
+ self.ERROR_ARGS = {}
3135
+ self.SetProgress(cancel_args)
3136
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
3137
+ return
3138
+
3139
+ if args["mode"] == 2: # Backup
3140
+ in_temp = [None] * 2
3141
+ if "verify_read" in args and args["verify_read"]: # Read twice for detecting instabilities
3142
+ xe = 2
3143
+ else:
3144
+ xe = 1
3145
+ for x in range(0, xe):
3146
+ if x == 1:
3147
+ self.NO_PROG_UPDATE = True
3148
+ else:
3149
+ self.NO_PROG_UPDATE = False
3150
+
3151
+ if self.MODE == "DMG" and _mbc.GetName() == "MBC7":
3152
+ in_temp[x] = self.ReadRAM_MBC7(address=pos, length=buffer_len)
3153
+ elif self.MODE == "DMG" and _mbc.GetName() == "MBC6" and bank > 7: # MBC6 flash save memory
3154
+ in_temp[x] = self.ReadROM(address=pos, length=buffer_len, skip_init=False, max_length=max_length)
3155
+ elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
3156
+ in_temp[x] = self.ReadRAM_TAMA5()
3157
+ elif self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
3158
+ in_temp[x] = self.ReadROM(address=0x20000+pos, length=buffer_len, skip_init=False, max_length=max_length)
3159
+ elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
3160
+ in_temp[x] = self.ReadRAM(address=int(pos/8), length=buffer_len, command=command, max_length=max_length)
3161
+ elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
3162
+ in_temp[x] = self.ReadROM(address=0x1F00000+pos, length=buffer_len, skip_init=False, max_length=max_length)
3163
+ elif self.MODE == "DMG" and _mbc.GetName() == "MBC2":
3164
+ in_temp[x] = self.ReadRAM(address=pos, length=buffer_len, command=command, max_length=max_length)
3165
+ for i in range(0, len(in_temp[x])):
3166
+ in_temp[x][i] = in_temp[x][i] & 0x0F
3167
+ else:
3168
+ in_temp[x] = self.ReadRAM(address=pos, length=buffer_len, command=command, max_length=max_length)
3169
+
3170
+ if len(in_temp[x]) != buffer_len:
3171
+ if (max_length >> 1) < 64:
3172
+ dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}!".format(len(in_temp[x]), buffer_len, len(buffer)))
3173
+ max_length = 64
3174
+ else:
3175
+ dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}! Decreasing maximum transfer buffer length to 0x{:X}.".format(len(in_temp[x]), buffer_len, len(buffer), max_length >> 1))
3176
+ max_length >>= 1
3177
+ self.DEVICE.reset_input_buffer()
3178
+ self.DEVICE.reset_output_buffer()
3179
+ continue
3180
+
3181
+ if xe == 2 and in_temp[0] != in_temp[1]:
3182
+ # print(in_temp[0], in_temp[1], sep="\n")
3183
+ 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})
3184
+ return False
3185
+
3186
+ temp = in_temp[0]
3187
+ buffer += temp
3188
+ self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)})
3189
+
3190
+ elif args["mode"] == 3: # Restore
3191
+ if self.MODE == "DMG" and _mbc.GetName() == "MBC7":
3192
+ self.WriteEEPROM_MBC7(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len])
3193
+ elif self.MODE == "DMG" and _mbc.GetName() == "MBC6" and bank > 7: # MBC6 flash save memory
3194
+ if self.FW["fw_ver"] > 1:
3195
+ self.WriteROM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len])
3196
+ self._cart_write(pos + buffer_len - 1, 0xF0)
3197
+ else:
3198
+ self.WriteFlash_MBC6(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], mapper=_mbc)
3199
+ elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
3200
+ self.WriteRAM_TAMA5(buffer=buffer[buffer_offset:buffer_offset+buffer_len])
3201
+ elif self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
3202
+ self.WriteROM_DMG_EEPROM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], bank=bank+8)
3203
+ elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
3204
+ self.WriteRAM(address=int(pos/8), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
3205
+ elif self.MODE == "AGB" and args["save_type"] in (4, 5): # FLASH
3206
+ sector_address = pos % 0x10000
3207
+ if agb_flash_chip == 0x1F3D: # Atmel AT29LV512
3208
+ self.WriteRAM(address=int(pos/128), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
3209
+ else:
3210
+ dprint("Erasing flash save sector; pos=0x{:X}, sector_address=0x{:X}".format(pos, sector_address))
3211
+ cmds = [
3212
+ [ 0x5555, 0xAA ],
3213
+ [ 0x2AAA, 0x55 ],
3214
+ [ 0x5555, 0x80 ],
3215
+ [ 0x5555, 0xAA ],
3216
+ [ 0x2AAA, 0x55 ],
3217
+ [ sector_address, 0x30 ]
3218
+ ]
3219
+ self._cart_write_flash(cmds)
3220
+ sr = 0
3221
+ lives = 50
3222
+ while True:
3223
+ time.sleep(0.01)
3224
+ sr = self._cart_read(sector_address, 2, agb_save_flash=True)
3225
+ try:
3226
+ sr = struct.unpack(">H", sr)[0]
3227
+ except:
3228
+ sr = str(sr)
3229
+ dprint("Data Check: 0x{:X} == 0xFFFF? {:s}".format(sr, str(sr == 0xFFFF)))
3230
+ if sr == 0xFFFF: break
3231
+ lives -= 1
3232
+ if lives == 0:
3233
+ errmsg = "Error: Save data flash sector at 0x{:X} didn’t erase successfully (SR={:04X}).".format(bank*0x10000 + pos, sr)
3234
+ print("{:s}{:s}{:s}".format(ANSI.RED, errmsg, ANSI.RESET))
3235
+ dprint(errmsg)
3236
+ break
3237
+ if buffer[buffer_offset:buffer_offset+buffer_len] != bytearray([0xFF] * buffer_len):
3238
+ if ("ereader" in self.INFO and self.INFO["ereader"] is True and sector_address == 0xF000):
3239
+ self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+0xF80], command=command, max_length=0x80)
3240
+ else:
3241
+ self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
3242
+ elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
3243
+ sector_address = pos + 0x1F00000
3244
+ if sector_address in (0x1F00000, 0x1F10000, 0x1F20000, 0x1F30000, 0x1F40000, 0x1F50000, 0x1F60000, 0x1F70000, 0x1F80000, 0x1F90000, 0x1FA0000, 0x1FB0000, 0x1FC0000, 0x1FD0000, 0x1FE0000, 0x1FF0000, 0x1FF2000, 0x1FF4000, 0x1FF6000, 0x1FF8000, 0x1FFA000, 0x1FFC000):
3245
+ dprint("DACS: Now at sector 0x{:X}".format(sector_address))
3246
+ cmds = [
3247
+ [ # Erase Sector
3248
+ [ 0, 0x50 ],
3249
+ [ sector_address, 0x20 ],
3250
+ [ sector_address, 0xD0 ],
3251
+ ]
3252
+ ]
3253
+ if sector_address == 0x1F00000: # First write
3254
+ temp = [
3255
+ [ # Unlock
3256
+ [ 0, 0x50 ],
3257
+ [ 0, 0x60 ],
3258
+ [ 0, 0xD0 ],
3259
+ ]
3260
+ ]
3261
+ temp.extend(cmds)
3262
+ cmds = temp
3263
+ elif sector_address == 0x1FFC000: # Boot sector
3264
+ temp = [
3265
+ [ # Unlock 1
3266
+ [ 0, 0x50 ],
3267
+ [ 0, 0x60 ],
3268
+ [ 0, 0xD0 ],
3269
+ ],
3270
+ [ # Unlock 2
3271
+ [ 0, 0x50 ],
3272
+ [ sector_address, 0x60 ],
3273
+ [ sector_address, 0xDC ],
3274
+ ]
3275
+ ]
3276
+ temp.extend(cmds)
3277
+ cmds = temp
3278
+
3279
+ for cmd in cmds:
3280
+ dprint("Executing DACS commands:", cmd)
3281
+ self._cart_write_flash(commands=cmd, flashcart=True)
3282
+ sr = 0
3283
+ lives = 20
3284
+ while True:
3285
+ time.sleep(0.1)
3286
+ sr = struct.unpack("<H", self._cart_read(sector_address, 2))[0]
3287
+ dprint("DACS: Status Register Check: 0x{:X} == 0x80? {:s}".format(sr, str(sr & 0xE0 == 0x80)))
3288
+ if sr & 0xE0 == 0x80: break
3289
+ lives -= 1
3290
+ if lives == 0:
3291
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An error occured while writing to the DACS cartridge. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.", "abortable":False})
3292
+ return False
3293
+ if sector_address < 0x1FFE000:
3294
+ dprint("DACS: Writing to area 0x{:X}–0x{:X}".format(0x1F00000+pos, 0x1F00000+pos+buffer_len-1))
3295
+ self.WriteROM(address=0x1F00000+pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len])
3296
+ else:
3297
+ dprint("DACS: Skipping read-only area 0x{:X}–0x{:X}".format(0x1F00000+pos, 0x1F00000+pos+buffer_len-1))
3298
+ else:
3299
+ self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
3300
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_offset+buffer_len})
3301
+
3302
+ pos += buffer_len
3303
+ buffer_offset += buffer_len
3304
+
3305
+ # if "detect" in args and args["detect"] is True:
3306
+ # buffer += bytearray(end_address - 64)
3307
+ # pos += end_address - 64
3308
+ # buffer_offset += end_address - 64
3309
+
3310
+ verified = False
3311
+ if args["mode"] == 2: # Backup
3312
+ self.INFO["transferred"] = len(buffer)
3313
+ rtc_buffer = None
3314
+ # Real Time Clock
3315
+ if args["rtc"] is True:
3316
+ self.NO_PROG_UPDATE = True
3317
+ if self.MODE == "DMG" and args["rtc"] is True:
3318
+ if _mbc.HasRTC():
3319
+ _mbc.LatchRTC()
3320
+ rtc_buffer = _mbc.ReadRTC()
3321
+ elif self.MODE == "AGB":
3322
+ _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)
3323
+ rtc_buffer = None
3324
+ if self.FW["fw_ver"] >= 12:
3325
+ self._write(self.DEVICE_CMD["AGB_READ_GPIO_RTC"])
3326
+ rtc_buffer = self._read(8)
3327
+ if len(rtc_buffer) == 8 and _agb_gpio.HasRTC(rtc_buffer) is True:
3328
+ rtc_buffer = rtc_buffer[1:]
3329
+ else:
3330
+ rtc_buffer = None
3331
+ else:
3332
+ if _agb_gpio.HasRTC() is True:
3333
+ rtc_buffer = _agb_gpio.ReadRTC(buffer=rtc_buffer)
3334
+ self.NO_PROG_UPDATE = False
3335
+ if rtc_buffer in (False, None): rtc_buffer = bytearray()
3336
+ self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)+len(rtc_buffer)})
3337
+
3338
+ # Bootleg mapper
3339
+ 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:
3340
+ buffer[5] = sram_5
3341
+
3342
+ if args["path"] is not None:
3343
+ if self.MODE == "DMG" and _mbc.GetName() == "MBC2":
3344
+ for i in range(0, len(buffer)):
3345
+ buffer[i] = buffer[i] & 0x0F
3346
+ file = open(args["path"], "wb")
3347
+ file.write(buffer)
3348
+ if rtc_buffer is not None:
3349
+ file.write(rtc_buffer)
3350
+ file.close()
3351
+ else:
3352
+ self.INFO["data"] = buffer
3353
+
3354
+ self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
3355
+ self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
3356
+
3357
+ if "verify_write" in args and args["verify_write"] not in (None, False):
3358
+ return True
3359
+ elif "verify_write" not in args or args["verify_write"] is False:
3360
+ verified = True
3361
+
3362
+ elif args["mode"] == 3: # Restore
3363
+ self.INFO["transferred"] = len(buffer)
3364
+ if args["rtc"] is True:
3365
+ advance = "rtc_advance" in args and args["rtc_advance"]
3366
+ self.SetProgress({"action":"UPDATE_RTC", "method":"write"})
3367
+ if self.MODE == "DMG" and args["rtc"] is True:
3368
+ _mbc.WriteRTC(buffer[-_mbc.GetRTCBufferSize():], advance=advance)
3369
+ elif self.MODE == "AGB":
3370
+ _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)
3371
+ _agb_gpio.WriteRTC(buffer[-0x10:], advance=advance)
3372
+
3373
+ self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer), "force_update":True})
3374
+
3375
+ # ↓↓↓ Write verify
3376
+ if "verify_write" in args and args["verify_write"] is True and args["erase"] is not True:
3377
+ if self.MODE == "DMG": _mbc.SelectBankRAM(0)
3378
+ self.SetProgress({"action":"INITIALIZE", "method":"SAVE_WRITE_VERIFY", "size":buffer_offset})
3379
+
3380
+ verify_args = copy.copy(args)
3381
+ start_address = 0
3382
+ end_address = buffer_offset
3383
+ if self.MODE == "AGB" and args["save_type"] == 6: # DACS
3384
+ end_address = min(len(buffer), 0xFE000)
3385
+
3386
+ path = args["path"] # backup path
3387
+ verify_args.update({"mode":2, "verify_write":buffer, "path":None})
3388
+ self.ReadROM(0, 4) # dummy read
3389
+ self.INFO["data"] = None
3390
+ if self._BackupRestoreRAM(verify_args) is False: return
3391
+
3392
+ if args["mbc"] == 6: # MBC2
3393
+ for i in range(0, len(self.INFO["data"])):
3394
+ self.INFO["data"][i] &= 0x0F
3395
+ buffer[i] &= 0x0F
3396
+
3397
+ args["path"] = path # restore path
3398
+ if self.CANCEL is True:
3399
+ pass
3400
+
3401
+ if self.MODE == "AGB" and "ereader" in self.INFO and self.INFO["ereader"] is True: # e-Reader
3402
+ buffer[0xFF80:0x10000] = self.INFO["data"][0xFF80:0x10000]
3403
+ buffer[0x1FF80:0x20000] = self.INFO["data"][0xFF80:0x10000]
3404
+
3405
+ elif (self.INFO["data"][:end_address] != buffer[:end_address]):
3406
+ msg = ""
3407
+ count = 0
3408
+ time_start = time.time()
3409
+ for i in range(0, len(self.INFO["data"])):
3410
+ if i >= len(buffer): break
3411
+ if time.time() > time_start + 10:
3412
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The save data was written completely, but didn’t pass the verification check.", "abortable":False})
3413
+ return False
3414
+ data1 = self.INFO["data"][i]
3415
+ data2 = buffer[:end_address][i]
3416
+ if data1 != data2:
3417
+ count += 1
3418
+ if len(msg.split("\n")) <= 10:
3419
+ msg += "- 0x{:06X}: {:02X}≠{:02X}\n".format(i, data1, data2)
3420
+ elif len(msg.split("\n")) == 11:
3421
+ msg += "(more than 10 differences found)\n"
3422
+ else:
3423
+ pass
3424
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The save data was written completely, but {:d} byte(s) ({:.2f}%) didn’t pass the verification check.\n\n{:s}".format(count, (count / len(self.INFO["data"]) * 100), msg[:-1]), "abortable":False})
3425
+ return False
3426
+ else:
3427
+ verified = True
3428
+ else:
3429
+ verified = True
3430
+ # ↑↑↑ Write verify
3431
+
3432
+ if self.MODE == "DMG":
3433
+ _mbc.SelectBankRAM(0)
3434
+ _mbc.EnableRAM(enable=False)
3435
+ self._set_fw_variable("DMG_READ_CS_PULSE", 0)
3436
+ if audio_low:
3437
+ self._set_fw_variable("FLASH_WE_PIN", 0x02)
3438
+ self.SetPin(["PIN_AUDIO"], True)
3439
+ self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"], wait=self.FW["fw_ver"] >= 12) # Prevent hotplugging corruptions on rare occasions
3440
+ elif self.MODE == "AGB":
3441
+ # Bootleg mapper
3442
+ if cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
3443
+ self._cart_write(address=5, value=0, sram=True)
3444
+ self._cart_write(address=5, value=buffer[5], sram=True)
3445
+
3446
+ # Clean up
3447
+ self.INFO["last_action"] = self.INFO["action"]
3448
+ self.INFO["action"] = None
3449
+ self.INFO["last_path"] = args["path"]
3450
+ self.SetProgress({"action":"FINISHED", "verified":verified})
3451
+ return True
3452
+
3453
+ def _FlashROM(self, args):
3454
+ self.FAST_READ = True
3455
+ if "buffer" in args:
3456
+ data_import = args["buffer"]
3457
+ else:
3458
+ with open(args["path"], "rb") as file: data_import = bytearray(file.read())
3459
+
3460
+ flash_offset = 0 # Batteryless SRAM or Transfer Resume
3461
+ if "flash_offset" in args:
3462
+ flash_offset = args["flash_offset"]
3463
+ if "start_addr" in args and args["start_addr"] > 0:
3464
+ data_import = bytearray(b'\xFF' * args["start_addr"]) + data_import
3465
+
3466
+ # Pad data
3467
+ if len(data_import) > 0:
3468
+ if len(data_import) < 0x400:
3469
+ data_import += bytearray([0xFF] * (0x400 - len(data_import)))
3470
+ if len(data_import) % 0x4000 > 0:
3471
+ data_import += bytearray([0xFF] * (0x4000 - len(data_import) % 0x4000))
3472
+
3473
+ # Skip writing the last 256 bytes of 32 MiB ROMs with EEPROM save type
3474
+ if self.MODE == "AGB" and len(data_import) == 0x2000000:
3475
+ temp_ver = "N/A"
3476
+ try:
3477
+ ids = [ b"SRAM_", b"EEPROM_V", b"FLASH_V", b"FLASH512_V", b"FLASH1M_V", b"AGB_8MDACS_DL_V" ]
3478
+ for id in ids:
3479
+ temp_pos = data_import.find(id)
3480
+ if temp_pos > 0:
3481
+ temp_ver = data_import[temp_pos:temp_pos+0x20]
3482
+ temp_ver = temp_ver[:temp_ver.index(0x00)].decode("ascii", "replace")
3483
+ break
3484
+ except ValueError:
3485
+ temp_ver = "N/A"
3486
+ if "EEPROM" in temp_ver:
3487
+ print("{:s}Note: The last 256 bytes of this 32 MiB ROM will not be written as this area is reserved by the EEPROM save type.{:s}".format(ANSI.YELLOW, ANSI.RESET))
3488
+ data_import = data_import[:0x1FFFF00]
3489
+
3490
+ # Fix bootlogo and header
3491
+ if "fix_bootlogo" in args and isinstance(args["fix_bootlogo"], bytearray):
3492
+ dstr = ''.join(format(x, '02X') for x in args["fix_bootlogo"])
3493
+ dprint("Replacing bootlogo data with", dstr)
3494
+ if self.MODE == "DMG":
3495
+ data_import[0x104:0x134] = args["fix_bootlogo"]
3496
+ elif self.MODE == "AGB":
3497
+ data_import[0x04:0xA0] = args["fix_bootlogo"]
3498
+ if "fix_header" in args and args["fix_header"]:
3499
+ dprint("Fixing header checksums")
3500
+ if self.MODE == "DMG":
3501
+ temp = RomFileDMG(data_import[0:0x200]).FixHeader()
3502
+ elif self.MODE == "AGB":
3503
+ temp = RomFileAGB(data_import[0:0x200]).FixHeader()
3504
+ data_import[0:0x200] = temp
3505
+
3506
+ supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
3507
+ cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
3508
+ if cart_type == "RETAIL": return False # Generic ROM Cartridge is not flashable
3509
+
3510
+ # Special carts
3511
+ # if "power_cycle" in cart_type and cart_type["power_cycle"] is True and not self.CanPowerCycleCart():
3512
+ # 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})
3513
+ # return False
3514
+ # Special carts
3515
+ # Firmware check L1
3516
+ 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)):
3517
+ #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})
3518
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L2.", "abortable":False})
3519
+ return False
3520
+ # Firmware check L1
3521
+ # Firmware check L2
3522
+ 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"])):
3523
+ 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))
3524
+ del(cart_type["commands"]["buffer_write"])
3525
+ # Firmware check L2
3526
+ # Firmware check L5
3527
+ 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)):
3528
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L5.", "abortable":False})
3529
+ return False
3530
+ if (self.FW["fw_ver"] < 5 and ("double_die" in cart_type and cart_type["double_die"] is True)):
3531
+ print("{:s}Note: Update your {:s} firmware to version L5 or higher for a better transfer rate with this cartridge.{:s}".format(ANSI.YELLOW, self.DEVICE_NAME, ANSI.RESET))
3532
+ del(cart_type["commands"]["buffer_write"])
3533
+ # Firmware check L5
3534
+ # Firmware check L8
3535
+ if (self.FW["fw_ver"] < 8 and "enable_pullups" in cart_type and cart_type["enable_pullups"] is True):
3536
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8.", "abortable":False})
3537
+ return False
3538
+ # Firmware check L8
3539
+ # Firmware check L12
3540
+ if (self.FW["fw_ver"] < 12 and "set_irq_high" in cart_type and cart_type["set_irq_high"] is True):
3541
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L12.", "abortable":False})
3542
+ return False
3543
+ # Firmware check L12
3544
+
3545
+ # Ensure cart is powered
3546
+ if self.CanPowerCycleCart(): self.CartPowerOn()
3547
+
3548
+ cart_type["_index"] = 0
3549
+ for i in range(0, len(list(self.SUPPORTED_CARTS[self.MODE].keys()))):
3550
+ if i == args["cart_type"]:
3551
+ try:
3552
+ cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i])
3553
+ except:
3554
+ pass
3555
+
3556
+ fc_fncptr = {
3557
+ "cart_write_fncptr":self._cart_write,
3558
+ "cart_write_fast_fncptr":self._cart_write_flash,
3559
+ "cart_read_fncptr":self.ReadROM,
3560
+ "cart_powercycle_fncptr":self.CartPowerCycleOrAskReconnect,
3561
+ "progress_fncptr":self.SetProgress,
3562
+ "set_we_pin_wr":self._set_we_pin_wr,
3563
+ "set_we_pin_audio":self._set_we_pin_audio,
3564
+ }
3565
+ if cart_type["command_set"] == "GBMEMORY":
3566
+ flashcart = Flashcart_DMG_MMSA(config=cart_type, fncptr=fc_fncptr)
3567
+ elif cart_type["command_set"] == "GBAMP":
3568
+ flashcart = Flashcart_AGB_GBAMP(config=cart_type, fncptr=fc_fncptr)
3569
+ elif cart_type["command_set"] == "BUNG_16M":
3570
+ flashcart = Flashcart_DMG_BUNG_16M(config=cart_type, fncptr=fc_fncptr)
3571
+ else:
3572
+ flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
3573
+
3574
+ rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
3575
+
3576
+ # ↓↓↓ Set Voltage
3577
+ if args["override_voltage"] is not False:
3578
+ if args["override_voltage"] == 5:
3579
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
3580
+ else:
3581
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
3582
+ elif flashcart.GetVoltage() == 3.3:
3583
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"], wait=self.FW["fw_ver"] >= 12)
3584
+ elif flashcart.GetVoltage() == 5:
3585
+ self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"], wait=self.FW["fw_ver"] >= 12)
3586
+ # ↑↑↑ Set Voltage
3587
+
3588
+ # ↓↓↓ Pad data for full chip erase on sector erase mode
3589
+ if not flashcart.SupportsChipErase() and flashcart.SupportsSectorErase() and args["prefer_chip_erase"]:
3590
+ 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))
3591
+ flash_size = flashcart.GetFlashSize()
3592
+ if flash_size is not False and len(data_import) < flash_size:
3593
+ # Pad with FF till the end (with MemoryError fix)
3594
+ pad_len = flash_size - len(data_import)
3595
+ while pad_len > 0x2000000:
3596
+ data_import += bytearray([0xFF] * 0x2000000)
3597
+ pad_len -= 0x2000000
3598
+ data_import += bytearray([0xFF] * (flash_size - len(data_import)))
3599
+ # ↑↑↑ Pad data for full chip erase on sector erase mode
3600
+
3601
+ # ↓↓↓ Flashcart configuration
3602
+ if self.FW["fw_ver"] >= 8:
3603
+ if "enable_pullups" in cart_type:
3604
+ if cart_type["enable_pullups"] is True:
3605
+ self._write(self.DEVICE_CMD["ENABLE_PULLUPS"], wait=True)
3606
+ dprint("Pullups enabled")
3607
+ else:
3608
+ self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
3609
+ dprint("Pullups disabled")
3610
+ if self.FW["fw_ver"] >= 12:
3611
+ if "enable_pullup_wr" in cart_type: # Joey Jr bug workaround
3612
+ self._set_fw_variable("PULLUPS_ENABLED", 2 if cart_type["enable_pullup_wr"] is True else 0)
3613
+ if self.MODE == "AGB":
3614
+ self._set_fw_variable("AGB_IRQ_ENABLED", 1 if "set_irq_high" in cart_type else 0)
3615
+
3616
+ _mbc = None
3617
+ errmsg_mbc_selection = ""
3618
+ if self.MODE == "DMG":
3619
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
3620
+ mbc = flashcart.GetMBC()
3621
+ if mbc is not False and isinstance(mbc, int):
3622
+ args["mbc"] = mbc
3623
+ dprint("Using forced mapper type 0x{:02X} for flashing".format(mbc))
3624
+ elif mbc == "manual":
3625
+ dprint("Using manually selected mapper type 0x{:02X} for flashing".format(args["mbc"]))
3626
+ else:
3627
+ args["mbc"] = 0
3628
+ dprint("Using default mapper type 0x{:02X} for flashing".format(args["mbc"]))
3629
+ if args["mbc"] == 0: args["mbc"] = 0x19 # MBC5 default
3630
+
3631
+ if not self.IsSupportedMbc(args["mbc"]):
3632
+ msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device.".format(Util.APPNAME, self.GetFullName())
3633
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
3634
+ return False
3635
+
3636
+ _mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
3637
+
3638
+ self._set_fw_variable("FLASH_PULSE_RESET", 1 if flashcart.PulseResetAfterWrite() else 0)
3639
+
3640
+ end_bank = math.ceil(len(data_import) / _mbc.GetROMBankSize())
3641
+ rom_bank_size = _mbc.GetROMBankSize()
3642
+
3643
+ _mbc.EnableMapper()
3644
+
3645
+ if flashcart.GetMBC() == "manual":
3646
+ errmsg_mbc_selection += "\n- Check mapper type used: {:s} (manual selection)".format(_mbc.GetName())
3647
+ else:
3648
+ errmsg_mbc_selection += "\n- Check mapper type used: {:s} (forced by selected cartridge type)".format(_mbc.GetName())
3649
+ if len(data_import) > _mbc.GetMaxROMSize():
3650
+ errmsg_mbc_selection += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(size=_mbc.GetMaxROMSize()))
3651
+
3652
+ elif self.MODE == "AGB":
3653
+ self._write(self.DEVICE_CMD["SET_MODE_AGB"], wait=self.FW["fw_ver"] >= 12)
3654
+ if flashcart and "flash_bank_size" in cart_type:
3655
+ end_bank = math.ceil(len(data_import) / cart_type["flash_bank_size"])
3656
+ else:
3657
+ end_bank = 1
3658
+ if flashcart and "flash_bank_size" in cart_type:
3659
+ rom_bank_size = cart_type["flash_bank_size"]
3660
+ else:
3661
+ rom_bank_size = 0x2000000
3662
+
3663
+ flash_buffer_size = flashcart.GetBufferSize()
3664
+ # ↑↑↑ Flashcart configuration
3665
+
3666
+ # ↓↓↓ DMG-MMSA-JPN hidden sector
3667
+ if self.MODE == "DMG" and _mbc.GetName() == "G-MMC1":
3668
+ if "buffer_map" not in args:
3669
+ if os.path.exists(os.path.splitext(args["path"])[0] + ".map"):
3670
+ with open(os.path.splitext(args["path"])[0] + ".map", "rb") as file: args["buffer_map"] = file.read()
3671
+ else:
3672
+ temp = data_import
3673
+ if len(temp) == 0: temp = bytearray([0xFF] * 0x180)
3674
+ try:
3675
+ gbmem = GBMemoryMap(rom=temp, oldmap=_mbc.ReadHiddenSector())
3676
+ args["buffer_map"] = gbmem.GetMapData()
3677
+ except Exception:
3678
+ print(traceback.format_exc())
3679
+ print("{:s}An error occured while trying to generate the hidden sector data for the NP GB-Memory cartridge.{:s}".format(ANSI.RED, ANSI.RESET))
3680
+ args["buffer_map"] = False
3681
+
3682
+ if args["buffer_map"] is False:
3683
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The NP GB-Memory Cartridge requires extra hidden sector data. As it couldn’t be auto-generated, please provide your own at the following path: {:s}".format(os.path.splitext(args["path"])[0] + ".map"), "abortable":False})
3684
+ return False
3685
+ else:
3686
+ dprint("Hidden sector data:", args["buffer_map"])
3687
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
3688
+ with open("debug_mmsa_map.bin", "wb") as f: f.write(args["buffer_map"])
3689
+ data_map_import = copy.copy(args["buffer_map"])
3690
+ data_map_import = bytearray(data_map_import)
3691
+ dprint("Hidden sector data loaded")
3692
+ # ↑↑↑ DMG-MMSA-JPN hidden sector
3693
+
3694
+ # ↓↓↓ Load commands into firmware
3695
+ flash_cmds = []
3696
+ command_set_type = flashcart.GetCommandSetType()
3697
+ temp = 0
3698
+ if command_set_type == "AMD":
3699
+ temp = 0x01
3700
+ #self._write(0x01) # FLASH_COMMAND_SET_AMD
3701
+ #self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
3702
+ dprint("Using AMD command set")
3703
+ elif command_set_type == "INTEL":
3704
+ temp = 0x02
3705
+ #self._write(0x02) # FLASH_COMMAND_SET_INTEL
3706
+ self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
3707
+ dprint("Using Intel command set")
3708
+ elif command_set_type == "SHARP":
3709
+ temp = 0x02
3710
+ #self._write(0x02) # FLASH_COMMAND_SET_INTEL
3711
+ self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 1)
3712
+ dprint("Using Sharp/Intel command set")
3713
+ elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
3714
+ temp = 0x00
3715
+ dprint("Using GB-Memory command set")
3716
+ elif command_set_type in ("BLAZE_XPLODER", "DATEL_ORBITV2", "EEPROM", "GBAMP", "BUNG_16M"):
3717
+ temp = 0x00
3718
+ else:
3719
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM flashing.", "abortable":False})
3720
+ return False
3721
+
3722
+ if flashcart.HasDoubleDie() and self.FW["fw_ver"] >= 5:
3723
+ self._set_fw_variable("FLASH_DOUBLE_DIE", 1)
3724
+ else:
3725
+ self._set_fw_variable("FLASH_DOUBLE_DIE", 0)
3726
+
3727
+ if command_set_type == "GBMEMORY" and self.FW["fw_ver"] < 2:
3728
+ self._set_fw_variable("FLASH_WE_PIN", 0x01)
3729
+ dprint("Using legacy GB-Memory mode")
3730
+ elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] < 12:
3731
+ self._set_fw_variable("FLASH_WE_PIN", 0x02)
3732
+ else:
3733
+ self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
3734
+ self._write(temp)
3735
+
3736
+ if flashcart.IsF2A():
3737
+ self._write(0x05) # FLASH_METHOD_AGB_FLASH2ADVANCE
3738
+ flash_cmds = [
3739
+ [ "SA", 0xE8 ],
3740
+ [ "SA", "BS" ],
3741
+ [ "PA", "PD" ],
3742
+ [ "SA", 0xD0 ],
3743
+ [ "SA", 0xFF ]
3744
+ ]
3745
+ dprint("Using Flash2Advance mode with a buffer of {:d} bytes".format(flash_buffer_size))
3746
+ elif command_set_type == "GBMEMORY" and self.FW["fw_ver"] >= 2:
3747
+ self._write(0x03) # FLASH_METHOD_DMG_MMSA
3748
+ dprint("Using GB-Memory mode")
3749
+ elif command_set_type == "DATEL_ORBITV2" and self.FW["fw_ver"] >= 12:
3750
+ self._write(0x09) # FLASH_METHOD_DMG_DATEL_ORBITV2
3751
+ dprint("Using Datel Orbit V2 mode")
3752
+ elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] >= 12:
3753
+ self._write(0x0A) # FLASH_METHOD_DMG_E201264
3754
+ dprint("Using E201264 mode")
3755
+ elif command_set_type == "GBAMP" and self.FW["fw_ver"] >= 12:
3756
+ self._write(0x0B) # FLASH_METHOD_AGB_GBAMP
3757
+ dprint("Using GBAMP mode")
3758
+ elif command_set_type == "BUNG_16M" and self.FW["fw_ver"] >= 12:
3759
+ self._write(0x0C) # FLASH_METHOD_DMG_BUNG_16M
3760
+ dprint("Using BUNG Doctor GB Card 16M mode")
3761
+ elif flashcart.SupportsBufferWrite() and flash_buffer_size > 0:
3762
+ self._write(0x02) # FLASH_METHOD_BUFFERED
3763
+ flash_cmds = flashcart.GetCommands("buffer_write")
3764
+ dprint("Using buffered writing with a buffer of {:d} bytes".format(flash_buffer_size))
3765
+ elif flashcart.SupportsPageWrite() and flash_buffer_size > 0 and self.FW["fw_ver"] >= 12:
3766
+ self._write(0x08) # FLASH_METHOD_PAGED
3767
+ flash_cmds = flashcart.GetCommands("page_write")
3768
+ dprint("Using paged writing with a buffer of {:d} bytes".format(flash_buffer_size))
3769
+ elif flashcart.SupportsSingleWrite():
3770
+ self._write(0x01) # FLASH_METHOD_UNBUFFERED
3771
+ flash_cmds = flashcart.GetCommands("single_write")
3772
+ dprint("Using single writing")
3773
+ else:
3774
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM writing.", "abortable":False})
3775
+ return False
3776
+
3777
+ if flashcart.WEisWR():
3778
+ we = 0x01 # FLASH_WE_PIN_WR
3779
+ self._write(we)
3780
+ dprint("Using WR as WE")
3781
+ elif flashcart.WEisAUDIO():
3782
+ we = 0x02 # FLASH_WE_PIN_AUDIO
3783
+ self._write(we)
3784
+ dprint("Using AUDIO as WE")
3785
+ elif flashcart.WEisWR_RESET():
3786
+ we = 0x03 # FLASH_WE_PIN_WR_RESET
3787
+ self._write(we) # FLASH_WE_PIN_WR_RESET
3788
+ dprint("Using WR+RESET as WE")
3789
+ else:
3790
+ we = 0x00
3791
+ self._write(we) # unset
3792
+
3793
+ for i in range(0, 6):
3794
+ if i > len(flash_cmds) - 1: # skip
3795
+ self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
3796
+ else:
3797
+ address = flash_cmds[i][0]
3798
+ value = flash_cmds[i][1]
3799
+ if not isinstance(address, int): address = 0
3800
+ if not isinstance(value, int): value = 0
3801
+ if self.MODE == "AGB": address >>= 1
3802
+ dprint("Setting command #{:d} to 0x{:X}=0x{:X}".format(i, address, value))
3803
+ self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value)))
3804
+ if self.FW["fw_ver"] >= 12: self.wait_for_ack()
3805
+
3806
+ if self.FW["fw_ver"] >= 6:
3807
+ if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
3808
+ self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
3809
+ self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
3810
+ if "bank_switch" in cart_type["commands"]:
3811
+ self._write(len(cart_type["commands"]["bank_switch"])) # number of commands
3812
+ for command in cart_type["commands"]["bank_switch"]:
3813
+ address = command[0]
3814
+ value = command[1]
3815
+ if value == "ID":
3816
+ self._write(bytearray(struct.pack(">I", address))) # address
3817
+ self._write(0) # type = address
3818
+ else:
3819
+ self._write(bytearray(struct.pack(">I", value))) # value
3820
+ self._write(1) # type = value
3821
+ ret = self._read(1)
3822
+ if ret != 0x01:
3823
+ print("Error in DMG_SET_BANK_CHANGE_CMD:", ret)
3824
+ else:
3825
+ self._write(0, wait=True)
3826
+ else:
3827
+ self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
3828
+ self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
3829
+ self._write(0, wait=True)
3830
+ else:
3831
+ if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
3832
+ self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
3833
+ else:
3834
+ self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
3835
+
3836
+ if self.FW["fw_ver"] >= 12:
3837
+ if "status_register_mask" in cart_type:
3838
+ self._set_fw_variable("STATUS_REGISTER_MASK", cart_type["status_register_mask"])
3839
+ self._set_fw_variable("STATUS_REGISTER_VALUE", cart_type["status_register_value"])
3840
+ else:
3841
+ self._set_fw_variable("STATUS_REGISTER_MASK", 0x80)
3842
+ self._set_fw_variable("STATUS_REGISTER_VALUE", 0x80)
3843
+ # ↑↑↑ Load commands into firmware
3844
+
3845
+ # ↓↓↓ Preparations
3846
+ if self.MODE == "DMG" and "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
3847
+ dprint("Setting ROM bank 1")
3848
+ _mbc.SelectBankROM(1)
3849
+ if we != 0x00:
3850
+ self._set_fw_variable("FLASH_WE_PIN", we)
3851
+ # ↑↑↑ Preparations
3852
+
3853
+ # ↓↓↓ Read Flash ID
3854
+ if "flash_ids" in cart_type:
3855
+ (verified, flash_id) = flashcart.VerifyFlashID()
3856
+ if not verified and not command_set_type == "BLAZE_XPLODER":
3857
+ print("{:s}Note: This cartridge’s Flash ID ({:s}) doesn’t match the cartridge type selection.{:s}".format(ANSI.YELLOW, ' '.join(format(x, '02X') for x in flash_id), ANSI.RESET))
3858
+ else:
3859
+ if flashcart.Unlock() is False: return False
3860
+ # ↑↑↑ Read Flash ID
3861
+
3862
+ # ↓↓↓ Read Sector Map
3863
+ sector_map = flashcart.GetSectorMap()
3864
+ smallest_sector_size = 0x2000
3865
+ sector_offsets = []
3866
+ write_sectors = []
3867
+ verify_sectors = []
3868
+ sector_pos = 0
3869
+ delta_state_new = None
3870
+ flash_capacity = len(data_import)
3871
+ if sector_map is not None and sector_map is not False:
3872
+ smallest_sector_size = flashcart.GetSmallestSectorSize()
3873
+ sector_offsets = flashcart.GetSectorOffsets(rom_size=flashcart.GetFlashSize(default=len(data_import)), rom_bank_size=rom_bank_size)
3874
+ if len(sector_offsets) > 0:
3875
+ flash_capacity = sector_offsets[-1][0] + sector_offsets[-1][1]
3876
+ if flash_capacity < len(data_import) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]):
3877
+ #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})
3878
+ #return False
3879
+ sector_offsets = flashcart.GetSectorOffsets(rom_size=len(data_import), rom_bank_size=rom_bank_size)
3880
+
3881
+ sector_offsets_hash = base64.urlsafe_b64encode(hashlib.sha1(str(sector_offsets).encode("UTF-8")).digest()).decode("ASCII", "ignore")[:4]
3882
+
3883
+ # Delta flashing
3884
+ if len(sector_offsets) > 1:
3885
+ splitext = os.path.splitext(args["path"])
3886
+ if splitext[0].endswith(".delta") and os.path.exists(splitext[0][:-6] + splitext[1]):
3887
+ delta_state_new = []
3888
+ with open(splitext[0][:-6] + splitext[1], "rb") as f:
3889
+ for i in range(0, len(sector_offsets)):
3890
+ s_from = sector_offsets[i][0]
3891
+ s_size = sector_offsets[i][1]
3892
+ s_to = s_from + s_size
3893
+ if data_import[s_from:s_to] != f.read(s_to - s_from):
3894
+ x = [ s_from, s_size, zlib.crc32(data_import[s_from:s_to]) & 0xFFFFFFFF ]
3895
+ delta_state_new.append(x)
3896
+ dprint("Sector differs:", x)
3897
+ write_sectors = copy.copy(delta_state_new)
3898
+ json_file = "{:s}_{:s}.json".format(splitext[0], sector_offsets_hash)
3899
+ if os.path.exists(json_file):
3900
+ with open(json_file, "rb") as f:
3901
+ try:
3902
+ delta_state_old = json.loads(f.read().decode("UTF-8-SIG"))
3903
+ except:
3904
+ delta_state_old = []
3905
+ if len(delta_state_old) > 0:
3906
+ for x in delta_state_old:
3907
+ if x in write_sectors:
3908
+ del(write_sectors[write_sectors.index(x)])
3909
+ dprint("Skipping sector:", x)
3910
+ else:
3911
+ write_sectors2 = []
3912
+ for y in write_sectors: write_sectors2.append(y[:-1])
3913
+ if x[:-1] not in write_sectors2:
3914
+ write_sectors.append(x)
3915
+ dprint("Forcing sector:", x)
3916
+
3917
+ if len(write_sectors) == 0:
3918
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No flash sectors were found that would need to be updated for delta flashing.", "abortable":False})
3919
+ return False
3920
+
3921
+ elif "flash_sectors" in args and len(args["flash_sectors"]) > 0:
3922
+ write_sectors = args["flash_sectors"]
3923
+
3924
+ elif flash_offset > 0: # Batteryless SRAM
3925
+ if "flash_size" not in args:
3926
+ flash_size = len(data_import) - flash_offset
3927
+ else:
3928
+ flash_size = args["flash_size"]
3929
+ data_import = data_import[:flash_size]
3930
+ bl_sectors = []
3931
+ for sector in sector_offsets:
3932
+ if flash_offset > sector[0]: continue
3933
+ #if len(bl_sectors) > 0 and (flash_offset + flash_size) < (sector[0] + sector[1]): break
3934
+ if (flash_offset + flash_size) < sector[0]: break
3935
+ bl_sectors.append(sector)
3936
+ write_sectors = bl_sectors
3937
+ if "bl_save" in args:
3938
+ data_import = bytearray([0] * bl_sectors[0][0]) + data_import
3939
+ else:
3940
+ write_sectors = []
3941
+ for item in sector_offsets:
3942
+ if item[0] < len(data_import):
3943
+ write_sectors.append(item)
3944
+
3945
+ dprint("Sectors to update:", write_sectors)
3946
+ # ↑↑↑ Read Sector Map
3947
+
3948
+ # ↓↓↓ Chip erase
3949
+ chip_erase = False
3950
+ if flashcart.SupportsChipErase() and not flash_offset > 0:
3951
+ if flashcart.SupportsSectorErase() and args["prefer_chip_erase"] is False and sector_map is not False:
3952
+ chip_erase = False
3953
+ else:
3954
+ chip_erase = True
3955
+ dprint("Erasing the entire flash chip")
3956
+ if flashcart.ChipErase() is False:
3957
+ return False
3958
+ elif flashcart.SupportsSectorErase() is False:
3959
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No erase method available.", "abortable":False})
3960
+ return False
3961
+ # ↑↑↑ Chip erase
3962
+
3963
+ # ↓↓↓ Flash Write
3964
+ if chip_erase:
3965
+ write_sectors = [[ 0, len(data_import) ]]
3966
+ for i in range(0, len(data_import), 0x20000):
3967
+ verify_sectors.append([i, 0x20000])
3968
+ elif len(write_sectors) == 0:
3969
+ write_sectors = sector_offsets
3970
+
3971
+ self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE", "size":len(data_import), "flash_offset":flash_offset, "sector_count":len(write_sectors)})
3972
+ self.SetProgress({"action":"UPDATE_POS", "pos":flash_offset})
3973
+ self.INFO["action"] = self.ACTIONS["ROM_WRITE"]
3974
+
3975
+ if smallest_sector_size is not False:
3976
+ buffer_len = smallest_sector_size
3977
+ elif self.MODE == "DMG":
3978
+ buffer_len = _mbc.GetROMBankSize()
3979
+ if _mbc.HasFlashBanks(): _mbc.SelectBankFlash(0)
3980
+ else:
3981
+ buffer_len = 0x2000
3982
+ dprint("Transfer buffer length is 0x{:X}".format(buffer_len))
3983
+
3984
+ current_bank = 0
3985
+ start_bank = 0
3986
+ start_address = 0
3987
+ buffer_pos = 0
3988
+ retry_hp = 0
3989
+ end_address = len(data_import)
3990
+ dprint("ROM banks:", end_bank)
3991
+
3992
+ if len(write_sectors) == 0:
3993
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Coulnd’t start writing ROM because the flash cart couldn’t be detected properly.", "abortable":False})
3994
+ return False
3995
+
3996
+ for sector in write_sectors:
3997
+ if chip_erase is False:
3998
+ if retry_hp == 0:
3999
+ retry_hp = 15 # First sector
4000
+ else:
4001
+ retry_hp = 100 # Other sectors
4002
+
4003
+ if self.MODE == "AGB":
4004
+ dprint("Writing sector:", hex(sector[0]), hex(sector[1]))
4005
+ buffer_pos = sector[0]
4006
+ start_address = buffer_pos
4007
+ end_address = sector[0] + sector[1]
4008
+ if sector[:2] not in sector_offsets:
4009
+ dprint("Sector not found for delta writing:", sector)
4010
+ continue
4011
+ sector_pos = sector_offsets.index(sector[:2])
4012
+ start_bank = math.floor(buffer_pos / rom_bank_size)
4013
+ end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
4014
+ # print(hex(start_address), hex(end_address), start_bank, end_bank)
4015
+ # print(hex(sector_offsets[sector_pos][0]), sector_pos)
4016
+ # print("")
4017
+ elif self.MODE == "DMG":
4018
+ dprint("Writing sector:", hex(sector[0]), hex(sector[1]))
4019
+ buffer_pos = sector[0]
4020
+ #end_address = sector[0] + sector[1]
4021
+ if sector[:2] not in sector_offsets:
4022
+ print("Sector not found for delta writing:", sector)
4023
+ continue
4024
+ sector_pos = sector_offsets.index(sector[:2])
4025
+ start_bank = math.floor(buffer_pos / rom_bank_size)
4026
+ end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
4027
+ # print(hex(start_address), hex(end_address), start_bank, end_bank)
4028
+ # print(hex(sector_offsets[sector_pos][0]), sector_pos)
4029
+ # print("")
4030
+
4031
+ #for bank in range(start_bank, end_bank):
4032
+ bank = start_bank
4033
+ while bank < end_bank:
4034
+ if self.CANCEL:
4035
+ cancel_args = {"action":"ABORT", "abortable":False}
4036
+ cancel_args.update(self.CANCEL_ARGS)
4037
+ self.CANCEL_ARGS = {}
4038
+ self.ERROR_ARGS = {}
4039
+ self.SetProgress(cancel_args)
4040
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
4041
+ return
4042
+
4043
+ status = None
4044
+ #print("Bank:", bank, "...")
4045
+ # ↓↓↓ Switch ROM bank
4046
+ if self.MODE == "DMG":
4047
+ if _mbc.ResetBeforeBankChange(bank) is True:
4048
+ dprint("Resetting the MBC")
4049
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4050
+ (start_address, bank_size) = _mbc.SelectBankROM(bank)
4051
+ if flashcart.PulseResetAfterWrite():
4052
+ if bank == 0:
4053
+ if self.FW["fw_ver"] < 2:
4054
+ self._write(self.DEVICE_CMD["OFW_GB_CART_MODE"])
4055
+ else:
4056
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4057
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4058
+ # else:
4059
+ # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4060
+ self._set_fw_variable("DMG_ROM_BANK", bank)
4061
+
4062
+ buffer_len = min(buffer_len, bank_size)
4063
+ if "start_addr" in flashcart.CONFIG and bank == 0: start_address = flashcart.CONFIG["start_addr"]
4064
+ end_address = start_address + bank_size
4065
+ start_address += (buffer_pos % rom_bank_size)
4066
+ if end_address > start_address + sector[1]:
4067
+ end_address = start_address + sector[1]
4068
+
4069
+ elif self.MODE == "AGB":
4070
+ if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
4071
+ if bank != current_bank:
4072
+ flashcart.Reset(full_reset=True)
4073
+ flashcart.SelectBankROM(bank)
4074
+ temp = end_address - start_address
4075
+ start_address %= cart_type["flash_bank_size"]
4076
+ end_address = min(cart_type["flash_bank_size"], start_address + temp)
4077
+ current_bank = bank
4078
+ # ↑↑↑ Switch ROM bank
4079
+
4080
+ skip_init = False
4081
+ pos = start_address
4082
+ dprint("buffer_pos=0x{:X}, start_address=0x{:X}, end_address=0x{:X}".format(buffer_pos, start_address, end_address))
4083
+
4084
+ while pos < end_address:
4085
+ if self.CANCEL:
4086
+ cancel_args = {"action":"ABORT", "abortable":False}
4087
+ cancel_args.update(self.CANCEL_ARGS)
4088
+ self.CANCEL_ARGS = {}
4089
+ self.ERROR_ARGS = {}
4090
+ self.SetProgress(cancel_args)
4091
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
4092
+ return
4093
+
4094
+ if buffer_pos >= len(data_import): break
4095
+
4096
+ # ↓↓↓ Sector erase
4097
+ se_ret = None
4098
+ if chip_erase is False:
4099
+ if sector_pos < len(sector_offsets) and buffer_pos == sector_offsets[sector_pos][0]:
4100
+ len_rest = end_address - pos
4101
+ skip_se = False
4102
+ ts_se_start = time.time()
4103
+ if "compare_sectors" in args and args["compare_sectors"] is True and sector[1] <= len_rest and not (flashcart and cart_type["command_set"] == "GBAMP"):
4104
+ verified = self.CompareCRC32(buffer=data_import, offset=sector[0], length=sector[1], address=start_address, flashcart=flashcart, reset=True)
4105
+ if verified is True:
4106
+ skip_se = True
4107
+ elif verified is not True and len(verified) == 2:
4108
+ dprint("Writing sector #{:d}, because the CRC32 didn’t match: 0x{:X} ≠ 0x{:X}".format(sector_pos, verified[0], verified[1]))
4109
+ verified = False
4110
+ skip_se = False
4111
+
4112
+ if self.CANCEL:
4113
+ cancel_args = {"action":"ABORT", "abortable":False}
4114
+ cancel_args.update(self.CANCEL_ARGS)
4115
+ self.CANCEL_ARGS = {}
4116
+ self.ERROR_ARGS = {}
4117
+ self.SetProgress(cancel_args)
4118
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
4119
+ return
4120
+
4121
+ if skip_se is True:
4122
+ dprint("Skipping sector #{:d}, because the CRC32 matched".format(sector_pos))
4123
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "sector_pos":sector_pos, "force_update":True})
4124
+ else:
4125
+ if sector not in verify_sectors:
4126
+ verify_sectors.append(sector)
4127
+ dprint("Erasing sector #{:d} at position 0x{:X} (0x{:X})".format(sector_pos, buffer_pos, pos))
4128
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "force_update":True})
4129
+ if self.CanPowerCycleCart(): self.CartPowerOn()
4130
+
4131
+ sector_pos += 1
4132
+ if flashcart.FlashCommandsOnBank1(): _mbc.SelectBankROM(bank)
4133
+ self.NO_PROG_UPDATE = True
4134
+ se_ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos, skip=skip_se)
4135
+ self.NO_PROG_UPDATE = False
4136
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
4137
+ continue
4138
+
4139
+ ts_se_elapsed = time.time() - ts_se_start
4140
+ if se_ret:
4141
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "sector_pos":sector_pos, "sector_erase_time":ts_se_elapsed, "force_update":True})
4142
+ sector_size = se_ret
4143
+ dprint("Next sector size: 0x{:X}".format(sector_size))
4144
+ skip_init = False
4145
+
4146
+ if skip_se:
4147
+ buffer_pos += sector_size
4148
+ pos += sector_size
4149
+ continue
4150
+ # ↑↑↑ Sector erase
4151
+
4152
+ if se_ret is not False:
4153
+ if command_set_type == "GBMEMORY" and self.FW["fw_ver"] < 2:
4154
+ status = self.WriteROM_GBMEMORY(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
4155
+ elif command_set_type == "GBMEMORY" and self.FW["fw_ver"] >= 2:
4156
+ 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))
4157
+ elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] < 12:
4158
+ status = self.WriteROM_DMG_MBC5_32M_FLASH(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
4159
+ #elif command_set_type == "DMG-MBC5-32M-FLASH" and self.FW["fw_ver"] >= 12:
4160
+ # 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))
4161
+ elif command_set_type == "EEPROM":
4162
+ status = self.WriteROM_DMG_EEPROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank, eeprom_buffer_size=256)
4163
+ elif command_set_type == "BLAZE_XPLODER":
4164
+ status = self.WriteROM_DMG_EEPROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
4165
+ elif command_set_type == "DATEL_ORBITV2" and self.FW["fw_ver"] >= 12:
4166
+ status = self.WriteROM(address=(pos | (bank << 24)), buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING))
4167
+ elif command_set_type == "DATEL_ORBITV2" and self.FW["fw_ver"] < 12:
4168
+ status = self.WriteROM_DMG_DatelOrbitV2(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
4169
+ else:
4170
+ max_buffer_write = self.MAX_BUFFER_WRITE
4171
+ if (len(data_import) == 0x1FFFF00) and (buffer_pos+buffer_len > len(data_import)):
4172
+ # 32 MiB ROM + EEPROM cart
4173
+ max_buffer_write = 256
4174
+ buffer_len = (buffer_pos+buffer_len - len(data_import))
4175
+ status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble, max_length=max_buffer_write)
4176
+
4177
+ if status is False or se_ret is False:
4178
+ self.CANCEL = True
4179
+ self.ERROR = True
4180
+ sr = "Unknown"
4181
+ if self.FW["fw_ver"] >= 12:
4182
+ if se_ret is False:
4183
+ sr = flashcart.LAST_SR
4184
+ if isinstance(sr, int):
4185
+ if sr < 0x100:
4186
+ sr = f"0x{sr:02X} ({sr>>4:04b} {sr&0xF:04b})"
4187
+ else:
4188
+ sr = f"0x{sr:04X} ({sr>>8:08b} {sr&0xFF:08b})"
4189
+ else:
4190
+ lives = 3
4191
+ while lives > 0:
4192
+ dprint("Retrieving last status register value...")
4193
+ self.DEVICE.reset_input_buffer()
4194
+ self.DEVICE.reset_output_buffer()
4195
+ sr = self._get_fw_variable("STATUS_REGISTER")
4196
+ if sr not in (False, None):
4197
+ if sr < 0x100:
4198
+ sr = f"0x{sr:02X} ({sr>>4:04b} {sr&0xF:04b})"
4199
+ else:
4200
+ sr = f"0x{sr:04X} ({sr>>8:08b} {sr&0xFF:08b})"
4201
+ break
4202
+ dprint("Erroneous response:", sr)
4203
+ lives -= 1
4204
+ if lives == 0:
4205
+ sr = "Timeout"
4206
+ dprint("Last status register value:", sr)
4207
+
4208
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
4209
+ break
4210
+ elif not self.DEVICE.is_open or self.DEVICE is None:
4211
+ 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)})
4212
+ break
4213
+ else:
4214
+ if chip_erase: retry_hp = 0
4215
+ if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
4216
+ retry_hp -= 5
4217
+ if retry_hp <= 0:
4218
+ 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)})
4219
+ continue
4220
+ else:
4221
+ retry_hp -= 10
4222
+ if retry_hp <= 0:
4223
+ 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})
4224
+ continue
4225
+
4226
+ rev_buffer_pos = sector_offsets[sector_pos - 1][0]
4227
+ buffer_pos = rev_buffer_pos
4228
+ bank = start_bank
4229
+ sector_pos -= 1
4230
+ err_text = "Write error! Retrying from 0x{:X}...".format(rev_buffer_pos)
4231
+ print("{:s}{:s}{:s}".format(ANSI.RED, err_text, ANSI.RESET))
4232
+ dprint(err_text, "Bank {:d} | HP: {:d}/100".format(bank, retry_hp))
4233
+ pos = end_address
4234
+ status = False
4235
+
4236
+ self.SetProgress({"action":"ERROR", "abortable":True, "pos":buffer_pos, "text":err_text})
4237
+ delay = 0.5 # + (100-retry_hp)/100
4238
+ if self.CanPowerCycleCart():
4239
+ self.CartPowerOff()
4240
+ time.sleep(delay)
4241
+ self.CartPowerOn()
4242
+ if self.MODE == "DMG" and _mbc.HasFlashBanks(): _mbc.SelectBankFlash(bank)
4243
+ time.sleep(delay)
4244
+ if self.DEVICE is None:
4245
+ raise ConnectionAbortedError("A critical connection error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False)))
4246
+
4247
+ self.ERROR = False
4248
+ if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
4249
+ self.CANCEL_ARGS.update({"info_type":"msgbox_warning", "info_msg":"The erroneous process has been stopped.", "abortable":False})
4250
+ break
4251
+ self.CANCEL = False
4252
+ self.CANCEL_ARGS = {}
4253
+ self.DEVICE.reset_input_buffer()
4254
+ self.DEVICE.reset_output_buffer()
4255
+ self._cart_write(pos, 0xF0)
4256
+ self._cart_write(pos, 0xFF)
4257
+ if flashcart.Unlock() is False: return False
4258
+ continue
4259
+
4260
+ self.CANCEL = True
4261
+ self.ERROR = True
4262
+ continue
4263
+
4264
+ skip_init = True
4265
+
4266
+ buffer_pos += buffer_len
4267
+ pos += buffer_len
4268
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
4269
+
4270
+ if status is not False:
4271
+ bank += 1
4272
+
4273
+ self.SetProgress({"action":"UPDATE_POS", "pos":len(data_import), "force_update":True})
4274
+ # ↑↑↑ Flash write
4275
+
4276
+ # ↓↓↓ GB-Memory Hidden Sector
4277
+ if command_set_type == "GBMEMORY":
4278
+ if flashcart.EraseHiddenSector(buffer=data_map_import) is False:
4279
+ return False
4280
+ status = self.WriteROM_GBMEMORY(address=0, buffer=data_map_import[0:128], bank=1)
4281
+ if status is False:
4282
+ self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An error occured while writing the hidden sector. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.", "abortable":False})
4283
+ return False
4284
+ # ↑↑↑ GB-Memory Hidden Sector
4285
+
4286
+ # ↓↓↓ Reset flash
4287
+ flashcart.Reset(full_reset=True)
4288
+ # ↑↑↑ Reset flash
4289
+
4290
+ # ↓↓↓ Flash verify
4291
+ verified = False
4292
+ crc32_errors = 0
4293
+ if "broken_sectors" in self.INFO: del(self.INFO["broken_sectors"])
4294
+ if "verify_write" in args and args["verify_write"] is True:
4295
+ self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE_VERIFY", "size":len(data_import), "flash_offset":flash_offset})
4296
+ if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
4297
+ with open("debug_verify.bin", "wb") as f: pass
4298
+
4299
+ current_bank = None
4300
+ broken_sectors = []
4301
+
4302
+ for sector in verify_sectors:
4303
+ if self.CANCEL:
4304
+ cancel_args = {"action":"ABORT", "abortable":False}
4305
+ cancel_args.update(self.CANCEL_ARGS)
4306
+ self.CANCEL_ARGS = {}
4307
+ self.ERROR_ARGS = {}
4308
+ self.SetProgress(cancel_args)
4309
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
4310
+ return
4311
+
4312
+ if sector[0] >= len(data_import): break
4313
+
4314
+ verified = False
4315
+ if self.FW["fw_ver"] >= 10 and not (flashcart and cart_type["command_set"] == "GBAMP"):
4316
+ if self.MODE == "AGB":
4317
+ dprint("Verifying sector:", hex(sector[0]), hex(sector[1]))
4318
+ buffer_pos = sector[0]
4319
+ start_address = buffer_pos
4320
+ end_address = sector[0] + sector[1]
4321
+ if not chip_erase:
4322
+ sector_pos = sector_offsets.index(sector[:2])
4323
+ else:
4324
+ sector_pos = 0
4325
+ start_bank = math.floor(buffer_pos / rom_bank_size)
4326
+ end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
4327
+ elif self.MODE == "DMG":
4328
+ dprint("Verifying sector:", hex(sector[0]), hex(sector[1]))
4329
+ buffer_pos = sector[0]
4330
+ if not chip_erase:
4331
+ sector_pos = sector_offsets.index(sector[:2])
4332
+ else:
4333
+ sector_pos = 0
4334
+ start_bank = math.floor(buffer_pos / rom_bank_size)
4335
+ end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
4336
+
4337
+ bank = start_bank
4338
+ while bank < end_bank:
4339
+ # ↓↓↓ Switch ROM bank
4340
+ if self.MODE == "DMG":
4341
+ if _mbc.ResetBeforeBankChange(bank) is True:
4342
+ dprint("Resetting the MBC")
4343
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4344
+ (start_address, bank_size) = _mbc.SelectBankROM(bank)
4345
+ verify_len = bank_size
4346
+ if flashcart.PulseResetAfterWrite():
4347
+ if bank == 0:
4348
+ if self.FW["fw_ver"] < 2:
4349
+ self._write(self.DEVICE_CMD["OFW_GB_CART_MODE"])
4350
+ else:
4351
+ self._write(self.DEVICE_CMD["SET_MODE_DMG"], wait=self.FW["fw_ver"] >= 12)
4352
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4353
+ # else:
4354
+ # self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
4355
+ self._set_fw_variable("DMG_ROM_BANK", bank)
4356
+
4357
+ buffer_len = min(buffer_len, bank_size)
4358
+ if "start_addr" in flashcart.CONFIG and bank == 0: start_address = flashcart.CONFIG["start_addr"]
4359
+ end_address = start_address + bank_size
4360
+ start_address += (buffer_pos % rom_bank_size)
4361
+ if end_address > start_address + sector[1]:
4362
+ end_address = start_address + sector[1]
4363
+ pos_from = (bank * start_address)
4364
+ # pos_to = (bank * start_address) + verify_len
4365
+
4366
+ elif self.MODE == "AGB":
4367
+ if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
4368
+ if bank != current_bank:
4369
+ flashcart.Reset(full_reset=True)
4370
+ flashcart.SelectBankROM(bank)
4371
+ temp = end_address - start_address
4372
+ start_address %= cart_type["flash_bank_size"]
4373
+ end_address = min(cart_type["flash_bank_size"], start_address + temp)
4374
+ current_bank = bank
4375
+ verify_len = sector[1]
4376
+ pos_from = sector[0]
4377
+ # pos_to = pos_from + verify_len
4378
+ # ↑↑↑ Switch ROM bank
4379
+
4380
+ dprint(f"Verifying ROM bank #{bank} at 0x{pos_from:x} (physical 0x{start_address:X}, 0x{verify_len:X} bytes)")
4381
+
4382
+ verified = False
4383
+ if sector[1] >= verify_len and crc32_errors < 5:
4384
+ verified = self.CompareCRC32(buffer=data_import, offset=pos_from, length=verify_len, address=start_address, flashcart=flashcart, reset=False)
4385
+ if verified is True:
4386
+ dprint("CRC32 verification successful between 0x{:X} and 0x{:X}".format(pos_from, verify_len))
4387
+ self.SetProgress({"action":"UPDATE_POS", "pos":pos_from + verify_len})
4388
+ elif verified is not True and len(verified) == 2:
4389
+ crc32_errors += 1
4390
+ dprint("Mismatch during CRC32 verification at 0x{:X}".format(pos_from), "Errors:", crc32_errors)
4391
+ verified = False
4392
+ break
4393
+
4394
+ if verified is False: break
4395
+
4396
+ else:
4397
+ self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
4398
+
4399
+ bank += 1
4400
+
4401
+ if not verified:
4402
+ verify_args = copy.copy(args)
4403
+ verify_args.update({"verify_write":data_import[sector[0]:sector[0]+sector[1]], "rom_size":len(data_import), "verify_from":sector[0], "path":"", "rtc_area":flashcart.HasRTC(), "verify_mbc":_mbc})
4404
+ verify_args["verify_base_pos"] = sector[0]
4405
+ verify_args["verify_len"] = len(verify_args["verify_write"])
4406
+ verify_args["rom_size"] = len(verify_args["verify_write"])
4407
+
4408
+ self.NO_PROG_UPDATE = True
4409
+ self.ReadROM(0, 4) # dummy read
4410
+ self.NO_PROG_UPDATE = False
4411
+ start_address = 0
4412
+ end_address = buffer_pos
4413
+
4414
+ verified_size = self._BackupROM(verify_args)
4415
+ if isinstance(verified_size, int): dprint("args[\"verify_len\"]=0x{:X}, verified_size=0x{:X}".format(verify_args["verify_len"], verified_size))
4416
+ if self.CANCEL or self.ERROR:
4417
+ cancel_args = {"action":"ABORT", "abortable":False}
4418
+ cancel_args.update(self.CANCEL_ARGS)
4419
+ self.CANCEL_ARGS = {}
4420
+ self.ERROR_ARGS = {}
4421
+ self.SetProgress(cancel_args)
4422
+ if self.CanPowerCycleCart(): self.CartPowerCycle()
4423
+ verified = False
4424
+ return
4425
+ elif (verified_size is not True) and (verify_args["verify_len"] != verified_size):
4426
+ if verified_size is None:
4427
+ print("{:s}Verification failed! Sector: {:s}{:s}".format(ANSI.RED, str(sector), ANSI.RESET))
4428
+ else:
4429
+ print("{:s}Verification failed at 0x{:X}! Sector: {:s}{:s}".format(ANSI.RED, sector[0]+verified_size, str(sector), ANSI.RESET))
4430
+ if sector not in broken_sectors:
4431
+ broken_sectors.append(sector)
4432
+ continue
4433
+ else:
4434
+ dprint("Verification between 0x{:X} and 0x{:X} successful by normal reading.".format(sector[0], sector[0]+sector[1]))
4435
+ verified = True
4436
+
4437
+ self.SetProgress({"action":"UPDATE_POS", "pos":len(data_import), "force_update":True})
4438
+ if len(broken_sectors) > 0:
4439
+ self.INFO["broken_sectors"] = broken_sectors
4440
+ self.INFO["verify_error_params"] = {}
4441
+ self.INFO["verify_error_params"]["rom_size"] = len(data_import)
4442
+ if self.MODE == "DMG":
4443
+ self.INFO["verify_error_params"]["mapper_name"] = _mbc.GetName()
4444
+ if flashcart.GetMBC() == "manual":
4445
+ self.INFO["verify_error_params"]["mapper_selection_type"] = 1 # manual
4446
+ else:
4447
+ self.INFO["verify_error_params"]["mapper_selection_type"] = 2 # forced by cart type
4448
+ self.INFO["verify_error_params"]["mapper_max_size"] = _mbc.GetMaxROMSize()
4449
+ verified = False
4450
+ # ↑↑↑ Flash verify
4451
+
4452
+ if delta_state_new is not None and not chip_erase and not "broken_sectors" in self.INFO:
4453
+ try:
4454
+ with open(json_file, "wb") as f:
4455
+ f.write(json.dumps(delta_state_new).encode("UTF-8-SIG"))
4456
+ except PermissionError:
4457
+ print("Error: Couldn’t update write-protected file “{:s}”".format(json_file))
4458
+
4459
+ # ↓↓↓ Switch to first ROM bank
4460
+ if self.MODE == "DMG":
4461
+ if _mbc.ResetBeforeBankChange(0) is True:
4462
+ dprint("Resetting the MBC")
4463
+ self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
4464
+ _mbc.SelectBankROM(0)
4465
+ self._set_fw_variable("DMG_ROM_BANK", 0)
4466
+ elif self.MODE == "AGB":
4467
+ if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
4468
+ flashcart.SelectBankROM(0)
4469
+ # ↑↑↑ Switch to first ROM bank
4470
+
4471
+ self.SetMode(self.MODE)
4472
+
4473
+ self.INFO["last_action"] = self.INFO["action"]
4474
+ self.INFO["action"] = None
4475
+ self.SetProgress({"action":"FINISHED", "verified":verified})
4476
+ return True
4477
+
4478
+ #################################################################
4479
+
4480
+ def TransferData(self, args, signal):
4481
+ self.ERROR = False
4482
+ self.CANCEL = False
4483
+ self.CANCEL_ARGS = {}
4484
+ self.READ_ERRORS = 0
4485
+ self.WRITE_ERRORS = 0
4486
+ if self.IsConnected():
4487
+ _apoe = False
4488
+ if self.CanPowerCycleCart():
4489
+ self.CartPowerOn()
4490
+ if self.FW["fw_ver"] >= 12:
4491
+ _apoe = self._get_fw_variable("AUTO_POWEROFF_ENABLED") == 1
4492
+ if _apoe is True:
4493
+ _apot = self._get_fw_variable("AUTO_POWEROFF_TIME")
4494
+ self._set_fw_variable("AUTO_POWEROFF_TIME", 5000)
4495
+
4496
+ ret = False
4497
+ self.SIGNAL = signal
4498
+ try:
4499
+ temp = copy.copy(args)
4500
+ if "buffer" in temp: temp["buffer"] = "(data)"
4501
+ dprint("args:", temp)
4502
+ del(temp)
4503
+ self.NO_PROG_UPDATE = False
4504
+ if args['mode'] == 1: ret = self._BackupROM(args)
4505
+ elif args['mode'] == 2: ret = self._BackupRestoreRAM(args)
4506
+ elif args['mode'] == 3: ret = self._BackupRestoreRAM(args)
4507
+ elif args['mode'] == 4: ret = self._FlashROM(args)
4508
+ elif args['mode'] == 0xFF: self.Debug()
4509
+ if self.FW is None: return False
4510
+ if self.FW["fw_ver"] >= 2 and self.FW["pcb_name"] == "GBxCart RW":
4511
+ if ret is True:
4512
+ self._write(self.DEVICE_CMD["OFW_DONE_LED_ON"])
4513
+ elif self.ERROR is True:
4514
+ self._write(self.DEVICE_CMD["OFW_ERROR_LED_ON"])
4515
+
4516
+ except serial.serialutil.SerialTimeoutException as _:
4517
+ print("Connection timed out. Please reconnect the device.")
4518
+ return False
4519
+ except serial.serialutil.PortNotOpenError as _:
4520
+ print("Connection closed.")
4521
+ return False
4522
+
4523
+ if self.CanPowerCycleCart() and _apoe is True:
4524
+ self._set_fw_variable("AUTO_POWEROFF_TIME", _apot)
4525
+
4526
+ return True