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