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

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