FlashGBX 3.37__py3-none-any.whl → 4.0__py3-none-any.whl

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